您当前的位置: 首页 >  unity

Jave.Lin

暂无认证

  • 3浏览

    0关注

    704博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Unity Shader - 获取BuiltIn深度纹理和自定义深度纹理的数据

Jave.Lin 发布时间:2020-03-08 07:08:41 ,浏览量:3

文章目录
  • 获取深度的方法
    • 设置Camera.depthTextureMode方式获取
    • Camera.RenderWithShader方式获取
      • 参考这篇:[定制自己的 Depth Texture](https://chengkehan.github.io/CustomDepthTexture.html)
      • 或是这篇:[Unity Shader 基础(3) 获取深度纹理](https://www.cnblogs.com/zsb517/p/6655546.html)
      • 调用Camera.RenderWithShader的CSharp
      • 使用_CustomDepthTexture的Shader
  • 实践
    • 简写说明
      • 什么是ndc,ndc.z
      • 什么是eyeZ
    • 使用unity内置_CameraDepthTexture纹理
      • Shader
      • Material
      • CSharp
      • 运行效果
      • 使用_CameraDepthNormalsTexture纹理
    • 自定义的深度图
      • 深度纹理像素编码的Shader
      • Unity 官方提供的:获取对象的深度信息的例子
      • CSharp
      • 后处理显示Shader
      • 运行效果
  • LinearEyeDepth, Linear01Depth
    • LinearEyeDepth 作用
    • Linear01Depth 作用
    • 快速的获取当前片段的Linear01Depth
  • _CameraDepthTexture和_CameraDepthNormalsTexture 存的深度是什么
  • 题外话
  • 注意Unity正交相机中的深度纹理的编码
  • 总结
  • Project
  • References

深度(Depth),在各种效果中都会用到,学会在Unity中如何获取,也是很有用的。 后续很多一些其他的效果都会与深度有关。 Unity官方在SIGGRAPH2011有分享过一些使用深度制作的特效,文档:SIGGRAPH2011 Special Effect with Depth.pdf 如果多年后,下载不了,链接无效了,可以点击这里(Passworld:cmte)下载(我收藏到网盘了)

由于篇幅原因,本文只介绍:

  • 如何获取纹理;
  • 如何读取数据;
  • 理解数据的来源。
获取深度的方法

下面再简单介绍一次吧:

  • 在Unity中获取,可以通过脚本层(CSharp层)设置Camera.depthTextureMode属性;然后在Shader定义对应变量获取。之前写过一篇,关于后处理获取深度描边的文章。此方法要留意设置好Shader tag的Queue=Geometry( Depth_{Screen} DepthView​=>DepthScreen​ 视空间深度 转化到 屏幕空间深度的公式如下: a = F / ( F − N ) a = F / (F - N) a=F/(F−N) b = N F / ( N − F ) b = NF / (N - F) b=NF/(N−F) d e p t h 屏 幕 空 间 = ( a Z + b ) / Z 为 视 空 间 深 度 depth_{屏幕空间} = (aZ + b)/ Z_{为视空间深度} depth屏幕空间​=(aZ+b)/Z为视空间深度​ d e p t h = ( a Z + b ) / Z depth = (aZ + b) / Z depth=(aZ+b)/Z

    D e p t h S c r e e n = > D e p t h V i e w Depth_{Screen} => Depth_{View} DepthScreen​=>DepthView​ 反推得 屏幕空间深度 转化到 视图空间深度的等于以下公式: d e p t h = ( a Z + b ) / Z depth = (aZ + b) / Z depth=(aZ+b)/Z d e p t h = a + b / Z depth = a + b / Z depth=a+b/Z d e p t h − a = b / Z depth - a = b / Z depth−a=b/Z ( d e p t h − a ) / b = 1 / Z (depth - a) / b = 1 / Z (depth−a)/b=1/Z b / ( d e p t h − a ) = Z b / (depth - a) = Z b/(depth−a)=Z

    D e p t h S c r e e n = > D e p t h V i e w Depth_{Screen} => Depth_{View} DepthScreen​=>DepthView​ 公式: b / ( d e p t h − a ) = Z b / (depth - a) = Z b/(depth−a)=Z,代入a,b: ( N F / ( N − F ) / ( d e p t h − ( F / ( F − N ) ) ) = Z (NF / (N - F) / (depth - (F / (F - N))) = Z (NF/(N−F)/(depth−(F/(F−N)))=Z

    假设N=0.3, F=1000,代入N,F: ((0.3 * 1000) / (0.3 - 1000)) / (depth - (1000 / (1000 - 0.3))) = Z (300 / (-999.7)) / (depth - (1000 / 999.7)) = Z -0.3000900270081024 / (depth - 1.000300090027008)= Z

    假设depth = 0.5,代入depth -0.3000900270081024 / (0.5 - 1.000300090027008) = Z -0.3000900270081024 / -0.500300090027008 = Z 0.5998200539838049 = Z 当depth(屏幕空间) = 0.5,Z(Z为视空间深度) = 0.5998200539838049 那么屏幕空间的depth就是要输入深度纹理的值。

    另外,还有一篇文章写得非常不错:OpenGL阴影映射 里面有将阴影如何生成,以及如何将片段转换到灯光坐标系空间下来获取深度图,然后判断片段深度与灯光坐标空间下的深度图的深度值做比较的决定是否在阴影中。

    里面的深度图,就是将ndc中的z值写入到深度纹理的:tex2D(_CameraDepthTexture, screenPos.xy).r == ndcZ 我借一下他文章中引用的learnopengl中借来的图 在这里插入图片描述

    注意Unity正交相机中的深度纹理的编码

    我虽然没去下载Unity的内置CameraDepthTexture.shader相关的代码。 但是试验后证明,Unity的正交相机的_CameraDepthTexture存着的内容与透视不一样。

    • 透视下存储的是ndc.z
    • 正交下存储的是线性深度比例值,而且他没有处理:UNITY_REVERSED_Z的宏的情况。而透视下的是处理了。所以可以看到我们的shader代码中,透视的是没有处理这个UNITY_REVERSED_Z的代码的。

    先用Profiler>Frame Debugger看看_CameraDepthTexture的内容效果: 在这里插入图片描述

    可以看到,我镜头距离模型很近时,反而深度值越大,距离模型很远时,深度值越小。 这就与透视的不一样了。从这点大概猜到:随着Unity shader内置代码的升级,官方也不断完善处理。让外部使用透明化,但我现在用的是:unity 2018.3.0f2版本的,正交相机下的,他们漏了处理这块内容,我猜大概是正交相机下比较少人用这块深度功能?用正交的一般都是2D游戏。但现在很多2D,或是固定视角2.5D的也有一部分是3D的。

    注意:

            // 正交相机下,_CameraDepthTexture存储的是线性值,且:距离镜头远的物体,深度值小,距离镜头近的物体,深度值大,可以使用UNITY_REVERSED_Z宏做处理
            // 透视相机下,_CameraDepthTexture存储的是ndc.z值,且:不是线性的。
            // _CameraDepthNormalsTexture的纹理,正交相机,与透视相机下都没问题一样这么使用
    

    具体可以参考:Unity Shader - 根据片段深度重建片段的世界坐标。只看在正交相机下的深度纹理重构世界坐标,在里头有详细的试验。

    总结

    在Unity中,获取深度的方式有多种多样。 理解如何编解码深度,对理解深度纹理存储的是什么内容有帮助。 理解Unity提供的深度纹理存储的是什么,才能知道什么地方,什么时候去使用它。 因为总有某些功能在实现时,是基于深度值来实现。

    Project

    backup : UnityShader_GetDepthNormalsTextureTesting_2018.03.2f0

    • Camera.depthTextureMode方式运行:GetBuiltInDepthTextureScene.unity场景。
    • 自定义深度纹理方式运行:GetCustomDepthTextureScene.unity场景。
    References
    • Using Depth Textures
    • Unity Shader-深度相关知识总结与效果实现(LinearDepth,Reverse Z,世界坐标重建,软粒子,高度雾,运动模糊,扫描线效果) - 这篇文章写得很详细,推荐看看。
    • 【Unity Shader】浅析Unity shader中RenderType的作用及_CameraDepthNormalsTexture
    • Unity Shader 基础(3) 获取深度纹理
    • 关于LightMode的pass tag可以查看官方手册或是API的说明。
    • Unity3D中的深度纹理和法线纹理
    • Unity Camera获取深度数据的几种方式 - 这时我写完文章后,发现另一个写得也不错的,分享一下。
关注
打赏
1664331872
查看更多评论
立即登录/注册

微信扫码登录

0.0452s