美文网首页
GLSL in Unity 系列文章(八):实时阴影实现——Ca

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

作者: 雄关漫道从头越 | 来源:发表于2021-05-20 16:37 被阅读0次

    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<Matrix4x4> world2ShadowMats = new List<Matrix4x4>(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<Camera>();
    
            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 系列文章(八):实时阴影实现——Ca

          本文链接:https://www.haomeiwen.com/subject/cjkbjltx.html