第十三章 使用深度和法线纹理

server/2024/9/24 13:00:14/

获取深度和法线纹理

背后的原理

深度纹理是一张渲染纹理,它里面存储的像素值不是颜色,而是一个高精度的深度值。深度值范围是[0, 1],非线性分布的。这些深度值来自于顶点变换后得到的归一化的设备坐标(NDC)。一个模型想要被绘制在屏幕上,需要把它的顶点从模型空间变换到齐次裁剪坐标系下,这是通过顶点着色器中乘以MVP变换矩阵得到的。在变换的最后一步,我们需要用一个投影矩阵来变换顶点,当我们使用的是透视投影类型的摄像机时,这个投影矩阵就是非线性的。

透视投影对顶点的变换过程。左侧显示投影变换前,观察空间下视锥体的结构及相应顶点位置。中间显示应用裁剪矩阵之后的变换结果,顶点着色器阶段输出的顶点变换结果。右侧是底层硬件进行了透视除法后得到的归一化的设备坐标。

使用正交摄像机时投影变换过程。同样会得到一个范围为[-1, 1]的立方体,正交投影使用的变换矩阵是线性的。

得到NDC之后,深度值就对应了NDC中顶点坐标的z分量的值。由于NDC中z分量值范围-1~1,为了存储在一张图像中,我们需要对其映射。

d=0.5\cdot z_{ndc}+0.5

d对应了深度纹理中的像素值,z_{ndc}对应了z分量的值。

在Unity中,深度纹理可以直接来自于真正的深度缓存,也可以是由一个单独的Pass渲染而得。Unity会使用着色器替换技术选择那些渲染类型(RenderType)标签为Opaque的物体,判断它们使用的渲染队列是否小于等于2500,如果满足条件,就把它渲染到深度和法线纹理中。

如何获取

只需要告诉Unity并在Shader中直接访问特定纹理属性。与Unity沟通过程是在脚本设置摄像机的depthTextureMode完成的,我们可以通过如下代码获取深度纹理值

camera.depthTextureMode = DepthTextureMode.Depth;

设置好上面摄像机后,就可以在Shader中通过声明_CameraDepthTexture变量来访问它。

如果想要获取深度+法线纹理,只需要在代码中这样设置

camera.depthTextureMode = DepthTextureMode.DepthNormals;

在Shader中通过声明_CameraDepthNormalTexture变量来访问它。

在Unity中,我们还可以在摄像机的Camera组件上看到当前摄像机是否需要渲染深度或深度+法线纹理。当在Shader中访问到深度纹理_CameraDepthTexture后,我们就可以使用当前像素的纹理坐标对它采样,Unity为我们提供一个统一的宏SAMPLE_DEPTH_TEXTURE来处理这些平台差异造成的问题。

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

i.uv是一个float2类型的变量,对应了当前像素的纹理坐标。SAMPLE_DEPTH_TEXTURE接受两个参数-深度纹理和一个float3或float4类型的纹理坐标。类似的宏还有SAMPLE_DEPTH_TEXTURE_PROJ、SAMPLE_DEPTH_TEXTURE_LOD。SAMPLE_DEPTH_TEXTURE_PROJ的内部使用了tex2Dproj这样的函数进行投影纹理采样,纹理坐标的前两个分量首先会除以最后一个分量,再进行纹理采样。如果提供了第四个分量,还会进行一次比较,通常用于阴影的实现中。SAMPLE_DEPTH_TEXTURE_PROJ的第二个参数通常是由顶点着色器输出插值而得的屏幕坐标。

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

其中,i.scrPos是在顶点着色器中通过调用ComputeScreenPos(o.pos)得到的屏幕坐标。

当通过纹理采样得到深度值后,这些深度值往往是非线性的,这种非线性来自透视投影使用的裁剪矩阵。但是,我们需要把投影后的深度值变换到线性空间下。我们只需要倒推顶点变换的过程即可。

