4.Lighting Models
-
自定义光照模型(custom lighting model)
- 改变#pragma指令为:#pragma surface surf LightingModelName
- 创建一个名为Lighting + LightingModelName的函数
- 例:指令为【#pragma surface surf SimpleLambert】,创建对应函数【LightingSimpleLambert()】
-
当两向量点积为0时(求点积函数dot()),两向量成90°角(正交) -
兰伯特反射
- 仅用点积作为光强的系数NdotL(N:Normal 表面法线 L:lightDir 光的方向)
- Unity已实现对应函数LightingLambert,可以直接使用#pragma surface surf Lambert(同时也有Phong、BlinnPhong)
- color.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
-
ToonShader
-
仅用曲面函数得到卡通效果是代价极其昂贵且耗时的(extremely expensive and time consuming) -
卡通阴影需要使用光照模型实现,被称为赛璐珞/赛璐璐(celluloid, 简写CEL) -
使用坡道贴图决定收到的阴影,使用NdotL在坡道贴图上采样得到光强的系数 -
或者不使用坡道贴图,用NdotL的值在0到1之间等距采样 (snap),如: half cel = floor(NdotL * _CelShadingLevels / (_CelShadingLevels - 0.5); -
Unity还提供了观察方向viewDir,在自定义光照模型中是可选的,可以写
- half4 LightingCustomName (SurfaceOutput s, half3 lightDir, half atten)
- half4 LightingCustomName (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
-
冯氏反射(Phong, 镜面反射模型,带viewDir)
-
最终光强I = 漫反射颜色D + 镜面反射S -
D = NdotL(与兰伯特反射相同) -
S = (Reflection dot viewDir)p float spec = pow(max(0, dot(reflectionVector, viewDir)), _SpecPower); -
Reflection = 2 * N * (N * NdotL) - LightDir :向量代数中可以依此公式计算出来 float3 reflectionVector = normalize(2.0 * s.Normal * NdotL - lightDir); -
c.rgb = (s.Albedo * _LightColor0.rgb * max(0,NdotL) * atten) + ( _SpecularColor.rgb *_LightColor0.rgb * spec); -
布林冯模型(BlinnPhong模型)
- 使用半程向量(viewDir和LightDir)替代了反射向量R的计算,半程向量halfVector = |V + L|
- float3 halfVector = normalize(lightDir + viewDir);
- spec = pow(max(0, dot(s.Normal, halfVector)), _SpecPower) * _SpecularColor;
- color.rgb = (s.Albedo * _LightColor0.rgb * NdotL) + ( _SpecularColor.rgb *_LightColor0.rgb * spec) * atten;
-
各向异性镜面模型Anisotropic
-
创建数据连接,需要把anisoDir信息(存在tex中)传递给Lighting函数,在surf函数中可以计算出这个值(tex2D()函数),所以用到自定义的输出结构 strutc SurfaceAnisoOutput { … } 设置surf(Input IN, inout SurfaceAnisoOutput o) 设置fixed4 LightingAnisotropic(SurfaceAnisoOutput s, fixed3 lightDir, half3 viewDir, fixed atten) -
计算半程向量halfVector和NdotL -
计算HdotA,A是各向异性方向和法线的半程向量(可以反应反射方向和各向异性方向的夹角) fixed HdotA = dot(normalize(s.Normal + s.AnisoDirection), halfVector); -
计算各向异性系数,用sin函数帮助修正上面得到的值,这个公式可以得到一个较暗的中间高光 float aniso = max(0, sin(radians((HdotA + _AnisoOffset) * 180))); -
用pow函数放大各项异性的影响,并乘以反射系数来降低总体强度 float spec = saturate(pow(aniso, s.Gloss * 128) * s.Specular); -
c.rgb = ((s.Albedo * _LightColor0.rgb * NdotL) + ( _SpecularColor.rgb * _LightColor0.rgb * spec)) * atten; -
注:注意到从书中摘抄的公式中,atten系数有些只乘在漫反射上,有些只乘在镜面反射上,有些两个都乘了,不清楚是作者故意为之还是其中有什么讲究,通过修改代码实验,肉眼也没有看出来区别;按照个人理解,光照衰减对两种反射都应该有影响,才疏学浅且眼光有限,故该问题留个人实力提升后解答
|