Unity实时阴影实现——Cascaded Shadow Mapping

前言

Unity内置的方向光实时阴影技术是Cascaded Shadow Mapping(简称CSM)。 由于Unity封装的原因,可能并不能满足实际项目实时阴影的需求,但我们可以从Unity开源出来的built-in shaders看出一些实现细节,这些可以作为自定义实现CSM的参考。

简述

Perspective aliasing in a shadow map is one of the most difficult problems to overcome. In the technical article, Common Techniques to Improve Shadow Depth Maps, perspective aliasing is described and some approaches to mitigate the problem are identified. In practice, CSMs tend to be the best solution, and are commonly employed in modern games.The basic concept of CSMs is easy to understand. Different areas of the camera frustum require shadow maps with different resolutions. Objects nearest the eye require a higher resolution than do more distant objects. In fact, when the eye moves very close to the geometry, the pixels nearest the eye can require so much resolution that even a 4096 × 4096 shadow map is insufficient.

基本的Shadow mapping在大场景下存在阴影图精度问题:

1.主相机整个视锥生成一张阴影图,会导致单个物体占用阴影图的比例太小,读取阴影图时出现采样精度不够(多个像素采样一个图素),产生锯齿。使用尺寸更大的阴影图可以改善这个问题,但会导致内存使用增加。

2.相机近端和远端物体对阴影图采样精度一样,会导致近端物体采样精度不够,远端物体采样精度浪费。

大场景方向光动态阴影的解决方案一般是采用CSM技术。CSM通过把相机的视锥体按远近划分为几个级别,处于各个级别下的物体深度信息绘制到各级阴影图中,显示时采样各自对应的阴影图。见下图示意:


2级shadow map采样示意图

实现

下面简述用Unity实现的大致步骤。

灯光相机视锥

正交的灯光相机视锥为长方体,为了减少灯光相机的无效绘制,需调整大小为主相机视锥的包围盒。

获取主相机视锥顶点

void InitFrustumCorners()
{
    mainCamera_fcs = new FrustumCorners();
    mainCamera_fcs.nearCorners = new Vector3[4];
    mainCamera_fcs.farCorners = new Vector3[4];
    lightCamera_fcs = new FrustumCorners();
    lightCamera_fcs.nearCorners = new Vector3[4];
    lightCamera_fcs.farCorners = new Vector3[4];
}

void CalcMainCameraFrustumCorners()
{
Camera.main.CalculateFrustumCorners(new Rect(0, 0, 1, 1), Camera.main.nearClipPlane, Camera.MonoOrStereoscopicEye.Mono, mainCamera_fcs.nearCorners);

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">mainCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">Camera</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">transform</span><span class="p">.</span><span class="n">TransformPoint</span><span class="p">(</span><span class="n">mainCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="p">}</span>

<span class="n">Camera</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">CalculateFrustumCorners</span><span class="p">(</span><span class="n">new</span> <span class="n">Rect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="n">Camera</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">farClipPlane</span><span class="p">,</span>  <span class="n">Camera</span><span class="p">.</span><span class="n">MonoOrStereoscopicEye</span><span class="p">.</span><span class="n">Mono</span><span class="p">,</span> <span class="n">mainCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">);</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">mainCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">Camera</span><span class="p">.</span><span class="n">main</span><span class="p">.</span><span class="n">transform</span><span class="p">.</span><span class="n">TransformPoint</span><span class="p">(</span><span class="n">mainCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="p">}</span>

}

这里用到了Unity的API:CalculateFrustumCorners,可方便获取相机Local的视锥顶点坐标,使用时需转换到World空间下。

计算灯光相机包围盒

因为计算得到的包围盒会被直接作为灯光相机的视锥,所以一般包围盒的计算转换到灯光相机空间进行。

void CalcLightCameraFrustum()
{
<span class="k">if</span> <span class="p">(</span><span class="n">dirLightCamera</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
    <span class="k">return</span><span class="p">;</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="m">4</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
<span class="p">{</span>
    <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="n">dirLightCamera</span><span class="p">.</span><span class="n">transform</span><span class="p">.</span><span class="n">InverseTransformPoint</span><span class="p">(</span><span class="n">mainCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="n">dirLightCamera</span><span class="p">.</span><span class="n">transform</span><span class="p">.</span><span class="n">InverseTransformPoint</span><span class="p">(</span><span class="n">mainCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="p">}</span>

<span class="kt">float</span><span class="p">[]</span> <span class="n">xs</span> <span class="p">=</span> <span class="p">{</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">x</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">x</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">2</span><span class="p">].</span><span class="n">x</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">3</span><span class="p">].</span><span class="n">x</span><span class="p">,</span>
                <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">x</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">x</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">2</span><span class="p">].</span><span class="n">x</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">3</span><span class="p">].</span><span class="n">x</span> <span class="p">};</span>

<span class="kt">float</span><span class="p">[]</span> <span class="n">ys</span> <span class="p">=</span> <span class="p">{</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">y</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">y</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">2</span><span class="p">].</span><span class="n">y</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">3</span><span class="p">].</span><span class="n">y</span><span class="p">,</span>
                <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">y</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">y</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">2</span><span class="p">].</span><span class="n">y</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">3</span><span class="p">].</span><span class="n">y</span> <span class="p">};</span>

<span class="kt">float</span><span class="p">[]</span> <span class="n">zs</span> <span class="p">=</span> <span class="p">{</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">z</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">z</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">2</span><span class="p">].</span><span class="n">z</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">3</span><span class="p">].</span><span class="n">z</span><span class="p">,</span>
                <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="n">z</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">1</span><span class="p">].</span><span class="n">z</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">2</span><span class="p">].</span><span class="n">z</span><span class="p">,</span> <span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">3</span><span class="p">].</span><span class="n">z</span> <span class="p">};</span>

<span class="kt">float</span> <span class="n">minX</span> <span class="p">=</span> <span class="n">Mathf</span><span class="p">.</span><span class="n">Min</span><span class="p">(</span><span class="n">xs</span><span class="p">);</span>
<span class="kt">float</span> <span class="n">maxX</span> <span class="p">=</span> <span class="n">Mathf</span><span class="p">.</span><span class="n">Max</span><span class="p">(</span><span class="n">xs</span><span class="p">);</span>
<span class="kt">float</span> <span class="n">minY</span> <span class="p">=</span> <span class="n">Mathf</span><span class="p">.</span><span class="n">Min</span><span class="p">(</span><span class="n">ys</span><span class="p">);</span>
<span class="kt">float</span> <span class="n">maxY</span> <span class="p">=</span> <span class="n">Mathf</span><span class="p">.</span><span class="n">Max</span><span class="p">(</span><span class="n">ys</span><span class="p">);</span>
<span class="kt">float</span> <span class="n">minZ</span> <span class="p">=</span> <span class="n">Mathf</span><span class="p">.</span><span class="n">Min</span><span class="p">(</span><span class="n">zs</span><span class="p">);</span>
<span class="kt">float</span> <span class="n">maxZ</span> <span class="p">=</span> <span class="n">Mathf</span><span class="p">.</span><span class="n">Max</span><span class="p">(</span><span class="n">zs</span><span class="p">);</span>

<span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">0</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vector3</span><span class="p">(</span><span class="n">minX</span><span class="p">,</span> <span class="n">minY</span><span class="p">,</span> <span class="n">minZ</span><span class="p">);</span>
<span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">1</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vector3</span><span class="p">(</span><span class="n">maxX</span><span class="p">,</span> <span class="n">minY</span><span class="p">,</span> <span class="n">minZ</span><span class="p">);</span>
<span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">2</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vector3</span><span class="p">(</span><span class="n">maxX</span><span class="p">,</span> <span class="n">maxY</span><span class="p">,</span> <span class="n">minZ</span><span class="p">);</span>
<span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">nearCorners</span><span class="p">[</span><span class="m">3</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vector3</span><span class="p">(</span><span class="n">minX</span><span class="p">,</span> <span class="n">maxY</span><span class="p">,</span> <span class="n">minZ</span><span class="p">);</span>

<span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">0</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vector3</span><span class="p">(</span><span class="n">minX</span><span class="p">,</span> <span class="n">minY</span><span class="p">,</span> <span class="n">maxZ</span><span class="p">);</span>
<span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">1</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vector3</span><span class="p">(</span><span class="n">maxX</span><span class="p">,</span> <span class="n">minY</span><span class="p">,</span> <span class="n">maxZ</span><span class="p">);</span>
<span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">2</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vector3</span><span class="p">(</span><span class="n">maxX</span><span class="p">,</span> <span class="n">maxY</span><span class="p">,</span> <span class="n">maxZ</span><span class="p">);</span>
<span class="n">lightCamera_fcs</span><span class="p">.</span><span class="n">farCorners</span><span class="p">[</span><span class="m">3</span><span class="p">]</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Vector3</span><span class="p">(</span><span class="n">minX</span><span class="p">,</span> <span class="n">maxY</span><span class="p">,</span> <span class="n">maxZ</span><span class="p">);</span>
<span class="p">...</span>

}

