Unity Shader学习笔记(13)混合光源、光的衰减
发表于2017-12-29
对于混合光源的渲染以及光的衰减可能有些人还没接触过,碰到这类需求可能都有人不知道怎么开始,那本篇文章对混合光源和光的衰减的介绍肯定可以给你答案。
混合光源的前向渲染
一个BasePass处理平行光、四个点光源调用四次Additional Pass,四个点光源的顺序是依靠重要度排序的(光源强度、颜色、距离远近)。注意:只处理了逐像素光照,即光源渲染模式如果为Not Imoportant,则不会对物体产生影响。
使用Phong光照模型。定义了Base Pass 和 Additional Pass来处理多个光源。
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代码如下。处理其他光源。
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
变换矩阵来计算点在光源空间的位置。
// 计算衰减纹理坐标值 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;
数学公式计算衰减
使用的是线性衰减:
flaot distance = length(_WorldSpaceLightPos0.xyz) - i.worldPosition.xyz); atten = 1.0 / distance;
Unity Shader学习笔记系列教程: