title: unity-shader-深度图及其应用
categories: Unity3d-Shader
tags: [unity, shader, 深度图, depth]
date: 2019-05-13 20:44:51
comments: false

unity-shader-深度图及其应用


前篇

  • Unity Shader - 深度图基础及应用 (比较详细的记录了深度图) - https://www.jianshu.com/p/80a932d1f11e
  • 深度值精度 - https://learnopengl-cn.readthedocs.io/zh/latest/04 Advanced OpenGL/01 Depth testing/#_3
  • 深度冲突 - https://learnopengl-cn.readthedocs.io/zh/latest/04 Advanced OpenGL/01 Depth testing/#_5
  • Unity Shader 基础(3) 获取深度纹理 - https://www.cnblogs.com/zsb517/p/6655546.html

开启渲染 深度图

需要 Camera 组件的 depthTextureMode 中的深度位设置为1, 也就是用 运算 DepthTextureMode.Depth

using UnityEngine;public class DepthTextureTest : MonoBehaviour {void OnEnable() {GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;}void OnDisable() {GetComponent<Camera>().depthTextureMode &= ~DepthTextureMode.Depth;}
}

然后在 shader 中定义 uniform 变量 , 类型时 sampler2D, 命名必须是 _CameraDepthTexture

sampler2D _CameraDepthTexture;// 采样深度图, 用模型的 屏幕坐标([0, 1]区间) 去 采样 场景深度图 获取场景 深度值 (非线性的 [0, 1] 区间)
float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));

深度图

  • Unity Shader - 深度图基础及应用 (介绍的挺详细的) - https://www.jianshu.com/p/80a932d1f11e

深度图的使用时比较耗费批次的, 场景中的 不透明 ( opaque ) 物体需要渲染两遍, 第一遍 ( 也就是多出来的那一倍 ) 渲染是为了的得到深度纹理.

所以 深度图 不等于 深度缓存

  • 深度图是提前把 不透明 ( opaque ) 物体全都渲染了一遍从而得到的
  • 深度缓存 是在渲染每一个物体是根据给定的 深度比较条件 从而写进去的值.
  • 注意: 通过增加 深度写入pass 的方式, 也不能将该物体写入到 深度图 中, 因为pass只在正常渲染下起作用.
    参考: [透明 ( transparent ) 物体写入深度的姿势](#透明 ( transparent ) 物体写入深度的姿势)

深度纹理并非深度缓冲中的数据,而是通过特定Pass获得

深度图 不等于 深度缓存

Unity4.X和Unity5.X版本的实现方式不太一样,Unity4.X通过"RenderType" 标签通过Camera Shader 替换获取,Unity5通过ShadowCaster Pass获取,Unity5官方文档描述:

Depth texture is rendered using the same shader passes as used for shadow caster rendering (ShadowCaster pass type). So by extension, if a shader does not support shadow casting (i.e. there’s no shadow caster pass in the shader or any of the fallbacks), then objects using that shader will not show up in the depth texture.
Make your shader fallback to some other shader that has a shadow casting pass, or If you’re using surface shaders, adding an addshadow directive will make them generate a shadow pass too.
Note that only “opaque” objects (that which have their materials and shaders setup to use render queue <= 2500) are rendered into the depth texture.

对于自身带有 ShadowCaster Pass 或者 FallBack 中含有,并且 Render Queue 小于等于 2500 的渲染对象才会出现在深度纹理中,详细的测试可以参考:【Unity Shader】Shadow Caster、RenderType和_CameraDepthTexture

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

实际测试

即使关了 深度写入 ZWrite Off , 深度图 还是有这个对象的深度信息. (使用 framedebugger 调试)


注意

需要注意的是 能量场 ( 透明渲染 ) _CameraDepthTexture 中只保存了场景中不透明物体的深度信息,因此这个时候无法从CameraDepthTexture 中获取 能量场 的深度信息,所以要在 vert 中计算顶点的深度,这里我利用了 COMPUTE_EYEDEPTH 这个内置的宏。在之后的 frag 内就可以很方便的获取场景和能量场当前片元的深度了。
参考: http://www.php361.com/index.php?c=index&a=view&id=5257

//vert
o.screenPos = ComputeScreenPos(o.vertex);
COMPUTE_EYEDEPTH(o.screenPos.z);//frag
float sceneZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)));
float partZ = i.screenPos.z;

