Shader随笔02

发布时间 2024-01-11 11:17:29作者: 被迫吃冰淇淋的小学生

Global Bake

这里是简化Lighting.cginc的UnityGI_Base函数

以及AutoLight.cginc的LightingLambert

来实现bake贴图采样(没开灯光)

其中,Mixed是重点
Directional Mode是重点

Light组件的Mode需要调成Mixed

完成以上内容可以在使用unity标准Shader的情况下看的bake情况

自定义cginc文件

直接看pass

Baked光照贴图采样技术
Pass
{
    Tags {"LightMode"="ForwardBase"}
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    //需要使用这两个宏定义
    #pragma multi_compile DIRLIGHTMAP_COMBINED
    #pragma multi_compile LIGHTMAP_ON

    #include "UnityCG.cginc"
    #include "CGINC/MyCGInc.cginc"

    struct appdata
    {
        float4 vertex : POSITION;
        #if defined(LIGHTMAP_ON)
            float4 texcoord1 : TEXCOORD1;
        #endif
        float3 normal : NORMAL;
    };

    struct v2f
    {
        float4 pos : SV_POSITION;
        float3 worldNormal : NORMAL;
        float3 worldPos : TEXCOORD0;
        #if defined(LIGHTMAP_ON)
            fixed4 lightmapUV : TEXCOORD1;
        #endif
    };

    v2f vert (appdata v)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.worldNormal = mul(unity_ObjectToWorld, v.normal);
        o.worldPos = mul(unity_ObjectToWorld, v.vertex);
        
        #if defined(LIGHTMAP_ON)
            o.lightmapUV.xy = v.texcoord1 * unity_LightmapST.xy + unity_LightmapST.zw;
        #endif

        return o;
    }

    fixed4 frag (v2f i) : SV_Target
    {
        SurfaceOutput o;
        UNITY_INITIALIZE_OUTPUT(SurfaceOutput, o);
        o.Albedo = 1;
        o.Normal = i.worldNormal;

        UnityGI gi;
        UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
        gi.light.color = _LightColor0;
        gi.light.dir = _WorldSpaceLightPos0;

        UnityGIInput giInput;
        UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
        giInput.light = gi.light;
        giInput.worldPos = i.worldPos;
        giInput.worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
        giInput.atten = 0.5;//先默认给一个,后续需要计算光照衰减

        #if defined(LIGHTMAP_ON)
            giInput.lightmapUV = i.lightmapUV;
        #endif

        //对烘焙贴图进行采样
        UnityGI_Base(giInput,1.0h,i.worldNormal, gi);
        //计算一个简单的Lambert光照模型
        return LightingLambert(o, gi);
    }
    ENDCG
}

这些gi啊giInput啊什么什么的,都来根据Unity内部实现使用的

来自于Lighting.cginc

同时,自定义的cginc文件也是改编自Lighting.cginc

1.SurfaceOutput是Lighting.cginc定义的struct,我们可以在自定义cginc中也定义一个

这里我定义了一个简略版,就是把需要的数据留着,不用的就删了

2.UnityGI和UnityGIInput也都是struct,由于东西太多,我这使用的#include "UnityLightingCommon.cginc"

这样,该cginc和该pass就可以使用这两个struct了

3.LightingLambert来自于Lighting.cginc,是实现一个简单的Lambert光照模型

4.UnityGI_Base来自于Lighting.cginc引入的UnityGlobalIllumination.cginc 

这里使用的是改编版,只留下了一个宏定义

UnityGI_Base原版
inline UnityGI UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld)
{
    UnityGI o_gi;
    ResetUnityGI(o_gi);

    // Base pass with Lightmap support is responsible for handling ShadowMask / blending here for performance reason
    #if defined(HANDLE_SHADOWS_BLENDING_IN_GI)
        half bakedAtten = UnitySampleBakedOcclusion(data.lightmapUV.xy, data.worldPos);
        float zDist = dot(_WorldSpaceCameraPos - data.worldPos, UNITY_MATRIX_V[2].xyz);
        float fadeDist = UnityComputeShadowFadeDistance(data.worldPos, zDist);
        data.atten = UnityMixRealtimeAndBakedShadows(data.atten, bakedAtten, UnityComputeShadowFade(fadeDist));
    #endif

    o_gi.light = data.light;
    o_gi.light.color *= data.atten;

    #if UNITY_SHOULD_SAMPLE_SH
        o_gi.indirect.diffuse = ShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
    #endif

    #if defined(LIGHTMAP_ON)
        // Baked lightmaps
        half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
        half3 bakedColor = DecodeLightmap(bakedColorTex);

        #ifdef DIRLIGHTMAP_COMBINED
            fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
            o_gi.indirect.diffuse += DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld);

            #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
                ResetUnityLight(o_gi.light);
                o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap (o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
            #endif

        #else // not directional lightmap
            o_gi.indirect.diffuse += bakedColor;

            #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
                ResetUnityLight(o_gi.light);
                o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
            #endif

        #endif
    #endif

    #ifdef DYNAMICLIGHTMAP_ON
        // Dynamic lightmaps
        fixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, data.lightmapUV.zw);
        half3 realtimeColor = DecodeRealtimeLightmap (realtimeColorTex);

        #ifdef DIRLIGHTMAP_COMBINED
            half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw);
            o_gi.indirect.diffuse += DecodeDirectionalLightmap (realtimeColor, realtimeDirTex, normalWorld);
        #else
            o_gi.indirect.diffuse += realtimeColor;
        #endif
    #endif

    o_gi.indirect.diffuse *= occlusion;
    return o_gi;
}

