struct appdata_full { float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; float4 texcoord1 : TEXCOORD1; float4 texcoord2 : TEXCOORD2; float4 texcoord3 : TEXCOORD3;#if defined(SHADER_API_XBOX360) half4 texcoord4 : TEXCOORD4; half4 texcoord5 : TEXCOORD5;#endif fixed4 color : COLOR; }; 我们注意到这些结构的字段和表面着色器中的字段不同,后面多了一个冒号和一个标签。这是该字段的语义,用于告诉GPU这个字段的数据应该去哪里读写。GPU毕竟是为了图形计算而特别设计的东西,很多东西都是固定的,我们只要记得有这么几个名字可以用行了。 类型 名字 标签 备注
float4 vertex POSITION 顶点在模型坐标系下的位置
float3 normal NORMAL 顶点的法向量
float4 tangent TANGENT 顶点的切向量
float4 color COLOR 顶点色
float4 texcoord TEXCOORD0 顶点的第一个uv坐标
float4 texcoord1 TEXCOORD1 顶点的第二个uv坐标,最多可以到5
Output
顶点着色器的输出是也是一个可以自己定义的结构,但是结构内容也是比较固定的,一般包含了顶点投影后的位置,uv,顶点色等,也可以加一些后面片段着色器需要用到但是需要在顶点着色器中计算的值。这个输出就是后面片段着色器的输入。
struct v2f { float4 pos : SV_POSITION; half2 uv : TEXCOORD0; }; 可以使用的字段有: 类型 标签 描述
float4 SV_POSITION 顶点在投影空间下的位置,注意和输入的模型坐标系下的位置不同,这个字段必必须
设置,这个坐标转换是顶点着色器的重要工作
float3 NORMAL 顶点在视图坐标系下的法向量
float4 TEXCOORD0 第一张贴图的uv坐标
float4 TEXCOORD1 第二张贴图的uv坐标
float4 TANGENT 切向量,主要用来修正法线贴图Normal Maps
fixed4 COLOR 第一个定点色
fixed4 COLOR1 第二个定点色
Any Any 其他自定义的字段
坐标变换
顶点着色器有一项重要的工作就是进行坐标变换。顶点着色器的输入中的坐标是模型坐标系(ObjectSpace)下的坐标,而最终绘制到屏幕上的是投影坐标。
在我们Shader里面只需要一句话就可以完成坐标的转换,这也是最简单的顶点着色器:
v2f vert(appdata v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); return o; } 用UNITY_MATRIX_MVP矩阵乘以顶点在模型坐标系下的坐标就得到投影坐标。 UNITY_MATRIX_MVP是Unity内建的模型->视->投影矩阵, Unity内建矩阵如下: ?
UNITY_MATRIX_MVP:当前模型->视图->投影矩阵。(注:模型矩阵为 本地->
世界)
? ? ? ? ? ?
UNITY_MATRIX_MV:当前模型->视图矩阵 UNITY_MATRIX_V:当前视图矩阵 UNITY_MATRIX_P:当前投影矩阵 UNITY_MATRIX_VP:当前视图->投影矩阵 UNITY_MATRIX_T_MV:转置模型->视图矩阵
UNITY_MATRIX_IT_MV:逆转置模型->视矩阵, 用于将法线从ObjectSpace旋转
到WorldSpace。为什么法线变化不能和位置变换一样用UNITY_MATRIX_MV呢?一是因为法线是3维的向量而- UNITY_MATRIX_MV是一个4x4矩阵,二是因为法线是向量,我们只希望对它旋转,但是在进行空间变换的时候,如果发生非等比缩放,方向会发生偏移。
? UNITY_MATRIX_TEXTURE0 to UNITY_MATRIX_TEXTURE3:纹理变换矩阵
下面简单介绍一下里面提到的几个坐标系:
模型坐标系:也叫物体坐标系,3D建模的时候每个模型都是在自己的坐标系下建立的,如
果一个人物模型脚底是(0,0,0) 点的话它的身上其它点的坐标都是相对脚底这个原点的。 世界坐标系:我们场景是一个世界,有自己的原点,模型放置到场景中后模型上的每个顶点就有了一个新的世界坐标。这个坐标可以通过模型矩阵×模型上顶点的模型坐标得到。 视图坐标系:又叫观察坐标系,是以观察者(相机)为原点的坐标系。场景中的物体只有被相机观察到才会绘制到屏幕上,相机可以设置视口大小和裁剪平面来控制可视范围,这些都是相对相机来说的,所以需要把世界坐标转换到视图坐标系来方便处理。
投影坐标系:场景是3D的,但是最终绘制到屏幕上是2D,投影坐标系完成这个降维的工作,投影变换后3D的坐标就变成2D的坐标了。投影有平行投影和透视投影两种,可以在Unity的相机上设置。
屏幕坐标系 : 最终绘制到屏幕上的坐标。屏幕的左下角为原点。
除了内建矩阵,Unity还内建了一些辅助函数也可以在顶点着色器里面使用:
?
float3 WorldSpaceViewDir (float4 v):根据给定的局部空间顶点位置到相机返回
世界空间的方向(非规范化的)
? float3 ObjSpaceViewDir (float4 v):根据给定的局部空间顶点位置到相机返回局
部空间的方向(非规范化的)
? float2 ParallaxOffset (half h, half height, half3 viewDir):为视差法线贴图计算
UV偏移
? ?
fixed Luminance (fixed3 c):将颜色转换为亮度(灰度)
fixed3 DecodeLightmap (fixed4 color):从Unity光照贴图解码颜色(基于平台
为RGBM 或dLDR)
? float4 EncodeFloatRGBA (float v):为储存低精度的渲染目标,编码[0..1)范围的
浮点数到RGBA颜色。
? ?
float DecodeFloatRGBA (float4 enc):解码RGBA颜色到float。
float2 EncodeViewNormalStereo (float3 n):编码视图空间法线到在0到1范围
的两个数。
? float3 DecodeViewNormalStereo (float4 enc4):从enc4.xy解码视图空间法线
Fragment Shader
// TODO