Unity提供了两个辅助函数来为我们进行上述计算过程:LinearEyeDepth和Linear01Depth。LinearEyeDepth负责把深度纹理的采样结果转换到视角空间下的深度值,也就是z_{view}。Linear01Depth会返回一个范围在[0, 1]的线性深度值,也就是z_{01}。这两个函数使用了内置的_ZBufferParams变量来得到远近裁剪平面的距离。如果我们需要获取深度+法线纹理,可以直接使用tex2D函数对_CameraDepthNormalsTexture进行采样,得到里面存储的深度和法线信息。Unity提供了辅助函数来为我们对这个采样结果进行解码,从而得到深度值和法线方向。这个函数是DecodeDepthNormal,它的第一个参数是对深度+法线纹理的采样结果。这个采样结果是Unity对深度和法线信息编码后的结果,它的xy分量存储的是视角空间下的法线信息,而深度信息被编码进了zw分量。通过调用DecodeDepthNormal函数对采样结果解码后,我们就可以得到解码后的深度值和法线。这个深度值是范围在[0, 1]的线性深度值,而得到的法线则是视角空间下的法线方向。我们也可以用DecodeFloatRG和DecodeViewNormalStereo来解码深度+法线纹理中的深度和法线信息。

查看深度和法线纹理


http://www.ppmy.cn/server/9768.html

相关文章

windows 11 打包python镜像相关操作

第一步:Docker下载 首先先到Docker官网下载最新官方Docker for Windows链接,点击下载之后进行安装,安装好之后在cmd执行 wsl --update。 第二步:在电脑上打开“控制面板”->“程序”-> “启动或关闭Windows功能”。 有的…

常见面试算法题-数组二叉数

■ 题目描述 【数组二叉树】 二叉树也可以用数组来存储,给定一个数组,树的根节点的值存储在下标1,对于存储在下标N的节点,它的左子节点和右子节点分别存储在下标2*N和2*N1,并且我们用值-1代表一个节点为空。 给定一…

【RT-Thread应用笔记】FRDM-MCXN947上的RW007实践——WiFi延迟和带宽测试

【RT-Thread应用笔记】FRDM-MCXN947上的RW007实践——WiFi延迟和带宽测试 一、背景介绍1.1 RW007模组简介1.2 Arduino接口简介1.3 RW007软件包简介1.4 RT-Thread env工具简介 二、创建工程2.1 新建工程2.2 添加rw007软件包2.3 打开RW007配置项2.4 启用pin驱动2.5 禁用rw007的ST…

python-基础(5)-字典

python专栏地址 上一篇: python-基础(4)-list 字典 本节主要介绍 字典初始字典的操作 一、字典简单了解 所谓字典,就像我们常用的字典一样,输入一个页码,就到达一页。它有两种方式创建: 直接写name{}namedict() gufeng {&quo…

java学习笔记4

7. 二维数组 7.1 定义 二维数组及就是数组的嵌套,数组的元素还是数组; 二维数组是一个 元素为一维数组 的一维数组; 语法上Java支持多维数组,但从内存分配原理的角度看,Java中只有一维数组,没有多维数组; 7.2 二维数组的初始化和赋值 7.2.1 长度已知 数据类型[][] 数…

CSS 命名规范 - BEM

CSS 命名规范 - BEM 规范化命名 CSS 的选择器按照规范命名的优点: 提高代码的 可读性 和 可维护性提高 可重用性可以有效地避免组件或模块间样式的相互污染,减少嵌套层级 BEM 格式 [prefix]-[block]__[element]--[modifier]Prefix。全局前缀&#x…

如何利用R语言获取worldclim30s的气候数据

要使用R语言获取WorldClim30s的气候数据,你可以使用raster包来处理栅格数据,而且getData()函数可以方便地从WorldClim网站下载数据。以下是一个获取WorldClim30s数据的基本步骤: 安装和加载必要的包: 在R中,首先要确保…

Vue2+ElementUI的el-table实现新增数据行与删除的功能

Vue2ElementUI的el-table实现新增数据行与删除的功能 文章目录 Vue2ElementUI的el-table实现新增数据行与删除的功能1. 代码2. 效果 1. 代码 TableIndex.vue如下 <template><div><div><el-button click"add" class"filter-item" pl…