在unity里,将场景的深度图输出到game窗口

Posted qq_48087740

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在unity里,将场景的深度图输出到game窗口相关的知识,希望对你有一定的参考价值。

记录一个遇到的需求x

场景:

美术同学在unity里搭好了场景,想要做pv,需要给其中的一个场景做一些ae的动效,因此需要这个场景的深度图

因为美术同学搭场景的时候就已经是在unity里面了,也用到了urp的渲染,不好再导回建模软件,所以只能直接在unity里操作
其实如blender等软件是可以快捷得到并且导出深度图的,最好在unity搭场景前先做好


解决方案:

查了很多资料,好像是不能作为图片输出出来?也可能是我没查到x
总之这里记录一种方法
(建议先给项目存档)

第一步,更改项目的管线设置

在 Project Settings 中找到 Graphics

双击这个Universal Render Pipeline Asset就可以编辑它
把这个Depth Texture勾选上

把相机的Rendering中的Depth Texture这个属性设置为on

也可以新建一个C#脚本,挂在想要用的camera上面。这种方式可以为相机指定更多的depthTextureMode,比如DepthNormal等

using UnityEngine;

public class depthCamera : MonoBehaviour

    // Start is called before the first frame update
    void Start()
    
        GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth;   
    

到此相机的设置就完成了。

第二步,shader相关的设置

我们新建一个unlitShader
通过_CameraDepthTexture来获取到深度图
在Pass中添加

TEXTURE2D_X_FLOAT(_CameraDepthTexture);

然后就可以在frag函数中直接使用深度图的信息了

完整的shader代码如下

Shader "Custom/DepthTest"

    Properties
    
        _Color("与主贴图正片叠底的颜色", Color) = (1,1,1,1)
        _MainTex("主贴图", 2D) = "white" 
        _DepthFactor("深度参数",Range(0,100)) = 1
    
    SubShader
    
        Tags
        
            "RenderType" = "Opaque" "Queue" = "Geometry+100"
        
        Pass
        
            Tags
            
                "LightMode" = "UniversalForward"
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct appdata
            
                float4 vertex : POSITION;
                float4 uv : TEXCOORD0;
            ;

            struct v2f
            
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 scrPos : TEXCOORD3;
            ;


            CBUFFER_START(UnityPerMaterial)
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            float _DepthFactor;
            CBUFFER_END
            TEXTURE2D_X_FLOAT(_CameraDepthTexture);
            SAMPLER(sampler_CameraDepthTexture);

            v2f vert(appdata v)
            
                v2f o;
                VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
                o.pos = vertexInput.positionCS;

                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.scrPos = ComputeScreenPos(vertexInput.positionCS);

                return o;
            

            float4 frag(v2f i) : SV_Target
            
                float4 sampleTex = tex2D(_MainTex, i.uv);
                half2 screenPos = i.scrPos.xy / i.scrPos.w;
                //深度
                float depth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, screenPos).r;
                //float depth = tex2D(_CameraDepthTexture,i.scrPos.xy).r;
                float depthValue = Linear01Depth(depth, _ZBufferParams);
                float3 finalColor = float3(depthValue, depthValue, depthValue);
                return float4(finalColor, 1);
            
            ENDHLSL
        
    

第三步,gameObject的材质

回到unity中,创建两个material,分别起名为DepthTest和DepthTestTran,这是为了使生成的深度图具有底色
为它们指定使用的shader


都用刚刚写的那个DepthTest

再创建一个Quad作为底色,把它放在摄像机的前面紧贴着摄像机,使它能够完全挡住视野。这个Quad的材质为DepthTestTran


将其他的所有想要加入深度图显示的物体的材质设置为DepthTest


然后就可以在game窗口里看到深度图了。


可能遇到的问题

要注意的是,如果此时屏幕是一片全白或者全黑的话,很有可能是相机的取景距离出了问题。这个深度本身是一个数字,代表了像素点距离相机的远近,它的大小与相机取景距离范围是有关的。


如果在室内场景,最远距离应该适当缩小,否则就可能导致物体距离相机太近(在比例上),导致画面全黑。远景也同理

还有就是,如果项目的灯光有颜色的话,最好也关掉。这个灯光也会影响到在其他软件里读取和使用深度图。

因为不知道怎么把深度图导出出来,所以我是直接截图了x

Unity Shader - 深度图基础及应用

参考技术A

深度图里存放了 [0,1] 范围的 非线性分布 的深度值,这些深度值来自 NDC 坐标。
在延迟渲染中,深度值默认已经渲染到G-buffer;而在前向渲染中,你需要去 申请 ,以便Unity在背后利用Shader Replacement将RenderType为Opaque、渲染队列小于等于 2500 并且有 ShadowCaster Pass 的物体的深度值渲染到深度图中。

第一步:在C#中设置Camera.main.depthTextureMode = DepthTextureMode.Depth;

