阴影实现

Posted 就当笔记吧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阴影实现相关的知识,希望对你有一定的参考价值。

最终效果

1) 两个阴影重叠的时候,正常

2) 在斜坡上,阴影正常

3) 在平地上遇到障碍物,阴影正常

4) 在平台边缘,镂空的地方,阴影正常

5) 没有做阴影衰减

6) 阴影抗锯齿没做(pcf)

 

原理

ShadowMap阴影是一种实时阴影,其原理其实也很简单,就2步:1、投射阴影,2、接收阴影
1) 投射阴影,就是哪些物体会产生阴影,会把这些物体的深度保存到一张深度图中,这张深度图就叫ShadowMap。在光源位置放一个同方向的相机,就能生成这个深度图了。
2) 接收阴影,就是哪些物体在阴影中,会在其表面显示阴影。所以,核心就是判断物体是否在阴影中,物体自身的深度值 > ShadowMap中的深度值就说明在阴影中, 就会在表面绘制阴影。
 

实现

投射阴影用shader

Shader "My/Shadow/MyShadowCaster"

    SubShader
    
        Tags  "RenderType" = "Opaque" 

        Pass
        
            ZWrite On
            ZTest LEqual
            Cull Front

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct a2v
            
                float4 vertex : POSITION;
            ; 

            struct v2f  
            
                float4 pos : SV_POSITION;
            ;

            v2f vert(a2v v)
            
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex); //模型空间转换为裁剪空间
                return o;
            

            fixed4 frag(v2f i) : SV_Target
            
                float depth = i.pos.z / i.pos.w; //w不为1时: 保证z在[-1, 1]范围内
                #if defined(SHADER_TARGET_GLSL)
                    depth = depth * 0.5 + 0.5;
                #elif defined(UNITY_REVERSED_Z) //DX11, DX12等这样的平台上, depth从1到0(解决浮点数精度和分布造成的Z-Fight闪烁问题)
                    depth = 1 - depth;
                #endif

                return EncodeFloatRGBA(depth);
            

            ENDCG
        
    

 

相机生成ShadowMap逻辑

using UnityEngine;

[RequireComponent(typeof(Camera))]
public class ShadowMapCamera : MonoBehaviour

    public Transform m_DirLight;
    public Shader m_ShadowCaster; //负责将阴影信息投射到shadowMap
    public Transform m_Player; //跟随玩家
    private Vector3 m_LastPlayerPos;

    [Range(0, 1)] public float m_ShadowStrength = 0;
    [Range(0, 2)] public float m_ShadowBias = 0;

    private Camera m_LightCamera;
    private RenderTexture m_ShadowMapTexture;

    private void Awake()
    
        m_LightCamera = GetComponent<Camera>();
        if (null == m_ShadowCaster)
            m_ShadowCaster = Shader.Find("My/Shadow/MyShadowCaster");
    

    void Start()
    
        ResetCamera(m_LightCamera);
        CreateShadowMapTexture();

        m_LightCamera.transform.rotation = m_DirLight.rotation; //假定运行过程中光角度不变

        Shader.SetGlobalFloat("_shadowBias", m_ShadowBias);
        Shader.SetGlobalFloat("_shadowStrength", m_ShadowStrength);

        UpdateLightCameraVPMatrix();
    

    private void UpdateLightCameraVPMatrix()
    
        Matrix4x4 projMatrix = GL.GetGPUProjectionMatrix(m_LightCamera.projectionMatrix, false);
        Matrix4x4 vpMatrix = projMatrix * m_LightCamera.worldToCameraMatrix; //矩阵左乘
        Shader.SetGlobalMatrix("_worldToShadow", vpMatrix);
    

    void Update()
    
        if (m_Player)
        
            var diff = m_Player.position - m_LastPlayerPos;
            if (diff.sqrMagnitude > 0)
            
                m_LastPlayerPos = m_Player.position;
                m_LightCamera.transform.position = m_LastPlayerPos;
                UpdateLightCameraVPMatrix();
            
        

        m_LightCamera.RenderWithShader(m_ShadowCaster, ""); //使用阴影投射shader渲染视野范围内的所有物体
    

    private void ResetCamera(Camera lightCamera)
    
        lightCamera.backgroundColor = Color.white;
        lightCamera.clearFlags = CameraClearFlags.SolidColor;

        lightCamera.orthographic = true;
        lightCamera.orthographicSize = 6f;

        lightCamera.nearClipPlane = -10;
        lightCamera.farClipPlane = 10f;

        lightCamera.enabled = false;
        lightCamera.allowMSAA = false;
        lightCamera.allowHDR = false;
        lightCamera.cullingMask = -1;
    

    private void UpdateShadowMapTexture()
    
        if (null != m_ShadowMapTexture)
        
            m_LightCamera.targetTexture = null;
            RenderTexture.ReleaseTemporary(m_ShadowMapTexture);
            m_ShadowMapTexture = null;
        
        CreateShadowMapTexture();
    

    private void CreateShadowMapTexture()
    
        m_ShadowMapTexture = new RenderTexture(1024, 1024, 16, RenderTextureFormat.RGFloat);
        m_ShadowMapTexture.name = "Shadowmap_RGFloat";
        m_ShadowMapTexture.hideFlags = HideFlags.DontSave;
        m_ShadowMapTexture.wrapMode = TextureWrapMode.Clamp;
        m_ShadowMapTexture.filterMode = FilterMode.Bilinear;

        m_LightCamera.targetTexture = m_ShadowMapTexture;
        Shader.SetGlobalTexture("_MyShadowMapTexture", m_ShadowMapTexture);
    

 