得到的包围盒用线框绘制出来见下图:



上图的灰色线框表示主相机视锥,绿色线框表示包围盒,红色线框表示近平面。

构造灯光相机

Vector3 pos = lightCamera_fcs.nearCorners[0] + (lightCamera_fcs.nearCorners[2] - lightCamera_fcs.nearCorners[0])  0.5f;
dirLightCamera.transform.position = dirLightCamera.transform.TransformPoint(pos);
dirLightCamera.transform.rotation = dirLight.transform.rotation;
dirLightCamera.nearClipPlane = minZ;
dirLightCamera.farClipPlane = maxZ;
dirLightCamera.aspect = Vector3.Magnitude(lightCamera_fcs.nearCorners[0] - lightCamera_fcs.nearCorners[1]) / Vector3.Magnitude(lightCamera_fcs.nearCorners[1] - lightCamera_fcs.nearCorners[2]);
dirLightCamera.orthographicSize = Vector3.Magnitude(lightCamera_fcs.nearCorners[1] - lightCamera_fcs.nearCorners[2]) 0.5f;

上述代码设置灯光相机的视锥参数(也是灯光相机的投影矩阵)nearClipPlane,farClipPlane,aspect和orthographicSize。

灯光相机的位置和朝向也需要设置,其中位置为近平面的中点,计算方法见下图:


[公式]

主相机视锥分割

Unity的QualitySettings里提供了对相机视锥的分割设置,见下图:

Unity4级视锥分割参数

UnityShaderVariables.cginc用以下变量保存分割参数:

    float4 _LightSplitsNear;
float4 _LightSplitsFar;

这里类似实现:

    float[] _LightSplitsNear;
float[] _LightSplitsFar;
<span class="p">...</span>

<span class="kt">float</span><span class="p">[]</span> <span class="n">nears</span> <span class="p">=</span> <span class="p">{</span> <span class="n">near</span><span class="p">,</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.067f</span> <span class="p">+</span> <span class="n">near</span><span class="p">,</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.133f</span> <span class="p">+</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.067f</span> <span class="p">+</span> <span class="n">near</span><span class="p">,</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.267f</span> <span class="p">+</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.133f</span> <span class="p">+</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.067f</span> <span class="p">+</span> <span class="n">near</span> <span class="p">};</span>
<span class="kt">float</span><span class="p">[]</span> <span class="n">fars</span> <span class="p">=</span> <span class="p">{</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.067f</span> <span class="p">+</span> <span class="n">near</span><span class="p">,</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.133f</span> <span class="p">+</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.067f</span> <span class="p">+</span> <span class="n">near</span><span class="p">,</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.267f</span> <span class="p">+</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.133f</span> <span class="p">+</span> <span class="n">far</span> <span class="p">*</span> <span class="m">0.067f</span> <span class="p">+</span> <span class="n">near</span><span class="p">,</span> <span class="n">far</span> <span class="p">};</span>

<span class="n">_LightSplitsNear</span> <span class="p">=</span> <span class="n">nears</span><span class="p">;</span>
<span class="n">_LightSplitsFar</span> <span class="p">=</span> <span class="n">fars</span><span class="p">;</span>

<span class="p">...</span>

<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="m">4</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
<span class="p">{</span>
    <span class="c1">//计算主相机分割视锥


//计算分割视锥的灯光相机包围盒

}

得到的分割包围盒见下图:

Unity4级视锥分割俯视图


级联阴影图生成

级联灯光相机通过设置各个级别的视锥依次绘制便得到各自对应阴影图:

List<Matrix4x4> world2ShadowMats = new List<Matrix4x4>(4);
RenderTexture[] depthTextures = new RenderTexture[4];

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

world2ShadowMats.Clear();

for (int i = 0; i < 4; i++)
{
ConstructLightCameraSplits(i);
dirLightCamera.targetTexture = depthTextures[i];
dirLightCamera.RenderWithShader(shadowCaster, “”);
projectionMatrix = GL.GetGPUProjectionMatrix(dirLightCamera.projectionMatrix, false);
world2ShadowMats.Add(projectionMatrix * dirLightCamera.worldToCameraMatrix);
}

