在入门精要第六章,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));