Cg顶点程序必须在结构中传递顶点数据。几种经常使用的顶点结构定义在文件UnityCG.cginc中,有以下三种结构体:android
一、appdata_base: 包含顶点位置,法线和一个纹理坐标。
二、appdata_tan:包含顶点位置,切线,法线和一个纹理坐标。
三、appdata_full:包含位置、法线、切线、顶点色和两个纹理坐标。ios
struct appdata_base { float4 vertex : POSITION; //顶点坐标 float3 normal : NORMAL;//法线 float4 texcoord : TEXCOORD0;//UV }; struct appdata_tan { float4 vertex : POSITION; float4 tangent : TANGENT; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct appdata_full { float4 vertex : POSITION;//顶点坐标 float4 tangent : TANGENT;//正切 float3 normal : NORMAL;//法线 float4 texcoord : TEXCOORD0;//第一层UV float4 texcoord1 : TEXCOORD1; //第二层UV fixed4 color : COLOR; //颜色 };
注:顶点坐标和正切线为何是float4,这有点意思,由于这里它表示是齐次坐标,好比咱们这样表示一个float4(x,y,z,w),当w = 1的时候它表示点(x,y,z),当w= 0的时候它表示一个向量(x,y,z)。区别就在这里,当W为1时表示点,当W为0时表示向量。app
texcoord0和texcoord1分别表示两层UV,有时候咱们模型上的贴图须要多个图片一块儿贴在一处,那么贴两层就会有两层UV。函数
以上三种类型为Unity内置的顶点Shader传入结构体,若是想自定义也是能够的,可是自定义结构体里面的属性,必须是基于appdata_full的,这样才能识别出来,也就是自定义的结构里的属性必须到appdata_full里的属性里去选。性能
下面给一个简单的顶点/片元 shader 事例:优化
Shader "Custom/Example" { Properties { _MainTex ("Texture", 2D) = "white" { } //引号里面的"Texture"则是Unity检视面板中对应显示的属性名称 } SubShader { pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" sampler2D _MainTex; //_MainTex_ST的ST应该是SamplerTexture的意思 ,就是声明_MainTex是一张采样图,也就是会进行UV运算。 //若是没有这句话,是不能进行TRANSFORM_TEX的运算的。_MainTex_ST.xy为 图中的Tiling,zw为图中的offset. float4 _MainTex_ST; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; } ; v2f vert (appdata_base v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); //MVP矩阵变换,将裁剪空间坐标转换为相对屏幕位置的UV坐标 o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); //TRANSFORM_TEX的做用是用顶点的UV v.texcoord和材质球的采样图片_MainTex作运算,确保顶点材质球里的缩放和偏移是正确的。等价于o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; return o; } float4 frag (v2f i) : COLOR { float4 texCol = tex2D(_MainTex,i.uv); float4 outp = texCol; return outp; } ENDCG } } }
其中SV_POSITION,SV_前缀的变量表明system value,在DX10之后的语义绑定中被使用表明特殊的意义,和POSITION用法并没有不一样。惟一区别是 SV_POSTION一旦被做为vertex shader的输出语义,那么这个最终的顶点位置就被固定了(不能tensellate,不能再被后续改变它的空间位置?),直接进入光栅化处理,若是做为fragment shader的输入语义那么和POSITION是同样的,表明着每一个像素点在屏幕上的位置(这个说法其实并不许确,事实是fragment 在 view space空间中的位置,但直观的感觉是如括号以前所述通常)
最后这个回答者说了,在DX10版本以前没有引入SV_的预约义语义,POSITION被用做vertex shader的输入,输出,fragment shader的输入参数。但DX10以后就推荐使用SV_POSITION做为vertex shader的输出和fragment shader的输入了,注意vertex shader的输入仍是使用POSITION!切记。可是DX10之后的代码依旧兼容POSITION做为全程表达,估计编译器会自动判断并替换的吧。spa
另外,给一点编写shader的建议:.net
一、只计算须要计算的东西;
二、一般,须要渲染的像素比顶点数多,而顶点数又比物体数多不少。因此若是能够,尽可能将运算从PS移到VS,或直接经过script来设置某些固定值;
三、在使用Surface Shader时,能够经过一些指令让shader优化不少。
一般状况下,Surface shader的不少默认选项都是开启的,以适应大多数状况,可是不少时候,你能够关闭其中的一些选项,从而让你的shader运行的更快:
(1) approxview 对于使用了view direction的shader,该选项会让view dir的normalize操做per-vertex进行,而不是per-pixel。这个优化一般效果明显。
(2) halfasview 可让Specular shader变得快一些,使用一个介于光照方向和观察方向之间的half vector来代替真正的观察方向viewDir来计算光照函数。
(3) noforwardadd Forward Render时,彻底只支持一盏方向光的per-pixel渲染,其他的光照所有按照per-vertex或SH渲染。这样能够确保shader在一个pass里渲染完成。
(4) noambient 禁掉ambient lighting和SH lighting,可让shader快一点儿。
四、浮点数精度相关:
float:最高精度,一般32位
half:中等精度,一般16位,-60000到60000,
fixed:最低精度,一般11位,-2.0到2.0,1/256的精度。
尽可能使用低精度。对于color和unit length vectors,使用fixed,其余状况,根据取值范围尽可能使用half,实在不够则使用float。
在移动平台,关键是在fragment shader中尽量多的使用低精度数据。另外,对于多数移动GPU,在低精度和高精度之间转换是很是耗的,在fixed上作swizzle操做也是很费事的。
五、Alpha Test
Alpha test和clip()函数,在不一样平台有不一样的性能开销。
一般使用它来cull那些彻底透明的像素。
可是,在ios和一些android上使用的PowerVR GPUs上面,alpha test很是的昂贵。
六、Color Mask
在移动设备上,Color Mask也是很是昂贵的,因此尽可能别使用它,除非真的是须要。code
最后推荐两篇不错的博客,orm