深度和法线纹理

news2025/1/17 13:56:57

屏幕后期处理效果的基本原理就是当游戏画面渲染完毕后通过获取到该画面的信息进行额外的效果处理 之前的边缘检测、高斯模糊、Bloom、运动模糊等效果都是基于获取当前屏幕图像中的像素信息进行后期处理

如果仅仅根据像素信息来进行一些效果处理,存在以下问题:

  1. 效果欠佳:比如实现边缘检测时,边缘检测信息受物体纹理和光照等因素影响,无法更准确的检测边缘,会得到一些我们不需要的边缘点
  2.  无法实现:比如我们想要实现一些景深效果(虚化背景),我们无法通过像素的颜色信息来判断离摄像机的远近

因此可以通过屏幕空间的深度纹理和法线纹理进行优化。

  • 屏幕空间深度纹理:用于存储屏幕图像中每个像素深度信息的纹理,制作出 边缘检测、运动模糊、景深、环境遮挡 等等效果
  • 屏幕空间法线纹理:用于存储屏幕图像中每个像素法线信息的纹理,制作出 边缘检测、运动模糊、景深、环境遮挡 等等效果

1、深度和法线纹理的使用

在Shader当中直接声明对应变量

  • 深度纹理:sampler2D _CameraDepthTexture;
  • 深度+法线纹理:sampler2D _CameraDepthNormalsTexture;(一般RG通道存储法线,BA通道存深度)之后直接在Shader中使用这两个变量便可以获取到相关信息

Shader中获取深度值:

Shader中获取法线信息:

2、深度纹理中存储的是什么信息

Unity中的深度纹理中存储的信息,也就是Shader中使用 _CameraDepthTexture 或_CameraDepthNormalsTexture 采样的信息,是进行裁剪空间变换后的 z 分量再转换到0~1之后的结果,因为齐次裁剪空间坐标范围为 -1 ~ 1,而纹理中存储的信息范围是 0 ~ 1,因此Unity会将其利用以下公式进行转换:深度纹理值 = 0.5 * z + 0.5

也就是说我们通过深度纹理直接采样得到的深度纹理值是是进行裁剪空间变换后的 z 分量再转换到0~1之后的结果

3、法线纹理中存储的是什么信息

Unity中的法线纹理中存储的信息,也就是Shader中使用_CameraDepthNormalsTexture采样得到的float4中的部分信息,它是观察空间下的 法线 再转换到0~1之后的结果,因为观察空间下的单位向量的分量取值范围是 -1~1,而纹理中存储的信息范围是 0 ~ 1,因此Unity会将其利用以下公式进行转换:法线纹理值 =(观察空间下法线 + 1)* 0.5【公式跟深度纹理值纠正是一样的,只不过要对x,y,z 都改变】

也就是说我们通过法线纹理直接采样得到的法线纹理值是是观察空间下的 法线 再转换到0~1之后的结果

4、Unity 如何得到深度和法线纹理的

Unity中深度和法线纹理一般通过两种途径获取

  • 从G-buffer几何缓冲区中获取
  • 由一个专门的Pass渲染而来

具体Unity是通过哪种方式获取,取决于使用的渲染路径和设备的硬件限制。
当使用延迟渲染路径时,深度和法线纹理可以直接访问到,因为延迟渲染路径会把信息存储到
G-buffer几何缓冲区中(深度和法线等信息都存储在其中)。
而当无法直接获取到深度和法线纹理时(比如硬件不支持延迟渲染路径 或 使用的是前向渲染路
径时),Unity会通过一个单独的Pass来进行渲染,获取深度和法线信息。

需要注意的是,当使用单独的Pass渲染获得深度和法线纹理时,两者是有区别的

  • 对于深度纹理来说:

