投影

Shadow Maps & Shadow Volume

Posted by Bob on April 12, 2019
Shadow Mapping(阴影映射)

来自CgTutorial的Shadow Mapping实现的基本流程 : 1、首先将整个场景的深度值预先渲染到以光源位置为原点、光线发射方向为观察方向的投影坐标系中,形成深度纹理。 2、再次渲染场景的过程中,将每个片断(像素)变换到前述眼坐标系(观察空间)中,并缩放到[0,1]的范围内以便查询纹理。绘制每个片元时,根据深度纹理距离(ZA)和片元距光源的实际距离(ZB)比较。若ZB>ZA,则需要绘制的片元处于阴影中,采用阴影的颜色着色,否则此片元不在阴影中,进行既定的光照着色。

image

由于深度纹理尺寸的限制,会让形成的阴影边缘锯齿严重。需要进行模糊处理,甚至是半影处理。

Unity内置的方向光实时阴影技术是Cascaded Shadow Mapping(简称CSM)

Shadow Volume

模型顶点到光源的方向与法线求点积,小于0的挤出即为阴影体积。

image

上图来自GPU精粹1-第九章有效的阴影体渲染

Projector Shadow

添加Projector组件


[RequireComponent(typeof(Projector))]
public class ProjectorShadow : MonoBehaviour 
{
    public LayerMask shadowCasterLayer;
    private RenderTexture _renderTex;

    void Awake()
    {
        Projector projector = GetComponent<Projector>();
        Camera camera = gameObject.AddComponent<Camera>();
        camera.cullingMask = shadowCasterLayer;
        camera.orthographic = true;
        camera.orthographicSize = projector.orthographicSize;
        camera.clearFlags = CameraClearFlags.SolidColor;
        camera.backgroundColor = new Color(1.0f,1.0f,1.0f,0.0f);
        _renderTex = new RenderTexture(512,512,0,RenderTextureFormat.R8);
        _renderTex.antiAliasing = 1;   // 关闭抗锯齿
        _renderTex.filterMode = FilterMode.Bilinear;
        _renderTex.wrapMode = TextureWrapMode.Clamp; // wrapmode要设置为Clamp
        camera.targetTexture = _renderTex;
        camera.SetReplacementShader(Shader.Find("Unlit/ProjectorShadow"),null);
        projector.material.SetTexture("_ShadowTex",_renderTex);
    }

    #if UNITY_EDITOR
    void OnApplicationQuit()
    {   
        Projector projector = GetComponent<Projector>();
        projector.material.SetTexture("_ShadowTex",null);
    }
    #endif
}


Shader "Projector/ProjectorShadow" {
    Properties {
        _ShadowTex ("ShadowTex", 2D) = "black"{}
        _FalloffTex ("FallOff", 2D) = "white" {}
    }
    Subshader {
        Tags {"Queue"="Transparent"}
        Pass {
            ZWrite Off
            ColorMask RGB
            Blend DstColor Zero
            Offset -1, -1

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            #include "UnityCG.cginc"
            
            struct v2f {
                float4 uvShadow : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 pos : SV_POSITION;
            };
            
            float4x4 unity_Projector;

            v2f vert (float4 vertex : POSITION)
            {
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, vertex);
                o.uvShadow = mul (unity_Projector, vertex);
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }
            
            sampler2D _ShadowTex;
            sampler2D _FalloffTex;
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 shadowCol  = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
                fixed4 maskCol  = tex2Dproj (_FalloffTex, UNITY_PROJ_COORD(i.uvShadow));
                fixed ratio =  shadowCol.r * maskCol.r;
                fixed4 col = fixed4(1,1,1,1) *  (1 - ratio);
                UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(1,1,1,1));
                return col;
            }
            ENDCG
        }
    }
}

参考资料:

common-techniques-to-improve-shadow-depth-maps

Shadow_Mapping.pdf

learn Shadow-Mapping

opengl-shadow-mapping

Shadow_mapping

cascaded-shadow-maps