相交判断

以上面 能量场 (ForceField) 为例, ForceField 丢到透明度队列渲染, 则该物体不会再就不会写入到深度图中, 从 Frame Debug 中可以看出

所以可以用 vert 计算出 深度值, 与 深度图 中的值 ( 也就是场景中 不透明 物体的z值 ) 相比较, 小于某个阈值时可以定义为相交.

//vert
o.screenPos = ComputeScreenPos(clipPos); // ComputeScreenPos 的参数是剪裁空间下的位置
COMPUTE_EYEDEPTH(o.screenPos.z); // 计算出 顶点位置 视空间 的 z 值, 保存到 o.screenPos.z //frag
float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)); // 用模型的 屏幕坐标([0, 1]区间) 去 采样 场景深度图 获取场景 深度值
float sceneZ = LinearEyeDepth(depth); // 把 深度值 转换到 视空间
float partZ = i.screenPos.z;// 两者相减就是深度的差异diff,再用1 - diff就得到了一个“相交程度”。
float diff = sceneZ - partZ; // 两个值都在同一空间下, 就可以做比较了
float intersect = (1 - diff) * _IntersectPower;

相关宏

#    define COMPUTE_EYEDEPTH(o) o = -UnityObjectToViewPos( v.vertex ).z # 计算出 视空间 的 z 值
#   define SAMPLE_DEPTH_TEXTURE_PROJ(sampler, uv) (tex2Dproj(sampler, uv).r)
#   define UNITY_PROJ_COORD(a) a

LinearEyeDepth 与 Linear01Depth 的使用场景

两者的共同点是 参数都是 用从 深度图 中采样出来的 深度值.

//vert
o.screenPos = ComputeScreenPos(clipPos); // ComputeScreenPos 的参数是剪裁空间下的位置//frag
float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));
  • LinearEyeDepth

    可以用在 物体a 需要与场景的 深度值 作比较, 进而判断出是否相交. 此时需要保证 物体a 不会渲染都深度图中, 不然不能做比较. 可以参考 [深度图 不等于 深度缓存](#深度图 不等于 深度缓存)

    比较的两个值需要转换到 同一空间 下 (一般就是是 视空间) 才能进行比较, 具体代码可以参考 相交判断

  • Linear01Depth

    可以用在 控制的值 (材质球暴露出来设置) 与 深度值 作比较, 所以要把 深度值 约束在 [0, 1] 区间内就得用到这个函数. 控制的值 就可以在 [0,1] 区间与 深度比较了.

    例如扫描线效果

    float4 frag_depth(v2f_img i) : SV_Target {float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);float lnr01Depth = Linear01Depth(depth);fixed4 screenTexture = tex2D(_MainTex, i.uv);float near = smoothstep(_ScanValue, lnr01Depth, _ScanValue - _ScanLineWidth); // _ScanValue 就是 控制值, 在 [0, 1] 区间float far = smoothstep(_ScanValue, lnr01Depth, _ScanValue + _ScanLineWidth);fixed4 emissionClr = _ScanLineColor * (near + far);return screenTexture + emissionClr;
    }
    

    后处理直接绘制 深度图

    float4 frag_depth(v2f_img i) : SV_Target {float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);float lnr01Depth = Linear01Depth(depth);return fixed4(lnr01Depth, lnr01Depth, lnr01Depth, 1);
    }
    


透明 ( transparent ) 物体写入深度的姿势