可以在主摄像机的Camera组件下看见提示:

这表明了主摄像机渲染了深度图

第二步:在Shader中声明_CameraDepthTexture

第三步:访问深度图

利用覆盖屏幕的uv值和深度图中的深度,我们可以重建出物体在世界空间中的坐标。
主要有以下两种方法:

首先要在C#脚本中传递当前的VP逆矩阵:

然后在Shader中首先制造NDC坐标:

利用当前的VP逆矩阵将NDC坐标转换到世界空间:

具体用法可以到下面的MotionBlur例子中查看。

首先需要知道,Post Process实际上是渲染一个覆盖屏幕的Quad,因此屏幕四个角对应摄像机的视椎体四个角。
首先是算出摄像机到四个角的向量:

假设有个绿点在toTopLeft所在线上,利用相似三角形,可以得到:

而depth是能够在Shader中获得的,因此我们只需要传递toTopLeft / near到Shader中就能计算出toGreen:

在Vertex中判断出对应顶点所在的向量:

可以看到uv值和对应的索引值正好是二进制的关系,所以可以如下求出:

你可能奇怪这样只能求到4个角线上的点,但vertex到fragment的过程中是有个东西叫插值的,这个 插值 正好能把每个像素所在的向量求出。
然后我们就能在fragment中求出世界坐标了:

具体的用法可以到下面的垂直雾效例子中找到。

输出[0,1]范围的深度值即可,如下:

思路是判断当前物体的深度值与深度图中对应的深度值是否在一定范围内,如果是则判定为相交。
首先访问当前物体的深度值:

然后访问深度图。由于此时不是Post Process,因此需要利用投影纹理采样来访问深度图:

最后就是进行相交判断:

在相交高亮效果的基础上,加上 半透明 边缘高亮 ,就能制造出一个简单的能量场效果:

思路是让雾的浓度随着深度值的增大而增大,然后进行的原图颜色和雾颜色的插值:

思路与相交高亮效果类似,只是这里是Post Process。自定义一个[0,1]变化的值_CurValue,根据_CurValue与深度值的差进行颜色的插值:

利用上面提到的第二种重建世界空间坐标的方法得到世界空间坐标,判断该坐标的Y值是否在给定阈值下,如果是则混合原图颜色和水的颜色:

利用上面提到的第二种重建世界空间坐标的方法得到世界空间坐标,让雾的浓度随着Y值变化:

思路是取当前像素的附近4个角,分别计算出两个对角的深度值差异,将这两个差异值相乘就得到我们判断边缘的值。
首先是得到4个角:

然后是得到这4个角的深度值:

最后就是根据对角差异来得到判断边缘的值:

运动模糊主要用在竞速类游戏中用来体现出速度感。这里介绍的运动模糊只能用于 周围物体不动,摄像机动 的情景。
思路是利用上面提到的重建世界坐标方法得到世界坐标,由于该世界坐标在摄像机运动过程中都是不动的,因此可以将该世界空间坐标分别转到摄像机运动前和运动后的坐标系中,从而得到两个NDC坐标,利用这两个NDC坐标就能得到该像素运动的轨迹,在该轨迹上多次取样进行模糊即可。
首先是得到世界坐标(这里使用提到的第一种重建方法):

然后是计算出运算前后的NDC坐标:

最后就是在轨迹上多次取样进行模糊:

景深是一种聚焦处清晰,其他地方模糊的效果,在摄影中很常见。
思路是首先渲染一张模糊的图,然后在深度图中找到聚焦点对应的深度,该深度附近用原图,其他地方渐变至模糊图。
第一步是使用SimpleBlur Shader渲染模糊的图,这里我只是简单地采样当前像素附近的9个点然后平均,你可以选择更好的模糊方式:

第二步就是传递该模糊的图给DepthOfField Shader:

第三步就是在DepthOfField Shader中根据焦点来混合原图颜色和模糊图颜色:

https://github.com/KaimaChen/Unity-Shader-Demo/tree/master/UnityShaderProject

Unity Docs - Camera’s Depth Texture
Unity Docs - Platform-specific rendering differences
神奇的深度图:复杂的效果,不复杂的原理
SPECIAL EFFECTS WITH DEPTH
GPU Gems - Chapter 27. Motion Blur as a Post-Processing Effect
《Unity Shader 入门精要》
《Unity 3D ShaderLab 开发实战详解》

以上是关于在unity里,将场景的深度图输出到game窗口的主要内容,如果未能解决你的问题,请参考以下文章

Unity2D:修改背景图的Pivot之后调整相机Game场景中不见背景Scene中在相机范围

unity打包windows多屏窗口位置

Unity3D中怎么将一个建好场景移动到另一个场景中

Unity场景切换

Unity中场景加载出现问题,解决方法是啥

unity3d场景里面放个物体怎么看不见