(三)unity Shader之——————surface shader转变为自己写的vertex&fragment Shader不受unity的动态实时点光源的影响解决方案

在写环境、植物的Shader时,原来使用的是Surface shader,出于性能的考虑要转变成效果一致的vertex&fragment Shader,转变中遇到了一个问题。

首先我加了一个unity的标准点光源,如果都换成unity的standard shader,或者其他的surface shader(包括现在使用的环境和植物的surface shader),你会发现环境都是受点光源影响的,如下图:

(三)unity Shader之——————surface shader转变为自己写的vertex&fragment Shader不受unity的动态实时点光源的影响解决方案_第1张图片

现在,我把树干和围栏的shader换成我根据surface shader转换成的vertex&fragment Shader,就会发现不受光了(光照模型的计算完全正确的情况下),注意下图中中间的树干和围栏:

 

(三)unity Shader之——————surface shader转变为自己写的vertex&fragment Shader不受unity的动态实时点光源的影响解决方案_第2张图片

经过查找原因,我做了一个实验,在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产生的效果一模一样了。

(三)unity Shader之——————surface shader转变为自己写的vertex&fragment Shader不受unity的动态实时点光源的影响解决方案_第3张图片

你可能感兴趣的:(unity,Shader,前向渲染)