Shader.SetGlobalMatrixArray("_gWorld2Shadow", world2ShadowMats);

Unity底层实现是用一张2x2阴影图集来保存4级阴影图。由于我使用的Unity版本原因,这里使用4张单独的rt来分别保存。

Unity2017新版本里已经加入对RenderTexture分区域更新的特性,这样便也可以在上层实现类似底层的级联阴影图集。


4级级联阴影图

级联阴影图采样

UnityShaderVariables.cginc用以下变量保存分割后的世界坐标到光源空间投影坐标的矩阵变换:

float4x4 unity_WorldToShadow[4];

配合视锥分割参数和片段深度可获取对应级联索引,Internal-ScreenSpaceShadows.cginc:

inline fixed4 getCascadeWeights(float3 wpos, float z)
{
fixed4 zNear = float4( z >= _LightSplitsNear );
fixed4 zFar = float4( z < _LightSplitsFar );
fixed4 weights = zNear * zFar;
return weights;

}

上面函数传进来的片段世界坐标wpos并没有用:(,返回值4个分量均为布尔值,比如z值0.4得到的返回值为fixed4(0,0,1,0),表示对应3级的矩阵变换和阴影图。

这里类似实现:

uniform float4x4 _gWorld2Shadow[4];

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

<span class="n">shadowCoord0</span><span class="p">.</span><span class="n">xy</span> <span class="p">/=</span> <span class="n">shadowCoord0</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
<span class="n">shadowCoord1</span><span class="p">.</span><span class="n">xy</span> <span class="p">/=</span> <span class="n">shadowCoord1</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
<span class="n">shadowCoord2</span><span class="p">.</span><span class="n">xy</span> <span class="p">/=</span> <span class="n">shadowCoord2</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
<span class="n">shadowCoord3</span><span class="p">.</span><span class="n">xy</span> <span class="p">/=</span> <span class="n">shadowCoord3</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>

<span class="n">shadowCoord0</span><span class="p">.</span><span class="n">xy</span> <span class="p">=</span> <span class="n">shadowCoord0</span><span class="p">.</span><span class="n">xy</span><span class="p">*</span><span class="m">0.5</span> <span class="p">+</span> <span class="m">0.5</span><span class="p">;</span>
<span class="n">shadowCoord1</span><span class="p">.</span><span class="n">xy</span> <span class="p">=</span> <span class="n">shadowCoord1</span><span class="p">.</span><span class="n">xy</span><span class="p">*</span><span class="m">0.5</span> <span class="p">+</span> <span class="m">0.5</span><span class="p">;</span>
<span class="n">shadowCoord2</span><span class="p">.</span><span class="n">xy</span> <span class="p">=</span> <span class="n">shadowCoord2</span><span class="p">.</span><span class="n">xy</span><span class="p">*</span><span class="m">0.5</span> <span class="p">+</span> <span class="m">0.5</span><span class="p">;</span>
<span class="n">shadowCoord3</span><span class="p">.</span><span class="n">xy</span> <span class="p">=</span> <span class="n">shadowCoord3</span><span class="p">.</span><span class="n">xy</span><span class="p">*</span><span class="m">0.5</span> <span class="p">+</span> <span class="m">0.5</span><span class="p">;</span>

<span class="n">float4</span> <span class="n">sampleDepth0</span> <span class="p">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_gShadowMapTexture0</span><span class="p">,</span> <span class="n">shadowCoord0</span><span class="p">.</span><span class="n">xy</span><span class="p">);</span>
<span class="n">float4</span> <span class="n">sampleDepth1</span> <span class="p">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_gShadowMapTexture1</span><span class="p">,</span> <span class="n">shadowCoord1</span><span class="p">.</span><span class="n">xy</span><span class="p">);</span>
<span class="n">float4</span> <span class="n">sampleDepth2</span> <span class="p">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_gShadowMapTexture2</span><span class="p">,</span> <span class="n">shadowCoord2</span><span class="p">.</span><span class="n">xy</span><span class="p">);</span>
<span class="n">float4</span> <span class="n">sampleDepth3</span> <span class="p">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_gShadowMapTexture3</span><span class="p">,</span> <span class="n">shadowCoord3</span><span class="p">.</span><span class="n">xy</span><span class="p">);</span>

