遮挡的实现原理

模拟现实空间的虚拟场景要实现透视效果,那么现实场景网格的Shader就是透明的,实现透明的方案基本有两种,一种是透明度测试,要么是全透明,要么就不透明;一种是透明度混合,可以实现半透明效果,但是需要关闭深度写入功能,渲染彼此相互遮挡的透明物体就可能会出现错误,但是在大多数情况下,这种情况是无法避免的;
Meta的SelectivePassthrough.shader使用的是透明度混合,像它这样,关闭了深度写入,就无法通过深度排序来进行片元级别的渲染了,需要使用渲染队列的方式来决定不同物体的渲染顺序。所以,前后遮挡的逻辑就很简单了。
notion image
现实透视场景的需求是,虽然是透明的,但是与虚拟物体之间没有混合效果,和不透明的网格没两样。这样一来,透明的场景在不透明物体之前时,得到的效果是透明场景覆盖不透明物体;透明场景在不透明物体之后时,不透明物体就覆盖透明场景。有时为了方便寻找被遮挡的物体,我们需要对物体的边缘施加高亮效果,就需要像UI一样始终渲染在最上层。
这时,渲染队列的排布就很明确了,索引值RenderQueue的大小是:
不透明虚拟物体材质<透视材质<高亮材质
推理过程
无论不透明的虚拟物体于透视场景之间的位置关系怎样,根据渲染队列的顺序,都是不透明物体先渲染,并将其深度值写入深度缓冲区。等到透明的场景开始渲染时。当透明物体在不透明物体之前时,其深度值要小于不透明物体,通过了SelectivePassthrough.shader的ZTest的(小于缓冲的深度值)LessEqual条件,所以能够直接渲染在不透明物体之上,也就起到了透明遮挡不透明的效果。
反之,其深度值若大于不透明物体,则不会通过深度测试,被不透明物体遮挡的那一块区域也就不会渲染。
将高亮材质的ZTest通过条件设为Always,再编写一个选中添加高亮材质的条件,这样一来,只要物体被选中且在视野范围内,高亮材质都会被渲染在最顶端。
 
使用Scene API实现的场景理解的遮挡效果是有限的静态遮挡,就像前面的透明场景遮挡不透明物体一样,整个桌子所占据的空间都被空间理解成了一个立方体,当动态物体移动到镂空的桌子底下时是看不到桌面下的存在的。这还是空间理解的实现原理所致——用平面和立方体去描摹现实空间的基本布局。
notion image
所以,Scene API的场景理解更适合在以下情况中使用:
  1. 对于场景模型的精确度要求不高;
  1. 现实场景元素为静态,确保其位置不会发生改变;
  1. 不会出现各种穿帮效果的场景;

配置Depth API

详情请参阅Meta的官方文档和项目的GitHub页面(大部分内容还是Meta描述地更加标准):
Unity-DepthAPI
oculus-samplesUpdated Aug 10, 2024
要实现虚拟世界中更好的遮挡效果,就要使用Depth API,Meta为Quest3的深度传感器特别设计了这个接口,它能够获取物体距离相机的深度信息,因此可以判断虚拟物体和现实物体到相机的距离,来处理虚拟物体与现实物体的前后遮挡关系。
Meta XR SDK V60及以上版本才能使用Depth API
Unity编辑器版本需要在2022.3.1以上
将以上链接在Unity包管理器中用Git导入即可
Project Setup Tool可以很方便地配置Depth API这样的接口所需要的环境,但是最好还是了解一下需要修改哪些配置。
notion image

Depth API的要求

  • Quest 3 or newer device.
  • Unity 2022.3.1f and higher OR Unity 2023.2 and higher
  • com.unity.xr.oculus package supporting Depth API. In Unity Package Manager via Add package by name with the name: com.unity.xr.oculus and version - 4.2.0
  • Graphics API must be set to Vulkan
notion image
  • Rendering mode must be set to Multiview
notion image
 
 
在Depth API的文件夹中寻找EnvironmentDepthOcclusion,然后拖放到场景中
它上面有一个Environment Depth Manager组件,将Occlusion Shaders Mode修改为Soft Occlusion,计算边缘遮挡部分的时候会采用更加平滑的边缘处理,相应地,也更加消耗性能。
notion image

使用Depth API

创建一个Cube,赋给新材质OcclusionMat,使用OcclusionStandard.shader。
然后找到[ l/r ]_handMeshNode,它们是虚拟手部模型的渲染目标,为了展现出虚拟物体与现实世界之间的动态遮挡效果,需要将它们进行隐藏,这样在应用运行的过程中就看不到手部的虚拟模型,也就能够展现出有现实的动态物体(手部)参与的遮挡效果。
以下是Meta对于不能使用虚拟手部实现动态遮挡的解释: (其实原理还是之前提到的透明度混合的缺点,会造成depth fighting,即深度冲突) Hand Removal If you want to render virtual hands to replace the physical hands, this can result in depth fighting. In order to avoid this you can enable the hands removal feature which will remove hands from the environment depth texture and replace it with an approximate background depth. To check if the device supports it: Utils.GetEnvironmentDepthHandRemovalSupported() To toggle the feature, call: Utils.SetEnvironmentDepthHandRemoval(bool enabled)
notion image
Depth API对于性能的消耗是比较大的,目前在Quest3上运行的效果并不太好,特别是遮挡的边缘部分,还是很粗糙。目前,用于空间计算的软硬件都还有很大的进步空间,可以肯定的是,随着未来慢慢地迭代优化,Depth API将会和Scene API一样,作为正式功能供开发者使用,带来更加真实的模拟效果。

实现动态抓取的效果

注:经过下面的配置之后,无法实现动态抓取,Depth API目前还只是一个实验功能,实际效果较差,还是要等到未来的技术进步,简化配置流程。
创建一个小球(任何3D物体都行),给碰撞体添加具有一定弹性的物理材质,刚体和Outline组件,以及Grabbable脚本。然后在Packages文件夹中搜索HandGrabInteractable预制体,将其作为小球的子物体,视作其右手的可交互目标,如果要使用双手,可以在添加一个左手的HandGrabInteractable,与右手同理,相关配置如下。
notion image
notion image
 
将小球拖给Pointable Element和Rigidbody
 
 
 
 
 
 
 
 
Interactable Unity Event Wrapper是负责处理可交互物体的交互事件的组件,这里需要对Hover和Select状态进行配置。
When Hower
使用小球的Outline组件,处于Hover状态时,小球启用轮廓高亮OVRRightHandSynthetic。
使用OVRRightHandSynthetic的子物体r_handMeshNode,即虚拟手部的渲染对象,处于Hover状态时,手部模型就会渲染。
使用EnvironmentDepthOcclusion的EnvironmentDepthManager组件,调用RemoveHands方法,这样,手腕以上手的部分,就不会对虚拟物体产生遮挡的效果
Select的配置逻辑同理,Un后缀的事件所调用的方法都没有打勾,传入参数false,是为了实现交互状态的闭环,选中物体,渲染手部,不选中物体,不渲染手部。

对比Scene API和Depth API

notion image
 
 
Loading...
Cloud
Cloud
Free writing
最新发布
RPG Maker MZ素材规格
2025-1-15
Project Pianting设计文档
2025-1-7
第一年
2025-1-6
水族馆
2025-1-6
Flow in Games
2025-1-6
MISIDE текстовое интервью с разработчиком
2025-1-6
公告