参考: Unity3D - Shader - 开启深度写入的半透明效果 - https://blog.csdn.net/biezhihua/article/details/78690574

正常来说 透明物体 是不会写入深度的, 但是可以通过增加多一个 pass 只用来写入深度, 但不输出颜色.

注意: 即使通过增加 深度写入pass 的方式, 也不能将该物体写入到 深度图 中, 因为pass只在正常渲染下起作用.

Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}// 仅仅是把模型的深度信息写入深度缓冲中
// 从而剔除模型中被自身遮挡的片元
Pass {// 开启深度写入ZWrite On// 用于设置颜色通道的写掩码(Wirte Mask)// ColorMask RGB|A|0|(R/G/B/A组合)// 当为0时意味着该Pass不写入任何颜色通道,也就不会输出任何颜色ColorMask 0
}

实测一下, 将透明物体的渲染设为 1000, 让它比 opaque 更快渲染

  • 有深度写入pass的结果. 可以正常遮挡后面渲染的 胶囊体 , 同时和天空盒混合了

  • 没有深度写入pass的结果, 不能遮挡 胶囊体

将透明物体的渲染设为 3000, 让它比 opaque 更后渲染, 就正常可以正常混合到 胶囊体, 应为颜色缓存区已经有了胶囊体的颜色了.

踩坑

  • 如果代码没错,而看到的是全黑的,那么应该就是摄像机的Far Clip Plane设得太大。

深度值 推到 世界坐标位置

  • Unity3D 片元NDC空间z值(ZBuffer)转View空间z值,公式推导 - https://blog.csdn.net/u012149999/article/details/78678901

  • 全面认识Depth - 这里有关于Depth的一切 - https://zhuanlan.zhihu.com/p/25095708

  • shader 推导流程, 可以参照 运动模糊

    //使用宏和纹理坐标对深度纹理进行采样,得到深度值
    float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);//构建当前像素的NDC坐标,xy坐标由像素的纹理坐标映射而来,z坐标由深度值d映射而来
    float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
    //使用 当前帧的视角 * 投影矩阵 的逆矩阵 对H进行变换
    float4 D = mul(_CurrentViewProjectionInverseMartix, H);
    //把结果除以它的w分量,得到该像素世界空间下的坐标
    float4 worldPos = D / D.w;
    

为什么要除以 分量w ? 参考说明 剪裁坐标

因为经过了 投影矩阵, 分量w 就不在为1, 所以要求得具体的世界坐标, 就需要 除以 分量w 的影响


深度偏移

