- unity shader 中实现漫反射光照模型

在入门精要第六章,6.4中,如何实现逐定点光照模型。

代码和注释如下

//标识当前shader文件的路径和在shader选择列表中的名称
Shader "UnityShaderPratice/Chapter6/VertexDiffuse"
//定义一个数形变量,变量名称是_Diffuse, 在编辑器中显示的变量名称为"Diffuse", 变量类型"Color", 变量的默认值是(1,1,1,1)
Properties {
     _Diffuse("Diffuse", Color) = (1,1,1,1)
}

//Unity的着色器中都包含了一个或者多个子着色器,unity会根据当前设备显卡的配置和参数选择一个适合的subshader进行着色绘制
SubShader{
    //顶点着色器或者片元着色器都需要在pass模块中书写
     Pass{
          //标签标示当前这个pass在光照流水线中的角色
          Tags{"LightModel" = "ForwardBase"}
          
          //用来包围CG代码
          CGPROGRAM
          
          //利用pragma指令来告诉unity我们分别定义了一个顶点着色器和片元着色器
          #pragma vertex vert
          #pragma fragment frag
          
          //为了使用unity内置的一些变量,
          #include "Lighting.cginc"
          
          //为了在shader中使用属性模块中的_Diffuse,我们需要定义一个和该属性类型相匹配的变量
          //材质的漫反射属性的取值范围一般在0 - 1,所以我们可以用fixed精度的变量来存储它。
          fixed4 _Diffuse;
              
          //声明定义点着色器数据结构
          struct a2v{
              float4 vertex : POSITION;
              float3 normal : NORMAL; 
          };
          
          //声明定义片元着色器数据结构
          struct v2f{
               float4 pos : SV_POSITION;
               fixed3 color : COLOR;
          };
          
          //定义顶点着色器
          v2f vert (a2v v) {
               //声明定义一个v2f片元着色器临时变量
               v2f o;
               fixed3 ambient =  UNITY_LIGHTMODEL_AMBIENT.xyz;
               //获取定点的投影坐标
               o.pos = UnityObjectToCliPos(v.vertex);
               //获取定点法线的世界向量
               fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
               //获取定点法线的光照向量
               fixed worldLight = normalize(_WorldSpaceLightPos0.xyz);    
               fixed3 color = _LightColor0 * _Diffuse * saturate(dot(worldNormal, worldLight));
               o.color = color + ambient ;
               return o;
               
          }
          
          fixed4 frag(v2f v) : SV_Target{
               return fixed4(v.color, 1.0);
          }
          
          ENDCG
     }

}


我遇到的问题。
法线向量B = 转换矩阵A-B * 法线向量A,即要将法线向量从模型空间转换到世界空间,但为了解决顶点坐标在转换过程中的非等比缩放问题,需要对法线向量的转换矩阵进行推导计算。推导的结果是将原有顶点转换矩阵进行逆转置操作。逆操作是为了还原比例缩放问题,但同样也还原了旋转,而旋转矩阵都是正交矩阵,那么可以在对还原后的矩阵进行转置操作,这样就可以在完成旋转的前提下还原费等比缩放问题。

(法线向量B是在世界空间下,法线向量A是在模型空间)
那么在实际转换的时候,按理说应该写成

mul ((float3x3)unity_ObjectToWorld, v.normal); 但笔者写成了mul(v.normal, (float3x3)unity_WorldToObject);我思考了好久,其实答案就在书中
4.9.2CG中的矢量和矩阵类型明确解释了

问题的本质在于:
在对顶点矩阵从模型空间转换到世界空间的时候,如果他的缩放比例没有做到等比缩放或者扩大,那么顶点坐标也会做不等比例的拉伸或者缩小,这样会导致当用顶点转换矩阵用在法线向量上后,推导后的法线向量不会再垂直顶点坐标。而非等比缩放矩阵本身不是正交矩阵,旋转矩阵本身是旋转矩阵。那么可以先对推导矩阵进行求逆,这样做将会还原顶点推导矩阵的缩放操作和旋转操作,但旋转矩阵本身是正交矩阵,矩阵的转置 == 矩阵的逆矩阵。所以可以对原有顶点推导矩阵进行求逆后在进行转置,这样一方面做到了还原缩放比例操作,另外对法线向量进行旋转操作。



等式1 : mul(v, m) == mul(transposed(m), v);
等式2 :mul(m, v) == mul(v, transposed(m));


那么根据等式1可以得到
mul ((float3x3)unity_ObjectToWorld, v.normal); ==  mul(v.normal, (float3x3)unity_WorldToObject);



等式1 : mul(v, m) == mul(transposed(m), v);
等式2 :mul(m, v) == mul(v, transposed(m));

你可能感兴趣的:(游戏研发)