<span class="kt">float</span> <span class="n">depth0</span> <span class="p">=</span> <span class="n">shadowCoord0</span><span class="p">.</span><span class="n">z</span> <span class="p">/</span> <span class="n">shadowCoord0</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
<span class="kt">float</span> <span class="n">depth1</span> <span class="p">=</span> <span class="n">shadowCoord1</span><span class="p">.</span><span class="n">z</span> <span class="p">/</span> <span class="n">shadowCoord1</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
<span class="kt">float</span> <span class="n">depth2</span> <span class="p">=</span> <span class="n">shadowCoord2</span><span class="p">.</span><span class="n">z</span> <span class="p">/</span> <span class="n">shadowCoord2</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
<span class="kt">float</span> <span class="n">depth3</span> <span class="p">=</span> <span class="n">shadowCoord3</span><span class="p">.</span><span class="n">z</span> <span class="p">/</span> <span class="n">shadowCoord3</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>

<span class="cp">#if defined (SHADER_TARGET_GLSL)

depth0 = depth00.5 + 0.5; //(-1, 1)–>(0, 1)
depth1 = depth10.5 + 0.5;
depth2 = depth20.5 + 0.5;
depth3 = depth30.5 + 0.5;
#elif defined (UNITY_REVERSED_Z)
depth0 = 1 - depth0; //(1, 0)–>(0, 1)
depth1 = 1 - depth1;
depth2 = 1 - depth2;
depth3 = 1 - depth3;
#endif

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

<span class="c1">//return col0;

float shadow = shadow0cascadeWeights[0] + shadow1cascadeWeights[1] + shadow2cascadeWeights[2] + shadow3cascadeWeights[3];
return shadow;
}

Shadow Cascadeds查看模式

Unity的Scene模式下有一个Shadow Cascadeds查看模式,可以方便查看整个场景的级联阴影图分布。

这种效果简单实现可以把级联级别作为颜色参数叠加到阴影颜色上即可:

return shadow*cascadeWeights;


Unity自带和自实现的Shadow Cascadeds查看模式对比

结论

CSM能有效解决透视相机下的阴影锯齿问题,基本的SM通过增加阴影图大小可改善阴影质量,但不能根本解决问题。

下面展示了同场景下SM和CSM的阴影质量对比,明显可看出CSM使用相同大小的阴影图(2048x2048 = 4x1024x1024),但获得了好得多的阴影质量。


单张2048的阴影贴图和4张1024级联阴影贴图阴影效果对比

当然,CSM存在重复绘制和多次采样的问题,这也是Unity的CSM方案对mobile平台缺省为1个cascade的原因。


完整代码见:

chenyong2github/CascadedShadowMapping github.com图标

参考文献:

Cascaded Shadow Maps

Cascaded Shadow Mapping

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity中可以使用ShadowMap来实现阴影效果,并且可以通过阴影的颜色、透明度等参数来实现阴影的分析和变化。以下是一个简单的实现步骤: 1.在Unity中创建一个场景,并将需要投射阴影的物体放置在场景中。 2.在场景中创建一个光源,并将其设置为投影模式。在光源的参数设置中,选择Shadow Type为Hard Shadows或Soft Shadows,并将Resolution设置为需要的大小。 3.在被投射阴影的物体上,将其接收阴影的属性设置为true。这可以通过在物体的Renderer组件中设置Cast Shadows为On,或者通过代码设置。 4.在阴影的Material中,可以设置颜色、透明度等参数来实现阴影的分析和变化。例如,可以根据时间、位置等因素来改变阴影的颜色或透明度。 以下是一个示例代码,可以在Update函数中实现根据时间变化阴影颜色的效果: ```csharp public class ShadowColor : MonoBehaviour { public Material shadowMaterial; //阴影的材质 public Color startColor; //开始颜色 public Color endColor; //结束颜色 public float duration = 2.0f; //变化时长 private float startTime; //开始时间 void Start() { startTime = Time.time; } void Update() { float t = (Time.time - startTime) / duration; shadowMaterial.color = Color.Lerp(startColor, endColor, t); } } ``` 在该代码中,首先定义了阴影的材质和起始、结束颜色。然后,在Start函数中记录了开始时间,每帧根据时间变化计算阴影颜色,最终将其赋值给阴影的材质。可以根据需要修改该代码来实现不同的阴影分析和变化效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值