在游戏蛮牛知识问答里面经常有人询问关于Unity中的Shader编程方面的函数问题,GPU编程其实就是把固定流水线的各种矩阵变换放到了GPU里面进行。
下面给大家主要介绍一些基本的常识:
我们在Shader编程中经常使用 Vertex & Fragment Shaders,通过举例说明:
struct Vert { float4 vertex : POSITION; float3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; Vert Input(Vert v) { Vert o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.color = ShadeVertexLights(v.vertex, v.normal); return o; }
结构体Vert是定义的vertex program作为输入。接下来解析一下Input函数
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
意思是把顶点的位置和Unity提前定义的一个矩阵UNITY_MATRIX_MVP(在UnityShaderVariables.cginc里定义)相乘,从而把顶点位置从model space转换到projection space。我们使用了矩阵乘法操作mul来执行这个步骤。
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
纹理计算其uv坐标,即根据mesh上的uv坐标来计算真正的纹理上对应的位置。我们使用了Unity.CG.cginc中的宏TRANSFORM_TEX来实现。
宏TRANSFORM_TEX的定义:#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)。我们需要在shader中定义一些额外的变量,即必须定义一个名为_YourTextureName_ST (也就是你的纹理的名字加一个 _ST后缀)。
Unity使用了三个数据来定义顶点光源:unity_LightPosition,unity_LightAtten和unity_LightColor。例如[0]表示最重要的光源。
当编写一个multi-pass的光照模型时,只需要一次处理一个单独的光源,这种情况下,Unity同样定义了一个名为_WorldSpaceLightPos0的值,来帮助我们得到它的位置,并且还提供了一个非常有用的函数ObjSpaceLightDir,它可以计算得到该光源的方向。而为了得到该光源的颜色,我们可以在程序中包含“Lighting.cginc”文件,然后使用_LightColor0进行访问。
接下来介绍一下Unity.CG.cginc包含的宏的解释:
宏TANGENT_SPACE_ROTATION(在UnityCG.cginc里定义)是用来来创建一个名为rotation的矩阵,并使用它把object space转换到tangent space中。
TRANSFER_VERTEX_TO_FRAGMENT宏,它在AutoLight.cginc里定义,它会根据该pass处理的光源类型(spot,point,or directional)来计算光源坐标的具体值,以及进行和shadow相关的计算等。
最后:我们在写Shader的时候,可以定义多个passes,其中每一个pass处理一个光源。一个Pass可以为该阶段定义一系列的tags。