参考书籍:《Unity Shader 入门精要》
3D数学 学习笔记(8) 光照
【常见问题】dot(lightCoord, lightCoord).rr和光源衰减的相关问题git
一个BasePass处理平行光、四个点光源调用四次Additional Pass,四个点光源的顺序是依靠重要度排序的(光源强度、颜色、距离远近)。注意:只处理了逐像素光照,即光源渲染模式若是为Not Imoportant,则不会对物体产生影响。
github
使用Phong光照模型。定义了Base Pass 和 Additional Pass来处理多个光源。web
Base Pass 代码以下。Unity会把最亮的平行光给BasePass处理,其余平行光交给AdditionalPass处理。缓存
Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma multi_compile_fwdbase // 保证使用光照衰减等光照变量能够正确被赋值 ... fixed4 frag(v2f i) : SV_Target { ... // 环境光,只在BasePass处理一次够了。 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 漫反射,_LightColor0是平行光颜色和强度相乘后的结果。 fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir)); ... // 镜面高光。 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss); // 衰减值,平行光为1.0。 fixed atten = 1.0; return fixed4(ambient + (diffuse + specular) * atten, 1.0); } ... }
Additional Pass代码以下。处理其余光源。svg
Pass { Tags { "LightMode"="ForwardAdd" } // 开启光照混合,与在帧缓存中的光照叠加 Blend One One CGPROGRAM // 获取正确光照变量 #pragma multi_compile_fwdadd ... fixed4 frag(v2f i) : SV_Target { fixed3 worldNormal = normalize(i.worldNormal); #ifdef USING_DIRECTIONAL_LIGHT // 若是是平行光,直接获取光源方向 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); #else // 点光或聚光,光源方向要依赖视角方向 fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz); #endif ... // 漫反射和高光反射 // 计算衰减值。平行光为1。其余光照较复杂。 #ifdef USING_DIRECTIONAL_LIGHT fixed atten = 1.0; #else #if defined (POINT) float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz; fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL; #elif defined (SPOT) float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)); fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL; #else fixed atten = 1.0; #endif #endif return fixed4((diffuse + specular) * atten, 1.0); } ENDCG }
UNITY_ATTEN_CHANNEL
:能够获得衰减纹理中衰减值所在份量。学习
须要预处理获得采样纹理,纹理的大小会影响衰减精度。相似渐变纹理,(0, 0)表示与光源重合时的衰减值,(1, 1)为离光源空间最大距离的衰减值。
Unity内部使用_LightTexture0
的纹理来计算光源衰减。利用_LightMatrix0
变换矩阵来计算点在光源空间的位置。.net
// 计算衰减纹理坐标值 float3 lightCoord = mul(_LightMatrix0, float4(i.worldPosition, 1)).xyz; // 根据坐标对纹理采样,用dot点乘就是直接作距离的平方,`.rr`的意思就是取获得rgb值的r做为一个新的二维坐标即(r, r)。 fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
使用的是线性衰减:code
flaot distance = length(_WorldSpaceLightPos0.xyz) - i.worldPosition.xyz); atten = 1.0 / distance;