前几天去一家公司面试,因为简历上写着“能写简单的Shader”,就被问了一个问题:怎么自定义一个光照模型?
直接懵逼了,隐隐约约能联想到的什么漫反射光+镜面反射光+环境光...难道是这个?光照模型就是#pragam surface surf Lambert里面的那个Lambert呗?这不都是写好的吗?还能自定义啊?!本着“程序是严谨的”的心态,宁可说不知道,也不能瞎说。。。
今天有空,抓紧学习一下自定义光照模型...
首先,新建一个SurfaceShader,打开后做如下修改
#pragma surface surf Standard fullforwardshadows >> #pragma surface surfMyLightingModel
之后,在SubShader块儿中实现这个光照模型:
inline float4 LightingMyLightingModel(SurfaceOutput s, fixed3 lightDir, fixed atten)
{
float difLight = max(0, dot (s.Normal, lightDir));
float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
col.a = s.Alpha;
return col;
}
其中lightDir是点到光源的单位向量,atten(attenuation)是衰减系数。如果要用到点到摄像机(观察者)的单位向量,就需要另一个参数:half3 viewDir(因为这个模型是个漫反射模型,就先不考虑观察者角度了)。
dot方法是点乘(点积),在这里求的是入射光线和该点法线的相似度,相似度越高,反射的光线就越多。
关于这个_LightColor0变量,查了一下,比较复杂,是根据当前环境中的各种光源计算出来的...我们只管用就好了。
以上就实现了一个简单的反射的光照模型。
最后来一个漫反射+镜面反射的光照模型,是不是传说中的冯氏反射模型呢?暂且就当做是吧...
float4 LightingPhongModel(SurfaceOutput s, float3 lightDir,half3 viewDir, half atten)
{
float4 col;
float diffuseF = max(0,dot(s.Normal,lightDir));
float specF;
float3 H = normalize(lightDir+viewDir);
float specBase = max(0,dot(s.Normal,H));
specF = pow(specBase,8);
col.rgb = s.Albedo * _LightColor0 * diffuseF *atten + _LightColor0*specF;
col.a = s.Alpha;
return col;
}
float3 H = normalize(lightDir+viewDir);
float specBase = max(0,dot(s.Normal,H));
specF = pow(specBase,8);
这三句是用来计算镜面反射的,镜面反射必然与观察者的位置有关,所以这个方法里用到了这个viewDir参数。
最后来点理论知识,关于漫反射和镜面反射的计算方式。
部分内容参考和引用自风宇冲http://blog.sina.com.cn/s/blog_471132920101dhnv.html
所谓的光照模型(光照方程),就是模型对光线做出的反应。因为材质和表面光滑度的不同,在光线照射到物体表面后,因为对光线的吸收和折射反射等等,最终进入眼睛的光线。
比较经典的一个例子,游戏《半条命》中使用的HalfLambert,修改一下我们的第一个光照模型:
float difLight = max(0, dot (s.Normal, lightDir));
float hLambert = difLight * 0.5 + 0.5;
float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);
col.a = s.Alpha;
return col;