C for Graphic:语言(vertex操作)
这次我们就来一点思维发散,看看shader中vertex顶点和frag片段函数到底能做些什么事情。
先来说说vertex顶点函数,从字面意思上就是提供给我们开发者操作vertex顶点的函数,之前我一般只在顶点函数中对CG运行时提供的顶点源数据进行MVP矩阵变换到裁剪空间(mul(UNITY_MATRIX_MVP,pos)或者UnityObjectToClipPos(pos)都可以),这个是vertex顶点函数必须做的一个操作,在渲染流水线整个过程中,必须经过这些矩阵变换才能将顶点源数据坐标变换到我们人眼观察的屏幕上。
那么是不是意味着vertex顶点函数只做一个MVP矩阵变换就行了呢?nonono,假如我就是想用各种运算函数对vertex顶点进行稀奇古怪的变换,比如我接下来要做的一个水波顶点运动变换,CG shader如下:
-
Shader "Unlit/VertexAnimationUnlitShader"
-
{
-
Properties
-
{
-
_MainTex ("Texture", 2D) = "white" {}
-
_Speed("Speed",Range(0.1,100)) = 0.5
-
_Range("Range",Range(0.1,10)) = 0.1
-
}
-
SubShader
-
{
-
Tags { "RenderType"="Opaque" }
-
LOD 100
-
Cull off
-
-
Pass
-
{
-
CGPROGRAM
-
#pragma vertex vert
-
#pragma fragment frag
-
-
#include "UnityCG.cginc"
-
-
struct appdata
-
{
-
float4 vertex : POSITION;
-
float2 uv : TEXCOORD0;
-
};
-
-
struct v2f
-
{
-
float2 uv : TEXCOORD0;
-
float4 vertex : SV_POSITION;
-
};
-
-
sampler2D _MainTex;
-
float4 _MainTex_ST;
-
float _Speed; //水波荡漾速度
-
float _Range; //水波荡漾幅度
-
-
v2f vert (appdata v)
-
{
-
v2f o;
-
//先将"逐"顶点变换到裁剪空间,当然这一步在进行了“波动”变换后再处理效果也一样
-
o.vertex = UnityObjectToClipPos(v.vertex);
-
//这里就是逐顶点角度差异化的一种方法
-
float angle = _Speed*_Time*(o.vertex.x + o.vertex.y + o.vertex.z);
-
//利用sin或者cos函数进行顶点“波动”处理
-
o.vertex += _Range*sin(angle);
-
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
-
return o;
-
}
-
-
fixed4 frag (v2f i) : SV_Target
-
{
-
fixed4 col = tex2D(_MainTex, i.uv);
-
return col;
-
}
-
ENDCG
-
}
-
}
-
}
shader代码中我也注释了,所以这里我简短的讲解一下(之前很详细的讲解了UnityCG的含义,这里就只写关键点)
①.水波荡漾这种“波动”表现,和三角函数中sin或cos函数图像很像,那么意味着我们可以使用sin或cos函数处理顶点的坐标变换,为了“波动”的控制性,我新增两个变量,一个是“波动”速度_Speed,一个是“波动”幅度_Range,实际图像绘画图如下:
新的函数为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文件,如下:
就是unity提供的时间变化量。
接下来就让看下实际效果,如下图:
这里拖拽修改_Speed或者_Range都会出现相应效果,特别注意因为我用的unity自带的plane,网格顶点之间的间距只有0.1个单位,所以Range超过0.1太多就会出现“撕裂”效果,小伙伴们可以自己验证一下!
这篇暂时写到这里,实际上有vertex顶点函数,我们可以发挥自己的极限想象力,做出各种各样炫动的效果,这里只是抛砖引玉。下一篇我们就看下frag片段函数能干些什么稀奇古怪的事情。
so,接下来我们继续。