Offset 修改深度偏移值

 Pass{Name "FORWARD" Tags { "LightMode" = "ForwardBase" }ZWrite OnOffset 3000,0

unity-shader-深度图及其应用相关推荐

  1. Unity Shader学习:动态模糊(shutter angle方式)

    Unity Shader学习:动态模糊 动态模糊一般有帧混合和motion vector两种,这里主要介绍motion vector的方法. Keijiro源码:https://github.com/ ...

  2. Unity Shader学习:体积光/体积阴影

    Unity Shader学习:体积光/体积阴影 在前向渲染下实现平行光的体积光影效果,需要全屏深度图,延迟渲染会更划算. 思路:通过ray marching的步进点位置计算该点是否在阴影中,采样阴影贴 ...

  3. Unity Shader - 水体交互

    水体交互 水体交互效果在游戏中是一个很常见的需求,这里简单实现一个可交互的水体. 本篇文章主要是介绍水体交互的实现思路,水体的渲染这里就不再详细介绍,网上很多关于水体的渲染方法很多,可以自己百度.Go ...

  4. unity shader学习---光照模型(二)

    更复杂的光照 效果图 理论 unity的光源类型 unity的渲染路径 在这里插入图片描述 实验 多光源 阴影 不透明物体的阴影 让物体接收阴影 统-管理光照衰减和阴影 透明度物体的阴影 代码 多光源 ...

  5. Unity Shader 水多种元素的实现(反射、折射、菲涅尔、深浅、浪花/泡沫、水波、可交互)

    综合效果 经过各元素叠加 和 程序的审美调参 后的综合效果 交互的水波与边缘浪花的合并需要优化一下 反射 两种方案: cubeMap 以水面对称设一个摄像机 cubeMap 实现:反射探针生成Cube ...

  6. 【Unity Shader】unity海边波浪效果的实现

    效果图如下(GIF因为为了把图压小所以删掉了一些帧导致后面速度突然很快,实际效果并不是这样~_~) PS.对于移动端,参考该文章:http://www.lsngo.net/2018/03/22/uni ...

  7. Unity Shader - 实现简单水体 - 浅水到深水颜色控制

    文章目录 制作步骤 准备好水体网格 扰动水体网格 添加水体网格色调,纹理 放置海上放哨点(一些随便放的立方体) 添加水的深浅透视效果 添加水光效 重构水顶点法线 正交的相机的深度需要注意 改进 Pro ...

  8. Unity Shader学习:SSR屏幕空间反射

    Unity Shader学习:SSR屏幕空间反射 本文在前向渲染模式下实现,延迟渲染更适合SSR,这里只简单的实现下,未作更深入的优化. 思路:沿视线和法线的反射向量步进光线,判断打到物体(这里用的是 ...

  9. 【unity shader】unity游戏特效-仿《幽灵特警》生命扫描仪索敌效果(运用深度、相交算法、CommandBuffer)

    街机游戏<幽灵特警>第一关有个这样的效果: 嗯,透视挂hhhh,关键很炫. 来做个吧. 第一步,做"墙" 仔细观察GIF可见,这个效果像是一堵向前跑的墙,撞到无生命物体 ...

  10. Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap

    文章目录 思路 实践 在方向光的位置,放一个正交相机 调整光源相机参数 将光源投影空间的正交视锥体画出来 投射阴影 接收阴影 改进 超出Shadow map的默认为光照 添加光照处理 添加PCF柔滑整 ...

最新文章

  1. Java中 多态的理解
  2. 必背单词_研究生满大街走?真实数据来啦 真题必背单词Day10
  3. OSGI嵌入jetty应用服务器
  4. 加速你的Python
  5. 创建者模式 builder
  6. 逐帧动画与人运动动画制作
  7. Flask入门 表单Flask-wtf form原生 Bootstrap渲染(七)
  8. Linux下PDF转图片格式
  9. 这是浙江大学郑强教授的经典语录 虽然我不完全赞同但对他的精神佩服的五体投地...
  10. python光棍节快乐_光棍节快乐的祝福语QQ【17句】
  11. ubuntu  管理员权限 文件操作
  12. VMWare、Ubuntu、ROS安装参考汇总
  13. 海洋CMS采集翻译发布插件
  14. redis appendonly.aof文件损坏修复方法
  15. php多人联网,像素生存者3怎么联机 多人联网怎么玩
  16. Shell攻关之shell基础
  17. Collection和List,Set,Map的关系与说明
  18. AFNetworking用法
  19. 操作系统学习笔记——第二章 进程的描述与控制(二)
  20. telegram怎么改密码?

热门文章

  1. unity3d模拟树叶飘动_Unity3D实现扭动挤压浏览效果
  2. Spring Cloud Fegin 详解(一)
  3. 以太网 总线型? or 星型?
  4. 字节跳动28岁程序员期权价值过亿提前退休
  5. 前端优化-图片打包处理base64的原因
  6. 使用计算机有哪些违规操作,你在用的哪些APP含违法违规 没想到居然有它
  7. linux kde桌面 配置,自定义kde桌面配置并发布
  8. Android刘海屏、水滴屏全面屏适配详解
  9. Ajax 文件上传(前后端完整代码,注意修改文件保存路径\访问路径)
  10. VIVO和OPPO刘海屏适配方案