Unity Shader 学习笔记(6) 漫反射
参考书籍:《Unity Shader 入门精要》
3D数学 学习笔记(8) 光照
逐顶点、逐像素、半兰伯特光照模型对比:
逐顶点光照(Lambert法则)
Shader "Custom/Chapter 6/DiffuseVertexLevel" {
Properties {
_Diffuse ("Diffuse",Color) = (1,1,1,1)
}
SubShader {
Pass {
Tags {"LightMode" = "ForwardBase"} // 光照流水线,定义了才能得到一些内置光照变量
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc" // 后面用到 _LightColor0
fixed4 _Diffuse; // 声明属性变量
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR0;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 获得环境光,Unity内置变量
// 等价 UnityObjectToWorldNormal(v.normal)
fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); // 将法线从模型空间转到世界空间
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); // 光源方向(假设场景只有一个光源且为平行光)
// _LightColor0:光源颜色和强度。saturate:把参数限制在[0,1],CG提供的函数
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));
o.color = ambient + diffuse;
return o;
}
fixed4 frag(v2f i) : SV_Target {
return fixed4(i.color,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐像素光照
原理同上,只不过是在片元着色器中计算。计算的法线是像素的法线。
struct v2f {
float4 pos : SV_POSITION;
fixed3 worldNormal : TEXCOORD0; // 获取纹理坐标
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
// 不需要计算光照模型,直接把世界空间法线传给着色器
o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 获取环境光
fixed3 worldNormal = normalize(i.worldNormal); // 获取世界坐标的法线
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); // 光源方向
// _LightColor0:光源颜色和强度,saturate:把参数限制在[0,1]
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));
fixed3 color = ambient + diffuse;
return fixed4(color,1.0);
}
半兰伯特光照
因为上面两种光照在背面都是无法接收光照而显示全黑,所有就有了这个模型。定义如下:
一般α和β都取0.5,也就是把原来( n · I )的范围从[-1, 1]映射到[0, 1],即法线平行光照的片元才会是全黑的。
从逐片元光照代码中直接修改公式即可。
// 将原本n·l的[-1,1]映射到[0,1],两个常量可以变,通常都是两个0.5
fixed halfLambert = dot(worldNormal,worldLight) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;