GLSL in Unity 系列文章(八):实时阴影实现——Cascaded Shadow Mapping

Unity实时阴影实现——Shadow Mapping
Unity的实时阴影-ShadowMap实现原理
Unity实时阴影实现——Cascaded Shadow Mapping

用GLSL实现CMS(Cascaded Shadow Mapping)说了好久了,今天抽空搞一下,代码是参考网上大神的,不过翻译成GLSL还是遇到了一些坑,先看看效果吧:


CMS

锯齿还是有点严重,提高shadowmap的分辨率好像没什么效果,软阴影可能好点吧,不过后面有时间再搞吧。

1.生成深度图
Shader "GLSL/ShadowMapping/Caster" 
{
    SubShader {
        Tags {          
            "RenderType" = "Opaque"
        }
        Pass {
            Fog { Mode Off }
            Cull front//设置Cull front可解决面向光源的acne问题
            GLSLPROGRAM
            //gl_Vertex 顶点
            //gl_Position 裁剪空间坐标输出到片元着色器
            //gl_FragColor 输出颜色
            #include "UnityCG.glslinc"
            #include "lib/Custom.glslinc"
            
            uniform float _gShadowBias;

            struct v2f {
                vec4 pos;//其实没用到,为了展示如何使用glsl结构体
                vec2 depth;
            };

            #ifdef VERTEX
            out v2f v;
            void main()
            {            
                gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
                gl_Position.z += _gShadowBias;
                v.depth = gl_Position.zw;
            }
            #endif
            #ifdef FRAGMENT
            in v2f v;
            void main()
            {
                float depth = v.depth.x / v.depth.y;

            #if defined (UNITY_REVERSED_Z)
                depth = 1 - depth;       //(1, 0)-->(0, 1)
            #else
                depth = depth*0.5 + 0.5; //(-1, 1)-->(0, 1)
            #endif

                gl_FragColor = EncodeFloatRGBA(depth);
            }
            #endif
            ENDGLSL  
        }
    }
}
2.生成级联相关数据
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CascadedShadowMapping : MonoBehaviour
{
    public Light dirLight;
    Camera dirLightCamera;//灯光空间的相机

    public int shadowResolution = 1;
    public Shader shadowCaster = null;

    private Matrix4x4 biasMatrix = Matrix4x4.identity;

    List world2ShadowMats = new List(4);
    GameObject[] dirLightCameraSplits = new GameObject[4];
    RenderTexture[] depthTextures = new RenderTexture[4];//四级阴影纹理

    void OnDestroy()
    {
        dirLightCamera = null;

        for (int i = 0; i < 4; i++)
        {
            if (depthTextures[i])
            {
                DestroyImmediate(depthTextures[i]);
            }
        }
    }

    void Awake()
    {
        biasMatrix.SetRow(0, new Vector4(0.5f, 0, 0, 0.5f));
        biasMatrix.SetRow(1, new Vector4(0, 0.5f, 0, 0.5f));
        biasMatrix.SetRow(2, new Vector4(0, 0, 0.5f, 0.5f));
        biasMatrix.SetRow(3, new Vector4(0, 0, 0, 1f));

        InitFrustumCorners();
    }

    //初始化rt,4级阴影纹理对应4张rt
    private void CreateRenderTexture()
    {
        RenderTextureFormat rtFormat = RenderTextureFormat.Default;
        if (!SystemInfo.SupportsRenderTextureFormat(rtFormat))
            rtFormat = RenderTextureFormat.Default;

        for (int i = 0; i < 4; i++)
        {
            depthTextures[i] = new RenderTexture(1024, 1024, 24, rtFormat);
            Shader.SetGlobalTexture("_gShadowMapTexture" + i, depthTextures[i]);
        }
    }

    //创建灯光摄像机
    public Camera CreateDirLightCamera()
    {
        GameObject goLightCamera = new GameObject("Directional Light Camera");
        Camera LightCamera = goLightCamera.AddComponent();

        LightCamera.cullingMask = 1 << LayerMask.NameToLayer("Caster");
        LightCamera.backgroundColor = Color.white;
        LightCamera.clearFlags = CameraClearFlags.SolidColor;
        LightCamera.orthographic = true;
        LightCamera.enabled = false;

        for (int i = 0; i < 4; i++)
        {
            dirLightCameraSplits[i] = new GameObject("dirLightCameraSplits" + i);
        }

        return LightCamera;
    }

    private void Update()
    {
        CalcMainCameraSplitsFrustumCorners();
        CalcLightCameraSplitsFrustum();

        if (dirLight)
        {
            if (!dirLightCamera)
            {
                dirLightCamera = CreateDirLightCamera();
                CreateRenderTexture();
            }

            Shader.SetGlobalFloat("_gShadowBias", 0.005f);
            Shader.SetGlobalFloat("_gShadowStrength", 0.5f);

            world2ShadowMats.Clear();
            //构建4级阴影纹理
            for (int i = 0; i < 4; i++)
            {
                ConstructLightCameraSplits(i);

                dirLightCamera.targetTexture = depthTextures[i];
                dirLightCamera.RenderWithShader(shadowCaster, "");

                Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(dirLightCamera.projectionMatrix, false);
                world2ShadowMats.Add(projectionMatrix * dirLightCamera.worldToCameraMatrix);
            }

            Shader.SetGlobalMatrixArray("_gWorld2Shadow", world2ShadowMats);
        }
    }

    float[] _LightSplitsNear;
    float[] _LightSplitsFar;

    struct FrustumCorners
    {
        public Vector3[] nearCorners;
        public Vector3[] farCorners;
    }

    FrustumCorners[] mainCamera_Splits_fcs;
    FrustumCorners[] lightCamera_Splits_fcs;

    //初始化4级远近裁剪面坐标
    void InitFrustumCorners()
    {
        mainCamera_Splits_fcs = new FrustumCorners[4];
        lightCamera_Splits_fcs = new FrustumCorners[4];
        for (int i = 0; i < 4; i++)
        {
            mainCamera_Splits_fcs[i].nearCorners = new Vector3[4];
            mainCamera_Splits_fcs[i].farCorners = new Vector3[4];

            lightCamera_Splits_fcs[i].nearCorners = new Vector3[4];
            lightCamera_Splits_fcs[i].farCorners = new Vector3[4];
        }
    }

    void CalcMainCameraSplitsFrustumCorners()
    {
        float near = Camera.main.nearClipPlane;
        float far = Camera.main.farClipPlane;

        //分4级:x + x*2 + x*2*2 + x*2*2*2 = 100% ==>15*x = 100% ==> x = 0.066666666≈0.067 = 6.7%
        //得到6.7%、13.3%、26.7%、53.3%等分的4级远近裁剪坐标
        float[] nears = { near, far * 0.067f + near, far * 0.133f + far * 0.067f + near, far * 0.267f + far * 0.133f + far * 0.067f + near };
        float[] fars = { far * 0.067f + near, far * 0.133f + far * 0.067f + near, far * 0.267f + far * 0.133f + far * 0.067f + near, far };

        _LightSplitsNear = nears;
        _LightSplitsFar = fars;

        Shader.SetGlobalVector("_gLightSplitsNear", new Vector4(_LightSplitsNear[0], _LightSplitsNear[1], _LightSplitsNear[2], _LightSplitsNear[3]));
        Shader.SetGlobalVector("_gLightSplitsFar", new Vector4(_LightSplitsFar[0], _LightSplitsFar[1], _LightSplitsFar[2], _LightSplitsFar[3]));

        //计算主摄像机的4级视锥体
        for (int k = 0; k < 4; k++)
        {
            Camera.main.CalculateFrustumCorners(new Rect(0, 0, 1, 1), _LightSplitsNear[k], Camera.MonoOrStereoscopicEye.Mono, mainCamera_Splits_fcs[k].nearCorners);
            for (int i = 0; i < 4; i++)
            {
                mainCamera_Splits_fcs[k].nearCorners[i] = Camera.main.transform.TransformPoint(mainCamera_Splits_fcs[k].nearCorners[i]);
            }

            Camera.main.CalculateFrustumCorners(new Rect(0, 0, 1, 1), _LightSplitsFar[k], Camera.MonoOrStereoscopicEye.Mono, mainCamera_Splits_fcs[k].farCorners);
            for (int i = 0; i < 4; i++)
            {
                mainCamera_Splits_fcs[k].farCorners[i] = Camera.main.transform.TransformPoint(mainCamera_Splits_fcs[k].farCorners[i]);
            }
        }
    }

    //计算灯光相机包围盒
    void CalcLightCameraSplitsFrustum()
    {
        if (dirLightCamera == null)
            return;

        for (int k = 0; k < 4; k++)
        {
            for (int i = 0; i < 4; i++)
            {
                lightCamera_Splits_fcs[k].nearCorners[i] = dirLightCameraSplits[k].transform.InverseTransformPoint(mainCamera_Splits_fcs[k].nearCorners[i]);
                lightCamera_Splits_fcs[k].farCorners[i] = dirLightCameraSplits[k].transform.InverseTransformPoint(mainCamera_Splits_fcs[k].farCorners[i]);
            }

            float[] xs = { lightCamera_Splits_fcs[k].nearCorners[0].x, lightCamera_Splits_fcs[k].nearCorners[1].x, lightCamera_Splits_fcs[k].nearCorners[2].x, lightCamera_Splits_fcs[k].nearCorners[3].x,
                       lightCamera_Splits_fcs[k].farCorners[0].x, lightCamera_Splits_fcs[k].farCorners[1].x, lightCamera_Splits_fcs[k].farCorners[2].x, lightCamera_Splits_fcs[k].farCorners[3].x };

            float[] ys = { lightCamera_Splits_fcs[k].nearCorners[0].y, lightCamera_Splits_fcs[k].nearCorners[1].y, lightCamera_Splits_fcs[k].nearCorners[2].y, lightCamera_Splits_fcs[k].nearCorners[3].y,
                       lightCamera_Splits_fcs[k].farCorners[0].y, lightCamera_Splits_fcs[k].farCorners[1].y, lightCamera_Splits_fcs[k].farCorners[2].y, lightCamera_Splits_fcs[k].farCorners[3].y };

            float[] zs = { lightCamera_Splits_fcs[k].nearCorners[0].z, lightCamera_Splits_fcs[k].nearCorners[1].z, lightCamera_Splits_fcs[k].nearCorners[2].z, lightCamera_Splits_fcs[k].nearCorners[3].z,
                       lightCamera_Splits_fcs[k].farCorners[0].z, lightCamera_Splits_fcs[k].farCorners[1].z, lightCamera_Splits_fcs[k].farCorners[2].z, lightCamera_Splits_fcs[k].farCorners[3].z };

            float minX = Mathf.Min(xs);
            float maxX = Mathf.Max(xs);

            float minY = Mathf.Min(ys);
            float maxY = Mathf.Max(ys);

            float minZ = Mathf.Min(zs);
            float maxZ = Mathf.Max(zs);

            lightCamera_Splits_fcs[k].nearCorners[0] = new Vector3(minX, minY, minZ);
            lightCamera_Splits_fcs[k].nearCorners[1] = new Vector3(maxX, minY, minZ);
            lightCamera_Splits_fcs[k].nearCorners[2] = new Vector3(maxX, maxY, minZ);
            lightCamera_Splits_fcs[k].nearCorners[3] = new Vector3(minX, maxY, minZ);

            lightCamera_Splits_fcs[k].farCorners[0] = new Vector3(minX, minY, maxZ);
            lightCamera_Splits_fcs[k].farCorners[1] = new Vector3(maxX, minY, maxZ);
            lightCamera_Splits_fcs[k].farCorners[2] = new Vector3(maxX, maxY, maxZ);
            lightCamera_Splits_fcs[k].farCorners[3] = new Vector3(minX, maxY, maxZ);

            Vector3 pos = lightCamera_Splits_fcs[k].nearCorners[0] + (lightCamera_Splits_fcs[k].nearCorners[2] - lightCamera_Splits_fcs[k].nearCorners[0]) * 0.5f;

            dirLightCameraSplits[k].transform.position = dirLightCameraSplits[k].transform.TransformPoint(pos);
            dirLightCameraSplits[k].transform.rotation = dirLight.transform.rotation;
        }
    }

    void ConstructLightCameraSplits(int k)
    {
        dirLightCamera.transform.position = dirLightCameraSplits[k].transform.position;
        dirLightCamera.transform.rotation = dirLightCameraSplits[k].transform.rotation;

        dirLightCamera.nearClipPlane = lightCamera_Splits_fcs[k].nearCorners[0].z;
        dirLightCamera.farClipPlane = lightCamera_Splits_fcs[k].farCorners[0].z;

        dirLightCamera.aspect = Vector3.Magnitude(lightCamera_Splits_fcs[k].nearCorners[0] - lightCamera_Splits_fcs[k].nearCorners[1]) / Vector3.Magnitude(lightCamera_Splits_fcs[k].nearCorners[1] - lightCamera_Splits_fcs[k].nearCorners[2]);
        dirLightCamera.orthographicSize = Vector3.Magnitude(lightCamera_Splits_fcs[k].nearCorners[1] - lightCamera_Splits_fcs[k].nearCorners[2]) * 0.5f;
    }
}
3.接受阴影
Shader "GLSL/CSMShadowMapping/Receiver" {

    SubShader {
        Tags { "RenderType"="Opaque"  }

        LOD 300 

        Pass {
            Name "FORWARD"
            Tags{ "LightMode" = "ForwardBase" }

            GLSLPROGRAM
            //gl_Vertex 顶点
            //gl_Position 裁剪空间坐标输出到片元着色器
            //gl_FragColor 输出颜色
            #include "UnityCG.glslinc"
            #include "lib/Custom.glslinc"
            #pragma fragmentoption ARB_precision_hint_fastest 

            uniform mat4 _gWorldToShadow;
            uniform sampler2D _gShadowMapTexture;
            uniform vec4 _gShadowMapTexture_TexelSize;
            /*{TextureName}_TexelSize - a float4 property contains texture size information :
            x contains 1.0 / width
            y contains 1.0 / height
            z contains width
            w contains height*/

            uniform vec4 _gLightSplitsNear;
            uniform vec4 _gLightSplitsFar;
            uniform mat4 _gWorld2Shadow[4];
            
            uniform sampler2D _gShadowMapTexture0;
            uniform sampler2D _gShadowMapTexture1;
            uniform sampler2D _gShadowMapTexture2;
            uniform sampler2D _gShadowMapTexture3;
            uniform float _gShadowStrength;

            struct v2f
            {
                vec2 uv;
                vec4 shadowCoord;
                float eyeZ;
                vec4 worldPos;
            };
            

            //3x3的PCF Soft Shadow
            float PCFSample(float depth, vec2 uv)
            {
                float shadow = 0.0;
                for (int x = -1; x <= 1; ++x)
                {
                    for (int y = -1; y <= 1; ++y)
                    {
                        vec4 col = texture(_gShadowMapTexture, uv + vec2(x, y) * _gShadowMapTexture_TexelSize.xy);
                        float sampleDepth = DecodeFloatRGBA(col);
                        shadow += sampleDepth < depth ? _gShadowStrength : 1.0;//接受物体片元的深度与深度图的值比较,大于则表示被挡住灯光,显示为阴影,否则显示自己的颜色(这里显示白色)
                    }
                }
                return shadow /= 9.0;
            }

            vec4 getCascadeWeights(float z)
            {
                vec4 zNear = vec4(z >= _gLightSplitsNear.x?1.0:0.0,z >= _gLightSplitsNear.y?1.0:0.0,z >= _gLightSplitsNear.z?1.0:0.0,z >= _gLightSplitsNear.w?1.0:0.0);
                vec4 zFar = vec4(z < _gLightSplitsFar.x?1.0:0.0,z < _gLightSplitsFar.y?1.0:0.0,z < _gLightSplitsFar.z?1.0:0.0,z < _gLightSplitsFar.w?1.0:0.0);
                vec4 weights = zNear * zFar;
                return weights;
            }

            vec4 getShadowCoord(vec4 wpos, vec4 cascadeWeights)
            {
                vec3 sc0 = (_gWorld2Shadow[0] * wpos).xyz;
                vec3 sc1 = (_gWorld2Shadow[1] * wpos).xyz;
                vec3 sc2 = (_gWorld2Shadow[2] * wpos).xyz;
                vec3 sc3 = (_gWorld2Shadow[3] * wpos).xyz;
                return vec4(sc0 * cascadeWeights[0] + sc1 * cascadeWeights[1] + sc2 * cascadeWeights[2] + sc3 * cascadeWeights[3], 1);
            }

            vec4 SampleShadowTexture(vec4 wPos, vec4 cascadeWeights)
            {
                vec4 shadowCoord0 = (_gWorld2Shadow[0] * wPos);
                vec4 shadowCoord1 = (_gWorld2Shadow[1] * wPos);
                vec4 shadowCoord2 = (_gWorld2Shadow[2] * wPos);
                vec4 shadowCoord3 = (_gWorld2Shadow[3] * wPos);

                shadowCoord0.xy /= shadowCoord0.w;
                shadowCoord1.xy /= shadowCoord1.w;
                shadowCoord2.xy /= shadowCoord2.w;
                shadowCoord3.xy /= shadowCoord3.w;

                shadowCoord0.xy = shadowCoord0.xy*0.5 + 0.5;
                shadowCoord1.xy = shadowCoord1.xy*0.5 + 0.5;
                shadowCoord2.xy = shadowCoord2.xy*0.5 + 0.5;
                shadowCoord3.xy = shadowCoord3.xy*0.5 + 0.5;

                vec4 sampleDepth0 = texture(_gShadowMapTexture0, shadowCoord0.xy);
                vec4 sampleDepth1 = texture(_gShadowMapTexture1, shadowCoord1.xy);
                vec4 sampleDepth2 = texture(_gShadowMapTexture2, shadowCoord2.xy);
                vec4 sampleDepth3 = texture(_gShadowMapTexture3, shadowCoord3.xy);

                float depth0 = shadowCoord0.z / shadowCoord0.w;
                float depth1 = shadowCoord1.z / shadowCoord1.w;
                float depth2 = shadowCoord2.z / shadowCoord2.w;
                float depth3 = shadowCoord3.z / shadowCoord3.w;

                #if defined (UNITY_REVERSED_Z)
                    depth0 = 1 - depth0;       //(1, 0)-->(0, 1)
                    depth1 = 1 - depth1;
                    depth2 = 1 - depth2;
                    depth3 = 1 - depth3;
                #else
                    depth0 = depth0*0.5 + 0.5; //(-1, 1)-->(0, 1)
                    depth1 = depth1*0.5 + 0.5;
                    depth2 = depth2*0.5 + 0.5;
                    depth3 = depth3*0.5 + 0.5;
                #endif

                float shadow0 = sampleDepth0.r < depth0 ? _gShadowStrength : 1.0;
                float shadow1 = sampleDepth1.r < depth1 ? _gShadowStrength : 1.0;
                float shadow2 = sampleDepth2.r < depth2 ? _gShadowStrength : 1.0;
                float shadow3 = sampleDepth3.r < depth3 ? _gShadowStrength : 1.0;

                //return col0;
                float shadow = shadow0 * cascadeWeights[0] + shadow1 * cascadeWeights[1] + shadow2 * cascadeWeights[2] + shadow3 * cascadeWeights[3];
                //return shadow * cascadeWeights;
                return vec4(shadow, shadow, shadow, shadow);
            }

            #ifdef VERTEX
            out v2f o;
            void main()
            {            
                gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
                o.uv = gl_MultiTexCoord0.xy;
                o.worldPos = unity_ObjectToWorld * gl_Vertex;
                o.shadowCoord = _gWorldToShadow * o.worldPos;
                o.eyeZ = gl_Position.w;
            }
            #endif
            #ifdef FRAGMENT
            in v2f o;
            void main()
            {
                vec4 weights = getCascadeWeights(o.eyeZ);
                // sample depth texture
                vec4 col = SampleShadowTexture(o.worldPos, weights);//310以后texture2D过期了,使用texture函数
                gl_FragColor = col;
            }
            #endif
            ENDGLSL
        }
    }
}

github:https://github.com/eangulee/GLSLInUnity.git

你可能感兴趣的:(GLSL in Unity 系列文章(八):实时阴影实现——Cascaded Shadow Mapping)