C for Graphic:语言(vertex操作)

转载自: https://blog.****.net/yinhun2012/article/details/82752910


之前我们非常详细的学习了unity CG shader每一句代码的含义,以及其上下文渲染流程环境,同时也随便讲了几个简单的内置函数比如tex2D等意义,相信小伙伴们基本上对unity CG shader有明确的理解了,用通俗的话来说就是心里有了数。

         这次我们就来一点思维发散,看看shader中vertex顶点和frag片段函数到底能做些什么事情。

         先来说说vertex顶点函数,从字面意思上就是提供给我们开发者操作vertex顶点的函数,之前我一般只在顶点函数中对CG运行时提供的顶点源数据进行MVP矩阵变换到裁剪空间(mul(UNITY_MATRIX_MVP,pos)或者UnityObjectToClipPos(pos)都可以),这个是vertex顶点函数必须做的一个操作,在渲染流水线整个过程中,必须经过这些矩阵变换才能将顶点源数据坐标变换到我们人眼观察的屏幕上。

         那么是不是意味着vertex顶点函数只做一个MVP矩阵变换就行了呢?nonono,假如我就是想用各种运算函数对vertex顶点进行稀奇古怪的变换,比如我接下来要做的一个水波顶点运动变换,CG shader如下:

         

  1. Shader "Unlit/VertexAnimationUnlitShader"
  2. {
  3. Properties
  4. {
  5. _MainTex ("Texture", 2D) = "white" {}
  6. _Speed("Speed",Range(0.1,100)) = 0.5
  7. _Range("Range",Range(0.1,10)) = 0.1
  8. }
  9. SubShader
  10. {
  11. Tags { "RenderType"="Opaque" }
  12. LOD 100
  13. Cull off
  14. Pass
  15. {
  16. CGPROGRAM
  17. #pragma vertex vert
  18. #pragma fragment frag
  19. #include "UnityCG.cginc"
  20. struct appdata
  21. {
  22. float4 vertex : POSITION;
  23. float2 uv : TEXCOORD0;
  24. };
  25. struct v2f
  26. {
  27. float2 uv : TEXCOORD0;
  28. float4 vertex : SV_POSITION;
  29. };
  30. sampler2D _MainTex;
  31. float4 _MainTex_ST;
  32. float _Speed; //水波荡漾速度
  33. float _Range; //水波荡漾幅度
  34. v2f vert (appdata v)
  35. {
  36. v2f o;
  37. //先将"逐"顶点变换到裁剪空间,当然这一步在进行了“波动”变换后再处理效果也一样
  38. o.vertex = UnityObjectToClipPos(v.vertex);
  39. //这里就是逐顶点角度差异化的一种方法
  40. float angle = _Speed*_Time*(o.vertex.x + o.vertex.y + o.vertex.z);
  41. //利用sin或者cos函数进行顶点“波动”处理
  42. o.vertex += _Range*sin(angle);
  43. o.uv = TRANSFORM_TEX(v.uv, _MainTex);
  44. return o;
  45. }
  46. fixed4 frag (v2f i) : SV_Target
  47. {
  48. fixed4 col = tex2D(_MainTex, i.uv);
  49. return col;
  50. }
  51. ENDCG
  52. }
  53. }
  54. }

          shader代码中我也注释了,所以这里我简短的讲解一下(之前很详细的讲解了UnityCG的含义,这里就只写关键点)

          ①.水波荡漾这种“波动”表现,和三角函数中sin或cos函数图像很像,那么意味着我们可以使用sin或cos函数处理顶点的坐标变换,为了“波动”的控制性,我新增两个变量,一个是“波动”速度_Speed,一个是“波动”幅度_Range,实际图像绘画图如下:

           C for Graphic:语言(vertex操作)

          新的函数为y = _Range*sin(_Speed*θ),_Range就增幅了波动的幅度,_Speed则增幅了函数图像的速度。

          ②.float angle = _Speed*_Time*(o.vertex.x + o.vertex.y + o.vertex.z);这句代码是不是让大家有点迷惑,o.vertex.x + o.vertex.y + o.vertex.z是个什么鬼意思?这里说一下,vertex顶点函数按照我的说法那就是“逐”顶点函数,也就是说在一帧内将模型网格的所有顶点源数据依次传入,那么假如我们随便用一个float angle = _Speed*_Time;那么angle的角度对于该帧内所有顶点坐标就是固定不变的,那么接下来sin(angle)也是固定不变的,那么o.vertex += _Range*sin(angle);的变化相对于所有顶点坐标数据就是一致的,那么表现上,这个模型网格顶点就是做的“同样的位移运动”,如果以模型自己为参考系,那么模型就是“静止不动的”。

          而*(o.vertex.x + o.vertex.y + o.vertex.z),因为o.vertex的xyz三个分量都是不尽相同的,所以*(o.vertex.x + o.vertex.y + o.vertex.z)就造成了float angle = _Speed*_Time*(o.vertex.x + o.vertex.y + o.vertex.z);随顶点坐标的变化而变化,所以最后的运动计算结果,以模型自己为参考系,那么各个顶点都是“波动”的(当然我也可以写成*(o.vertex.x + o.vertex.yz)去掉z分量,也能达到“波动变化”的效果)。

              PS:_Time这个变量是什么?请打开unity内置着色器的UnityShaderVariables.cginc文件,如下:

              C for Graphic:语言(vertex操作)

              就是unity提供的时间变化量。

              接下来就让看下实际效果,如下图:

             C for Graphic:语言(vertex操作)

             这里拖拽修改_Speed或者_Range都会出现相应效果,特别注意因为我用的unity自带的plane,网格顶点之间的间距只有0.1个单位,所以Range超过0.1太多就会出现“撕裂”效果,小伙伴们可以自己验证一下!

              这篇暂时写到这里,实际上有vertex顶点函数,我们可以发挥自己的极限想象力,做出各种各样炫动的效果,这里只是抛砖引玉。下一篇我们就看下frag片段函数能干些什么稀奇古怪的事情。

             so,接下来我们继续。