学习shadowmap

1.创建场景,放一个球和一个平面上去。
2.添加光照
3.给球一个内置的diff材质
4.赋给平面一个材质,用来接收阴影,shader如下

Shader "Unlit/diff"
{
    Properties
    {
        _Color("Color", Color) = (1,1,1,1)

    }
        SubShader
    {
        Tags{ "RenderType" = "Opaque" 
     "LightMode" = "ForwardBase"}
        LOD 100

        Pass
    {
        CGPROGRAM
        // Upgrade NOTE: excluded shader from DX11; has structs without semantics (struct v2f members worldPos)
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float3 worldNormal : NORMAL;
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

  

            float4 _Color;


            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                // Transform the normal from object space to world space
                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {

                // Get the normal in world space
                fixed3 worldNormal = normalize(i.worldNormal);
            // Get the light direction in world space
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);


                fixed4 diff = saturate(dot(worldLightDir, worldNormal)) * _Color;

                return diff; 
            }
            ENDCG
         }
    }
}

把这个材质赋给球和平面,效果如下


image.png

接下来是要在光源方向渲染一张深度图,作为shadowMap。为了达到此效果,我们需要在光源处挂一个相机,这里通过脚本来挂。新建一个monobehaviour

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class shadowMap : MonoBehaviour {

    Camera _lightCamera;
    public Material depthMat;
    public int qulity = 1;
    public GameObject lightObj;
    
    RenderTexture lightDepthTexture;


    // Use this for initialization
    void Start ()
    {
        Camera curCam = GetComponent<Camera>();
    

        _lightCamera = CreateLightCamera();
        _lightCamera.RenderWithShader(depthMat.shader, "");
        _lightCamera.transform.parent = lightObj.transform;
        _lightCamera.transform.localPosition = Vector3.zero;
        _lightCamera.transform.localRotation = new UnityEngine.Quaternion();

        Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(_lightCamera.projectionMatrix, true);
        Shader.SetGlobalMatrix("_worldToLightClipMat", projectionMatrix * _lightCamera.worldToCameraMatrix);
        _lightCamera.RenderWithShader(depthMat.shader, "");

    }

    public Camera CreateLightCamera()
    {
        GameObject goLightCamera = new GameObject("Shadow Camera");
        Camera LightCamera = goLightCamera.AddComponent<Camera>();
        LightCamera.backgroundColor = Color.black;
        LightCamera.clearFlags = CameraClearFlags.SolidColor;
        LightCamera.orthographic = true;
        LightCamera.orthographicSize = 6f;
        LightCamera.nearClipPlane = 0.3f;
        LightCamera.farClipPlane = 20;
        LightCamera.enabled = false;

        if (!LightCamera.targetTexture)
            LightCamera.targetTexture = CreateTextureFor(LightCamera);
        lightDepthTexture = LightCamera.targetTexture;

        Shader.SetGlobalTexture("_LightDepthTexture", lightDepthTexture);

        return LightCamera;

    }

    private RenderTexture CreateTextureFor(Camera cam)
    {
        RenderTexture rt = new RenderTexture(Screen.width * qulity, Screen.height * qulity, 24, RenderTextureFormat.Default);
        rt.hideFlags = HideFlags.DontSave;

        return rt;
    }

    // Update is called once per frame
    void Update () {
        
    }
}

该脚本的功能,就是在启动时,在光源处创建一个正交相机,然后渲染一帧深度图,放入lightDepthTexture中。注意
Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(_lightCamera.projectionMatrix, true);
这一句第二个参数一定要填true,这样在DX下面,计算出来的屏幕坐标才能在纹理上得到到正确的翻转采样

接下来创建深度图材质

Shader "Unlit/depth"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
    }
        SubShader
    {
        Tags{ "RenderType" = "Opaque" }
        LOD 100

        Pass
    {
        //Cull Front
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag


#include "UnityCG.cginc"

        struct appdata
        {
            float4 vertex : POSITION;
            float2 uv : TEXCOORD0;
        };

        struct v2f
        {
            UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            float2 depth:TEXCOORD0;
        };

        sampler2D _MainTex;
        float4 _MainTex_ST;
        v2f vert(appdata v)
        {
            v2f o;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.depth.xy = o.vertex.zw;
            return o;
        }

        fixed4 frag(v2f i) : SV_Target
        {
            float depth = i.depth.x / i.depth.y;
#if UNITY_REVERSED_Z
            depth = 1 - depth; //(1, 0)-->(0, 1)
#else

            depth = depth * 0.5 + 0.5; //(-1, 1)-->(0, 1)
#endif
            return depth;
        }
            ENDCG
       }
    }
}


为了保证在OpenGL和DX下有同样的效果,需要处理一下
UNITY_REVERSED_Z宏,这个宏在DX11下开启,开启后,深度值在[1,0]之间,近平面是1,远是0 。在OpenGL下,这个宏不会开启,深度值是[-1,1]
运行一下,可以看到这个深度图


image.png

接下来要做的事就是修改diff.shader,让它可以接收阴影。在它的片元着色器中计算屏幕空间的坐标,和深度, 然后采样光源空间的深度图。进行深度比较。

       Shader "Unlit/diff"
{
    Properties
    {
        _Color("Color", Color) = (1,1,1,1)

    }
        SubShader
    {
        Tags{ "RenderType" = "Opaque"
        "LightMode" = "ForwardBase" }
        LOD 100

        Pass
    {
        CGPROGRAM
        // Upgrade NOTE: excluded shader from DX11; has structs without semantics (struct v2f members worldPos)
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

        struct appdata
    {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float3 worldNormal : NORMAL;
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;

        float4 lightClipPos  : TEXCOORD1;
    };



    float4 _Color;


    sampler2D _LightDepthTexture;
    float4 _LightDepthTexture_ST;
    float4x4 _worldToLightClipMat;
    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        // Transform the normal from object space to world space
       float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
        o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
        o.lightClipPos = mul(_worldToLightClipMat, worldPos);
        o.uv = v.uv;
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {

        // Get the normal in world space
        fixed3 worldNormal = normalize(i.worldNormal);
        // Get the light direction in world space
        fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);


        fixed4 diff = saturate(dot(worldLightDir, worldNormal)) * _Color;
        // 取光源坐标系下的深度
        float4 scrPos = ComputeScreenPos(i.lightClipPos);
     

        float depTexture = tex2Dproj(_LightDepthTexture, scrPos).r;


        float depth = i.lightClipPos.z / i.lightClipPos.w;

    #if UNITY_REVERSED_Z
        depth = 1 - depth; //(1, 0)-->(0, 1)
    #else

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

        float shadow = (depTexture )> (depth) ? 1 : 0.5;

        return   diff * shadow;
        }
        ENDCG
      }
    }
}

此时结果是


image.png

此时产生了acne.
需要加个bias修正一下。
float shadow = (depTexture + 0.01 )> (depth) ? 1 : 0.5;


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。