URP | Depth 深度
目录

效果

目的
学习深度的原理以及计算过程。
了解观察空间的概念,ComputeScreenPos函数和Linear01Depth函数的使用。
粒子切边问题。
Eye Depth | 观察空间
观察空间看到的物体是什么样子的?
第一种方法
观察空间是物体相对于摄像机所在平面的距离,(摄像机的深度是 0 ,物体是w)因为是相对,所以Z是相反的。
我们前计算顶点距离摄像机数值。

第二种方法
在顶点着色器阶段,使用函数ComputeScreenPos裁剪空间转换成屏幕空间,
0代表观察空间位置,w代表顶点位置,
扩展
为什么输出是屏幕空间.w分量,不应该是z吗?
从观察空间到裁剪空间的矩阵是投影矩阵,但要注意的是,投影矩阵并没有实现真正的投影,而是将顶点变换到齐次裁剪坐标,为裁剪做准备,
经过投影矩阵变换后,
(透视相机)透视投影下的顶点齐次坐标屏幕空间.w值为**-position.z** 。深度值就储存到w里了。

计算过程参考大佬方法
3D数学-透视投影 - 知乎 (zhihu.com)
图形学基础 - 变换 - 投影 - 知乎 (zhihu.com)
输出顶点
效果和上面的一样 ,
因为目前数值很大所以曝光了。越距离摄像机越远数值越大。

这一步可以理解是获取顶点在屏幕位置的数值,那我们下一步获取深度值。
获取深度值
URP管线设置开启深度图
打开渲染管线确认是否开启深度(Depth)

ShaderGraphs 里获取深度节点

Shader中获取深度图
声明深度变量
顶点着色器阶段---输入 裁剪空间齐次坐标转换到屏幕空间的齐次坐标
注意:这里是输入的是裁剪空间位置坐标
片元着色器阶段—输出结果
效果

代码

问题
问题1 为什么裁剪空间转换成屏幕空间需要在除 scrPos.w 分量?
在顶点着色器阶段,我们已经使用函数ComputeScreenPos 把裁剪空间转换成屏幕空间了。
为什么到片元着色器阶段还要使用 屏幕空间.xy / 屏幕空间.w 分量。
从摄像机空间到裁剪空间的矩阵是投影矩阵,但要注意的是,投影矩阵并没有实现真正的投影,而是将顶点变换到齐次裁剪坐标,为裁剪做准备,
经过投影矩阵变换后,
(透视相机)透视投影下的顶点齐次坐标屏幕空间.w值为**-position.z** 。深度值就储存到w里了。
(正交相机) 正交投影的w值为1
我们上一节护盾篇介绍到 position.z就是物体的深度值。

裁剪空间下齐次坐标,x和y的取值范围是(-w,w) 然后 W 分量**(-z)**
屏幕空间下的齐次坐标,取值范围是(0-1),W分量 (-z)
最后通过透视除法之后xy的取值范围就是(0-1)了。
所以 :这个过程通过齐次除法(透视除法)转换成普通坐标
问题2 为什么还需要使用Linear01Depth函数对深度图处理。
采样得到的深度值,在透视投影的情况下不是线性的,因为经过透视投影和齐次除法后,Zndc和Zview的倒数是线性关系,我们采样得到了非线性的深度值.


判断物体是否和其它物体相交
实现这个效果我们就需要判断当前物体的深度值与深度图中对应的深度值是否在一定范围内,
如果是则判定为相交。
第1步我们在裁剪空间计算出物体的位置信息,
第2步获取场景深度数据,物体的像素深度。
第3步 在判断物体是否和场景中的其他物体相交。
特效中粒子和模型交叉时会出现切边。就可以使用这样的方法解决。
默认效果,带有切边

1. 第一种方法 使用裁剪空间计算深度
第一种方法,护盾使用的方法。
开始计算出物体在屏幕空间的位置信息。
计算空间深度转换成线性
这里使用函数Linear01Depth 转换到(0-1)
注意 :函数定义在Common.hlsl里,注意它和build in的同名函数的参数不同

在计算模型线性深度
最后计算出物体之间的深度效果
效果

参数

代码

2. 第二种 使用屏幕空间获取深度
使用函数ComputeScreenPos 裁剪空间信息转换到齐次空间下屏幕空间位置 。
顶点着色器阶段
在片元着色器阶段
输出结果

代码
ComputeScreenPos | 计算齐次空间下屏幕坐标位置
URP内置获取
扩展 ComputeScreenPos 屏幕齐次坐标
透视投影之后透视除法之前的坐标空间被称为裁剪空间,也叫齐次(裁剪)空间
看这个函数输入的是一个位置信息,该函数传入的是齐次空间下的坐标,
下面我们进行齐次坐标推导到屏幕坐标
顶点在变换到齐次坐标后,其x和y分量的范围在[-w, w]。
假设目前屏幕的宽度为width,高度为height,那么屏幕坐标的计算方法为:
Unity Shader
这样变换完的结果和我们上面计算的结果一样。
总结
ComputeScreenPos 函数是计算裁剪空间转换到屏幕空间

LinearEyeDepth和Linear01Depth
这是这俩个函数的对比。
Linear01Depth会返回相机空间中范围在(0,1]的深度,近平面为Near/Far,远平面为1。
LinearEyeDepth会返回相机空间中的深度,近平面为Near,远平面为Far。
总结
这俩个计算深度的方法不同,都是计算物体深度,第二种方法更简单。
扩展:实现水深度
使用上面2种制作一下水面深度的效果。
第一种,裁剪空间和像素深度
在片元计算器阶段 使用裁剪空间和屏幕位置计算顶点位置。
获取渲染深度
输入裁剪空间的像素深度
计算俩个物体的深度


第二种 自定义函数求深度
定义ComputeScreenPos函数 ,上面介绍过。
这个函数是把裁剪空间齐次坐标转换到屏幕空间的齐次坐标,映射到 (0,1)范围
定义一个函数获取全部深度,输出是float类型,所以输出单通道。
在定义一个计算俩个物体深度
注意 : ScreenPosition.xy / ScreenPosition.w 是在做透视除法,像素除深度
在片元着色器阶段输出

代码