Unity内部会使用着色器替换技术选择渲染类型 RenderType =“Opaque” (不透明物体)
然后判断它们的渲染队列Queue是否小于等于2500(Background-1000、Geometry-2000、AlphaTest-2450)
如果满足这个条件,就会使用物体投射阴影时的Pass(LightMode 为 ShadowCaster 的Pass)
来得到深度纹理,如果没有这个Pass,那么该物体不会出现在深度纹理中!
因此这里的重点是,如果我们希望物体能够正确的出现在深度纹理中

  1. 必须在Shader中正确的设置RenderType标签
  2. 必须有投射阴影用的Pass(LightMode为ShadowCaster的Pass)
  • 对于法线纹理来说:

Unity底层会使用一个单独的Pass把整个场景再次渲染一次,从而得到深度和法线信息
这里为什么是深度和法线信息呢,因为当需要得到法线纹理时,Unity中是和深度一起获取的
( _CameraDepthNormalsTexture )
这个Pass包含在Unity内置的Shader中,我们可以在官方下载源文件解压后进行查看

5、深度和法线纹理使用时调用的函数原理

  • 深度

  • 法线 

SAMPLE_DEPTH_TEXTURE 宏:
它是用于从深度纹理中进行采样的宏,相比直接用tex2D进行采样,它在内部会帮助我们适配各
种不同的平台
,因为不同平台对深度纹理的采样规则会有所不同。它采样得到的深度值是裁剪空间下的z分量转换到0~1之间的结果

通过SAMPLE_DEPTH_TEXTURE得到的深度值是非线性的,所谓的非线性值的是指在透视摄像机的裁剪空间中深度值分部不均匀

  • 当深度值接近裁剪面近时,深度值变化迅速,精度高
  • 当深度值接远裁剪面近时,深度值变化缓慢,精度低

更直观的解释:一个相机在观察一个3D场景时,近处的物体移动一点,视觉上变化很大,所以需
要更高的精度来记录这种变化。而远处的物体移动同样的距离,视觉上的变化很小,因此可以使
用较低的精度来记录

因此为了让我们在Shader中利用深度值进行的计算更加准确,我们需要获得线性的深度值,只需要把裁剪空间下的深度值转换到观察空间下,便可以得到线性的深度值
Unity Shader中提供了内置函数LinearEyeDepth 和 Linear01Depth 都可以得到观察空间下的线性深度值

  • LinearEyeDepth:得到的是像素到摄像机的实际距离
  • Linear01Depth:得到的是实际距离被压缩到0~1之间的值

DecodeDepthNormal函数内部其实也是执行的DecodeFloatRG和DecodeViewNormalStereo函数,它的作用就是得到观察空间下的对应像素的 法线 和 线性 深度值(0~1)
可以一次性的获得两个信息,也可以选择分别调用DecodeFloatRG和DecodeViewNormalStereo
单独获取深度和法线信息

函数中具体做的事情,就是利用法线的xy算出z,得到最终的法线信息;将裁剪空间下的非线性深度值 转换为观察空间下线性的范围为0~1的深度值

总结:直接采样出来的深度和法线信息是不会直接使用的,我们需要将他们通过内置函数进行转换
得到最终我们会使用的观察空间下的深度和法线信息

6、获取深度纹理

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

public class DepthTexture : PostEffectBase
{
    // Start is called before the first frame update
    void Start()
    {
        Camera.main.depthTextureMode = DepthTextureMode.Depth;   
    }
}
Shader "ShaderProj/13/DepthTexture"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _CameraDepthTexture;

            v2f vert (appdata_base v)
            {
               v2f o;
               o.vertex = UnityObjectToClipPos(v.vertex);
               o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

               return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
                fixed linearDepth = Linear01Depth(depth);
                return fixed4(linearDepth, linearDepth, linearDepth, 1);
            }
            ENDCG
        }
    }
    Fallback Off
}

7、获取法线纹理

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