加了接收阴影逻辑的shader

Shader "My/Shadow/DiffusePerPixel_ShadowReceive"

    Properties
    
        _MainTex("Main Tex", 2D) = "white" 
        _Color("Tint", Color) = (1, 1, 1, 1) //贴图颜色色调
    
    SubShader
    
        Pass
        
            Tags  "LightMode" = "ForwardBase"  //设置了LightMode=ForwardBase时, 内置变量_WorldSpaceLightPos0才被赋值

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            
                float4 vertex : POSITION;
                float3 normal : NORMAL; //顶点法线
                float2 texcoord : TEXCOORD0;
            ;

            struct v2f
            
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
                float4 worldPos : TEXCOORD2;
                float4 shadowCoord : TEXCOORD3;
            ;

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;

            //********** 用于阴影Receive
            float4x4 _worldToShadow;
            sampler2D _MyShadowMapTexture; //shadowMap贴图

            float _shadowStrength; //阴影强度
            float _shadowBias; //Shadow Acne问题需要
            //**********

            v2f vert(appdata v)
            
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);

                o.shadowCoord = mul(_worldToShadow, o.worldPos); //LightCamera的VP_Matrix
                return o;
            

            float hardShadow(float4 shadowCoord)
            
                float2 uv = shadowCoord.xy / shadowCoord.w;
                uv = uv * 0.5 + 0.5;

                float depth = shadowCoord.z / shadowCoord.w; //w不为1时: 保证z在[-1, 1]范围内
#if defined(SHADER_TARGET_GLSL)
                depth = depth * 0.5 + 0.5;
#elif defined(UNITY_REVERSED_Z)
                depth = 1 - depth;
#endif

                float4 orignDepth = tex2D(_MyShadowMapTexture, uv);
                float sampleDepth = DecodeFloatRGBA(orignDepth);
                return (sampleDepth + _shadowBias) < depth ? _shadowStrength : 1; //shadowMap中z值更远就不在阴影中, 返回1
            

            fixed4 frag(v2f i) : SV_Target
            
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //主光源方向

                fixed4 c = tex2D(_MainTex, i.uv);
                fixed3 albedo = c.rgb * _Color.rgb;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; //环境光
                fixed lambert = max(0, dot(worldNormal, worldLightDir)); //漫反射计算公式
                fixed3 diffuse = _LightColor0.rgb * albedo * lambert;

                fixed atten = 1.0; //这边没处理衰减, 暂时固定1
                float shadow = 1.0;
                shadow = hardShadow(i.shadowCoord);
                
                return fixed4(ambient + diffuse * shadow * atten, 1.0);
            
            ENDCG
        
    

 

相关设置

 

 

相关链接

实时渲染-阴影渲染实现(Unity) - 知乎 (zhihu.com)

 

阴影进阶,实现更加的立体的阴影效果!

CSS 阴影的存在,让物体看上去更加有型立体。 然而,在最简单的阴影使用之上,我们可以实现更多有意思且更加立体的阴影效果。 本文将带大家看看如何使用 CSS 实现几类比普通阴影更加立体的阴影效果。 CSS 阴影基础 CSS 中,明面上可以实现阴影的有三个属性: box-shadow - 盒阴影 te

以上是关于阴影实现的主要内容,如果未能解决你的问题,请参考以下文章

CSS实现表格阴影

3D光照阴影 平面阴影矩阵推导及代码实现

3D光照阴影 平面阴影矩阵推导及代码实现

WebGL入门(四十二)-使用(FBO)实现阴影效果

WebGL入门(四十二)-使用(FBO)实现阴影效果

用GTK实现模糊阴影技术