Shader学习的基础知识(二十四)深度纹理和法线纹理

原理

Unity里取深度和法线很简单,但是我们还是要知道一下它的原理。
深度纹理记录的是不是像素值,而是深度值0~1之间,而且是非线性分布的。深度值来自于变换后得到的归一化的设备坐标。如果一个模型要想显示在屏幕上,要把它的顶点从模型空间变到齐次空间下,当我们使用透视摄像机时这个投影就是非线性的。

深度纹理可以直接来自于真正的深度缓存,也可以由一个单独的Pass渲染得到。unity会把标签RenderType为Opaque的物体且它使用队列小于2500的,就把它的深度和法线渲染出来,存在深度纹理和法线纹理中。我们可以让一个摄像机生成一张深度纹理或是一张深度+法线纹理。

着色器替换技术,选取需要的不透明的使用它投射使用的Pass(即LightMode为ShadowCaster得来到深度纹理。如果Shader中不包含这种Pass,则不会有深度纹理)

深度纹理通常有24或16位,如果深度+法线纹理则为32位,法线信息会写进R和G通道,深度则是B和A通道。

如何获取

通过以下代码即可获取:

camera.depthTextureMode=DepthTextureMode.Depth;

一旦设置好上面的摄像机模式后,我们就可以在Shader中通过CmaeraDepthNormalsTexture来访问了

camera.depthTextureMode |= DepthTextureMode.Depth;
camera.depthTextureMode |= DepthTextureMode.DepthNormals;

访问_CameraDepthTexture后就可以对纹理坐标进行采样。绝大多数情况下使用tex2D即可。
SAMPLE_DEPTH_TEXTURE用来处理平台差异问题。

float d = SAMLPLET_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);

i.uv是一个float2对应了当前像素的纹理坐标。类似的还有
SAMPEL_DEPTH_TEXTURE_PROJ:接受两个参数深度纹理和一个float3和float4类型的纹理坐标,内部使用了tex2Dproj进行投影纹理采样,通常用于阴影的实现。
SAMPLE_DEPTH_TEXTURE_LOD:第二个参数通常是由顶点着色器输出插值而得的屏幕坐标,例如:

float d = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture,UNITY_PROJ_COORD(i.scrPos));

i.scrPos就顶点着色器中能过调用ComputeScreenPos(o.pos)得到的屏幕坐标。

常用的辅助函数

LinearEyeDepth:负责把深度纹理的采样转到视角空间下的深度值。
Linear01Depth:返回一个0~1的线性深度值 。
如果需要得到深度+法线纹理,可以直接使用tex2D对_CameraDepthNormalsTexture进行采样。
DecodeDepthNormal:的第一个参数是对深度+法线采样的结果0~1;
也可以通过DecodeFloatRG和DecodeViewNormalStereo来解码深度+法线纹理中的深度法线信息。

查看深度和法线纹理

可以用帧调试器Freame Debugger来查看,如果发现几乎全白的可以把远截面调小即可。
一般显示出线性空间下的信息或解码后的法线方向 会更加有用。
我们可以利用下面代码输出线性深度值:

float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
float linearDepth=Linear01Depth(depth);
return fixed4(linearDepth,linearDepth,linearDepth,1.0);

或是输出法线方向:

fixed3 normal=DecodeViewNormalStereo(tex2D(_CameraDepthNormalsTexture,i.uv).xy);
return fixed4(normal*0.5+0.5,1);

基础说到这,后面开始讲实例。

摄像机部分代码:

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

[ExecuteInEditMode]   // 可以在编辑界面看到深度绘制效果
public class MainCameraDepth : MonoBehaviour
{

    public Material mat;
    public int width = 512;
    public int height = 512;

    private Camera cam;
    private RenderTexture rt;
    private int image_id = 0;

    void Start()
    {
        cam = GetComponent<Camera>();   //获取当前绑定到脚本的相机

        cam.depthTextureMode = DepthTextureMode.Depth;
    }

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, destination, mat);
    }

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

    }
}

Shader部分代码:

Shader "Custom/DepthGrayscale" {
	SubShader{
	Tags { "RenderType" = "Opaque" }

	Pass{
	CGPROGRAM
	#pragma vertex vert
	#pragma fragment frag
	#include "UnityCG.cginc"

	sampler2D _CameraDepthTexture;

	struct v2f {
	   float4 pos : SV_POSITION;
	   float4 scrPos:TEXCOORD1;
	};

	//Vertex Shader
	v2f vert(appdata_base v) {
	   v2f o;
	   o.pos = UnityObjectToClipPos(v.vertex);
	   o.scrPos = ComputeScreenPos(o.pos);
	   return o;
	}

	//Fragment Shader
	half4 frag(v2f i) : COLOR{
	   float depthValue = Linear01Depth(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r);
	   half4 depth;

	   depth.r = depthValue;
	   depth.g = depthValue;
	   depth.b = depthValue;

	   depth.a = 1;
	   return depth;
	}
	ENDCG
	}
	}
		FallBack "Diffuse"
}

如果没有过度色可能是这个值太大的缘故
在这里插入图片描述

在这里插入图片描述

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小盖子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值