public class DepthNormalsTexture : PostEffectBase
{
    // Start is called before the first frame update
    void Start() 
    {
        Camera.main.depthTextureMode = DepthTextureMode.DepthNormals;
    }
}
Shader "ShaderProj/13/DepthNormalTexture"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _CameraDepthNormalsTexture;

            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float4 depthNormal = tex2D(_CameraDepthNormalsTexture, i.uv);
                fixed depth;
                fixed3 normals;
                DecodeDepthNormal(depthNormal, depth, normals);

                return fixed4(normals * 0.5 + 0.5, 1);
            }
            ENDCG
        }
    }
    Fallback Off
}

 因为是按照观察空间来计算的,所以法线展示出来的颜色按照摄像机的【Local】坐标(x轴:右边,红色;y轴:上边,绿色;z 轴:朝向反向向,蓝色)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2256393.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Oracle之表空间迁移

问题背景:一个数据表随着时间的累积,导致所在表空间占用很高,里面历史数据可以清除,保留近2个月数据即可 首先通过delete删除了2个月以前的数据。 按网上的教程进行空间压缩,以下sql在表所在用户执行: -- 允许表重新…

非父子通信(扩展)-- event bus 事件总线

创建一个空实例Bus, export default 导出Bus 过程:由A组件对Bus组件进行监听,B组件触发Bus对应的事件,由于A组件进行监听,触发事件之后就会进行A组件的回调,那么就可以将消息发送给A了 在src文件夹下新建utils文件夹&a…

vue深入理解(1)

