unity shaderlab 深度图的实现过程 代码+详细注释

  • Post author:
  • Post category:其他




一 简介



1.1 啥是深度图

深度图通过获取观察视角中,物体由近到远的深度信息,来实现与其相关的特殊效果。

深度值是在像素信息中保存的[0,1]范围的非线性值,这些深度值来自裁剪坐标。Unity会自动利用Shader Replacement将RenderType为Opaque、渲染队列小于等于2500并且有ShadowCaster Pass的物体的深度值渲染到深度图中。



1.2 深度图可以实现的效果
  1. 垂直雾效
  2. 能量护盾
  3. 扫描线
  4. 卡通描边
  5. 运动模糊
  6. 等等



二 深度图的简单实现



2.1 获取像素深度信息

向前渲染和延迟渲染中获取深度信息的方式各有不同。



2.1.1【延迟渲染获取深度信息】

由于延迟渲染是先经过深度测试再处理灯光,所以像素缓冲区中已经有了深度信息。除了延迟渲染的相关设置外,无需任何多余设置即可直接在shader中使用



2.1.2【正向渲染获取深度信息】

在向前渲染中,必须先通过以下步骤用摄像机获取图像的像素与深度信息,才能在shader中使用

【步骤】新建c#脚本,将以下代码放置到脚本中,并将脚本赋给摄像机。

相机渲染图像后会调用脚本中的OnRenderImage方法。通过调用Graphics.Blit方法将已经渲染的RenderTexture source用指定或默认的shader pass处理并通过RenderTexture destination输出

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

//需要Camera组件
[RequireComponent(typeof (Camera))]
public class cameraDepth : MonoBehaviour
{
    public Material cameraMaterial;  
    void Start () {  
        Camera camera = gameObject.GetComponent<Camera> ();
        //设置Camera的depthTextureMode,生成深度图
        camera.depthTextureMode = DepthTextureMode.Depth;  
    }  

    //OnRenderImage在所有的渲染完成后调用
    //该函数允许我们处理渲染后的图像,输入原图像source,输出的图像desitination
    void OnRenderImage (RenderTexture source, RenderTexture destination){  
        //如果指定了cameraMaterial  就用cameraMaterial处理输出的图像,否则就用输入的原图像用作输出
        if (cameraMaterial != null)
        {
            //该方法用于将输入的图像指定material和shader pass 后输出
            Graphics.Blit(source, destination, cameraMaterial);
        }
        else
        {
            Graphics.Blit(source, destination);
        }
    } 
}



2.2 Shader中使用深度信息

新建Shader脚本,将以下代码放置到脚本中。新建Material并指定好刚刚新建的Shader,赋给物体


Shader "Custom/Depth" {  
    SubShader {  
        Tags { "RenderType"="Opaque" }  
          
        Pass{  
            CGPROGRAM  
            // 定义顶点、片片渲染器,并引入shader相关的宏
            #pragma vertex vert  
            #pragma fragment frag  
            #include "UnityCG.cginc"  
            // 声明贴图变量,用于接受摄像机的贴图
            sampler2D _CameraDepthTexture;  
            // 片元渲染器的输入结构 
            struct v2f {  
                // 像素的坐标
                float4 pos : SV_POSITION;  
                float3 uv:TEXCOORD0;
                // 屏幕坐标
                float4 screenPos:TEXCOORD1;  
            };  
              
            //顶点渲染器
            v2f vert (appdata_base v){  
               v2f o;  
               // 将顶点坐标模型空间转为裁剪空间
               o.pos = UnityObjectToClipPos (v.vertex);  
               // 使用ComputeScreenPos计算出屏幕坐标
               // 输入裁剪空间的位置,获取齐次空间的屏幕坐标。(不是最终输出的屏幕坐标)
               o.screenPos = ComputeScreenPos(o.pos);  
               o.uv = v.texcoord;
               return o;  
            }  
              
            //片元渲染器
            half4 frag (v2f i) : COLOR{  
                // 计算出投影纹理坐标
                half4 proj =UNITY_PROJ_COORD(i.screenPos);
                //tex2D和tex2Dproj的区别在于:tex2Dproj会先将第二个参数除以它的最后一个分量。 
                //要想进行深度值对比,只有先存储深度值。在生成ShadowMap时存储深度值,然后无论用tex2D还是tex2Dproj都可以得到深度值。
                half4 _tex2Dproj = tex2Dproj(_CameraDepthTexture, proj);
                //LinearEyeDepth负责把深度纹理的采样结果转换到视角空间下的深度值
                //Linear01Depth则返回一个范围在[0,1]的线性深度值
                half depthValue = Linear01Depth(_tex2Dproj.r);  

                // float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
	            // float depthValue = Linear01Depth(depth);

                return half4(depthValue, depthValue, depthValue, 1);   
            }  
            ENDCG  
        }  
    }  
    // 这个必须要有。失败的时候尝试调用Diffuse subshader
    FallBack "Diffuse" 
}  

就完成了

在这里插入图片描述



版权声明:本文为lengyoumo原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。