我看着就是一个简单的bake贴图采样,不过和法线有关

MyCGInc.cginc
 #ifndef MYCGINC_CGINCLUDE
#define MYCGINC_CGINCLUDE

#include "UnityLightingCommon.cginc"

struct SurfaceOutput {
    fixed3 Albedo;
    fixed3 Normal;
};

inline fixed4 LightingLambert (SurfaceOutput s, UnityGI gi)
{
    fixed4 c;
    fixed diff = max (0, dot (s.Normal, gi.light.dir));
    c.rgb = s.Albedo *  gi.light.color * diff;

    #if defined(LIGHTMAP_ON)
        c.rgb += s.Albedo * gi.indirect.diffuse;
    #endif

    return c;
}

inline void UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld, inout UnityGI gi)
{
    #if defined(LIGHTMAP_ON)
        half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
        half3 bakedColor = DecodeLightmap(bakedColorTex);

        #ifdef DIRLIGHTMAP_COMBINED
            fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
            gi.indirect.diffuse += DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld);
        #endif
    #endif

    //将衰减应用于灯光颜色在
    gi.light.color *= data.atten;

    //环境光遮蔽
    gi.indirect.diffuse *= occlusion;
}

#endif

Global Bake之Non-Directional

近距离采用实时阴影,远距离采用Baked阴影  的技术

1.Light组件的Mode需要调成Mixed

2.Bake设置的LightingMode要调成Shadowmask

3.Bake设置的Directional Mode要调成Non-Directional

4.ProjectSettings-Quality-Shadows-Shadowmask Mode要调成Distance Shadowmask

5.ProjectSettings-Quality-Shadows-ShadowDistance调成你想要的值,这里是 5

效果:Bake的光照PosY是30,实时的光照PosY调了个46

距离小于ShadowDistance就用的实时阴影,大于就用bake阴影


此时上面是实时,下面是bake

继续远离

代码部分:

原先的自定义cginc没有实现该技术

该技术来自于UnityGlobalIllumination.cginc的HANDLE_SHADOWS_BLENDING_IN_GI宏定义,

就是将自定义cginc,修改成了使用Lighting.cginc的Bake阴影技术和AutoLight.cginc的实时阴影技术

代码部分

主pass
Pass
{
    Tags {"LightMode"="ForwardBase"}
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    
    #pragma multi_compile_fwdbase

    #include "UnityCG.cginc"
    #include "Lighting.cginc"
    #include "AutoLight.cginc"

    struct appdata
    {
        float4 vertex : POSITION;
        #if defined(LIGHTMAP_ON)
            float4 texcoord1 : TEXCOORD1;
        #endif
        float3 normal : NORMAL;
    };

    struct v2f
    {
        float4 pos : SV_POSITION;
        float3 worldNormal : NORMAL;
        float3 worldPos : TEXCOORD0;
        #if defined(LIGHTMAP_ON)
            fixed4 lightmapUV : TEXCOORD1;
        #endif
        UNITY_LIGHTING_COORDS(2,3)
    };

    v2f vert (appdata v)
    {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.worldNormal = mul(unity_ObjectToWorld, v.normal);
        o.worldPos = mul(unity_ObjectToWorld, v.vertex);
        
        #if defined(LIGHTMAP_ON)
            o.lightmapUV.xy = v.texcoord1 * unity_LightmapST.xy + unity_LightmapST.zw;
        #endif
        UNITY_TRANSFER_LIGHTING(o, v.texcoord1.xy);

        return o;
    }

    fixed4 frag (v2f i) : SV_Target
    {
        SurfaceOutput o;
        UNITY_INITIALIZE_OUTPUT(SurfaceOutput, o);
        o.Albedo = 1;
        o.Normal = i.worldNormal;

        UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

        UnityGI gi;
        UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
        gi.light.color = _LightColor0;
        gi.light.dir = _WorldSpaceLightPos0;

        UnityGIInput giInput;
        UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
        giInput.light = gi.light;
        giInput.worldPos = i.worldPos;
        giInput.worldViewDir = normalize(_WorldSpaceCameraPos - i.worldPos);
        giInput.atten = atten;

        #if defined(LIGHTMAP_ON) || defined(DYNAMACLIGHTMAP_ON)
            giInput.lightmapUV = i.lightmapUV;
        #endif

        //对烘焙贴图进行采样
        LightingLambert_GI(o,giInput,gi);
        //计算一个简单的Lambert光照模型
        return LightingLambert(o, gi);
    }
    ENDCG
}
阴影Pass
pass
{
    Tags{ "LightMode"="ShadowCaster" }
    CGPROGRAM
    
    #pragma vertex vert
    #pragma fragment frag
    #pragma multi_compile_shadowcaster
    #include "UnityCG.cginc"

    struct a2v
    {
        float4 vertex: POSITION;
        half3 normal: NORMAL;
    };

    struct v2f
    {
        V2F_SHADOW_CASTER;
    };

    v2f vert(a2v v)
    {
        v2f o;
        TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        SHADOW_CASTER_FRAGMENT(i);
    }

    ENDCG
}