在写环境、植物的Shader时,原来使用的是Surface shader,出于性能的考虑要转变成效果一致的vertex&fragment Shader,转变中遇到了一个问题。
首先我加了一个unity的标准点光源,如果都换成unity的standard shader,或者其他的surface shader(包括现在使用的环境和植物的surface shader),你会发现环境都是受点光源影响的,如下图:
现在,我把树干和围栏的shader换成我根据surface shader转换成的vertex&fragment Shader,就会发现不受光了(光照模型的计算完全正确的情况下),注意下图中中间的树干和围栏:
经过查找原因,我做了一个实验,在surface shader中加了一个noforwardAdd指令
#pragma surface surf BlinnPhongLight noforwardAdd
然后就跟图二一样不受光了。这下问题就水落石出了。
关于表面着色器的机制和原理,请看我的上一篇文章:
(二)unity Shader之——————简单易懂的深度剖析研究unity表面着色器(Surface Shader)
原文链接:https://blog.csdn.net/cgy56191948/article/details/100118784
目前只有一个pass块,前向渲染 的"LightMode" = "ForwardBase",只需要加一个pass块,定义"LightMode" = "ForwardAdd"
编译指令和宏的定义跟前者一致,在一些特殊的地方进行修改即可,例如前者的:
#pragma multi_compile_fwdbase nodynlightmap
#pragma multi_compile_fwdbase
#define _FWD_BASE
后者改为:
#pragma multi_compile_fwdadd nodynlightmap
#pragma multi_compile_fwdadd
#define _FWD_ADD
然后可能需要更改的地方是光照模型计算了。那我的项目来说,因为在前向渲染 的"LightMode" = "ForwardBase" 的Pass块中,我用的光源不是unity的标准点光源,而是进行封装和计算的光源信息,光照模型的计算比较复杂。但是现在需要让untiy标准点光源对其物体受光,那么很好办,直接拿untiy内置的(Lambert或BlinnPhong)光照函数里面的代码即可(在Lighting.cginc里面)。
Lambert光照函数:
inline fixed4 UnityLambertLight (SurfaceOutput s, UnityLight light)
{
fixed diff = max (0, dot (s.Normal, light.dir));
fixed4 c;
c.rgb = s.Albedo * light.color * diff;
c.a = s.Alpha;
return c;
}
inline fixed4 LightingLambert (SurfaceOutput s, UnityGI gi)
{
fixed4 c;
c = UnityLambertLight (s, gi.light);
#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
c.rgb += s.Albedo * gi.indirect.diffuse;
#endif
return c;
}
BlinnPhong光照函数:
inline fixed4 UnityBlinnPhongLight (SurfaceOutput s, half3 viewDir, UnityLight light)
{
half3 h = normalize (light.dir + viewDir);
fixed diff = max (0, dot (s.Normal, light.dir));
float nh = max (0, dot (s.Normal, h));
float spec = pow (nh, s.Specular*128.0) * s.Gloss;
fixed4 c;
c.rgb = s.Albedo * light.color * diff + light.color * _SpecColor.rgb * spec;
c.a = s.Alpha;
return c;
}
inline fixed4 LightingBlinnPhong (SurfaceOutput s, half3 viewDir, UnityGI gi)
{
fixed4 c;
c = UnityBlinnPhongLight (s, viewDir, gi.light);
#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
c.rgb += s.Albedo * gi.indirect.diffuse;
#endif
return c;
}
下面是我简化后的代码:
//光照模型的计算
UnityLight light = gi.light;
fixed diff = max(0, dot(Normal, light.dir));
c.rgb = Albedo * light.color * diff;
#ifndef _DISABLE_NORMAL_MAP
half3 h = normalize(light.dir + worldViewDir);
float nh = max(0, dot(Normal, h));
float spec = pow(nh, Specular*128.0) * Gloss;
c.rgb += light.color * _SpecColor.rgb * spec;
#endif
c.a = Alpha;
//#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
//c.rgb += Albedo * gi.indirect.diffuse;
//#endif
return c;
GI需要自己去算,根据项目实际需求来,顶点和片元函数其他的代码就不贴了。我还定义了控制法线贴图是否生效的宏。
添加完毕,效果就跟原来的surface shader产生的效果一模一样了。