本文章内容主要来源于《vue.js设计与实现》 视图层框架设计 命令式和声明式 范式上,视图层框架通常分为命令式和范式 JQuery就是典型的命令式框架,命令式框架的一大特点就是关注过程 例子: $(#app) // 获取app.text(hello world) // 设置…

CSDN博客如何修改删除上传的资源

CSDN博客是我用过的最好用的博客,它对用户发布文章的限制比较少,而且还支持用户利用知识创新来获取收益,不象51CTO这种垃圾博客,动不动就给扣分限号。但我发现CSDN也有设计缺陷,虽然其上传资源的入口很好找&#xff0c…

【SpringBoot】Day11-10 yml文件配置

三种配置文件 前面我们一直使用springboot项目创建完毕后自带的application.properties进行属性的配置,那其实呢,在springboot项目当中是支持多种配置方式的,除了支持properties配置文件以外,还支持另外一种类型的配置文件&#x…

React路由使用入门react-router-dom

1.安装react-router-dom npm i react-router-dom 2.配置 (1)创建router实例对象并且配置路由对应关系 (2)路由绑定 import {createBrowserRouter,RouterProvider} from react-router-dom//(1)创建rou…

web复习(二)

编程题 1.编写一个函数&#xff0c;接收一个数组作为参数&#xff0c;返回一个对象&#xff0c;其中包含数组中每个元素及其出现次数。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewpo…

【CANoe示例分析】Basic UDP Multicast(CAPL)

1、工程路径 C:\Users\Public\Documents\Vector\CANoe\Sample Configurations 16.6.2\Ethernet\Simulation\UDPBasicCAPLMulticast 在CANoe软件上也可以打开此工程:File|Sample Configurations|Ethernet - Simulation of Ethernet ECUs|Basic UDP Multicast(CAPL) 2、示例目…

在Linux(ubuntu22.04)搭建rust开发环境

1.安装rust 1.安装curl: sudo apt install curl 2.安装rust最新版 curl --proto ‘https’ --tlsv1.2 https://sh.rustup.rs -sSf | sh 安装完成后出现&#xff1a;Rust is installed now. Great! 重启当前shell即可 3.检验是否安装成功 rustc --version 结果出现&…

react跳转传参的方法

传参 首先下载命令行 npm react-router-dom 然后引入此代码 前面跳转的是页面 后面传的是你需要传的参数接参 引入此方法 useLocation()&#xff1a;这是 react-router-dom 提供的一个钩子&#xff0c;用于获取当前路由的位置对象location.state&#xff1a;这是从其他页面传…

路径规划之启发式算法之九:灰狼优化算法(Grey Wolf Optimizer,GWO)

灰狼优化算法&#xff08;Grey Wolf Optimizer&#xff0c;GWO&#xff09;是一种智能优化算法&#xff0c;由澳大利亚格里菲斯大学学者Mirjalili等人在2014年提出。该算法灵感来源于灰狼群体的捕食行为&#xff0c;通过模拟灰狼的社会等级分层和狩猎机制来解决复杂的优化问题。…

数字乡村建设方案-6

1. 方案背景与目标 数字乡村建设旨在响应乡村振兴战略&#xff0c;解决顶层设计缺失、资源统筹不足、基础设施缺失等问题&#xff0c;通过信息化建设加强党的领导&#xff0c;提升乡村治理水平&#xff0c;促进乡村经济发展。 2. 乡村信息化需求 乡村管理人员希望通过信息化…

数据分析: 基于CSDN博客排行榜TOP100的博客创作分析和建议

在CSDN上写一些学习心得&#xff0c;分享一些经验&#xff0c;是一件令人愉悦的事情。但是绝大多数人&#xff0c;很多时候写的东西没人看&#xff0c;浏览量个位数&#xff0c;点赞收藏都是0&#xff0c;这着实让人觉得沮丧和无聊&#xff0c;最终选择放弃。 今天&#xff0c…

快速幂+逆元求组合数

在计算组合数 时&#xff0c;直接暴力计算既慢又容易溢出。今天我们来揭开 快速幂 和 模逆元 的神秘面纱&#xff0c;带你一边学习理论&#xff0c;一边轻松解决实际问题&#xff01; 什么是快速幂&#xff1f; 快速幂是一种高效计算 的方法。它利用指数的二进制表示&#x…

「OC」多线程(三)——NSOperation

「OC」多线程(三)——NSOperation 文章目录 「OC」多线程(三)——NSOperation前言介绍实现的具体步骤 NSOperation的创建NSOperationQueue的使用使用实例NSInvocationOperation的使用NSBlockOperation的使用NSOperationQueue的使用取消操作最大并发数 自定义NSOperation子类相关…

可供参考的GitHub国内镜像

在配置了本地hosts文件和魔法后仍存在无法访问的问题 针对如上问题&#xff0c;可以使用国内的镜像地址做替换 例如: https://github.com/bubbliiiing/detr-pytorch改成 https://hub.nuaa.cf/bubbliiiing/detr-pytorch推荐使用的镜像 https://hub.yzuu.cf/ https://hub.nua…

Codeforces Round 784 (Div. 4)

题目链接 A. Division? 题意 思路 模拟即可 示例代码 void solve() {int n;cin >> n;int ans;if(n > 1900) ans 1;else if(n > 1600) ans 2;else if(n > 1400) ans 3;else ans 4;cout << "Division " << ans << \n;}B. T…

E172 ASP.NET+SQL+C#+LW+图书管理系统的设计与实现 配置 源码 文档 全套资料

图书管理系统 1.项目摘要2. 系统的概述3.项目功能4.界面展示5.源码获取 1.项目摘要 摘 要 书籍是供人们获取并增长知识的主要途径&#xff0c;由于图书的种类较多&#xff0c;阅读者也较多&#xff0c;借阅量较大&#xff0c;且易出错&#xff0c;传统的图书借阅若还停留在手工…

TriCore架构-TC397将code从原来在P-Cache地址移到PSPR的地址,CPU的负载率为什么没影响

TC397有6个内核,每个核有自己的私有的Memory以及共有的Memory。 私有的:PSPR,DSPR,P-Cache,D-Cache,PF(X),LMU,DLMU,LPB PSPR主要用来运行RAM Code,比如说有些代码要放到RAM里面运行。 DSPR主要当成SRAM来用,比如用来存放全局变量。 P-Cache通过PFI接口访问DMU的3M内…

109.【C语言】数据结构之二叉树层序遍历

目录 1.知识回顾 2.代码实现 准备工作 LevelOrder函数 代码框架 关键代码 3.执行结果 1.知识回顾 层序遍历参见106.【C语言】数据结构之二叉树的三种递归遍历方式文章 截取的部分内容 定义:按层的方式遍历(,设n为树的深度,h1-->h2-->h3-->...-->hn) 以下面…