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<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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容