You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现VisionOS Portal实体突出效果?仿《Encounter Dinosaurs》体验

实现《Encounter Dinosaurs》式的Portal实体突出效果

核心原理分析

WWDC演讲中的标准Portal实现依赖PortalComponent的裁剪平面,会强制裁剪超出Portal边界的内容。而《Encounter Dinosaurs》的效果是将场景拆分为受限背景元素(如岩石,保留裁剪)和可突破前景元素(如恐龙,取消裁剪),通过动态调整渲染和遮挡逻辑,实现实体从Portal突出,同时保证相机在Portal前后都有正确表现。

分步实现方案

1. 拆分场景层级

将虚拟场景分为两个独立部分:

  • 背景世界:包含需要被Portal边界裁剪的元素(如岩石、环境),使用标准PortalMaterial+PortalComponent实现基础Portal效果。
  • 前景实体:需要突破Portal的元素(如恐龙),不加入背景世界,直接添加到主场景,通过空间变换与虚拟世界对齐。

2. 配置基础Portal与背景世界

先搭建标准的Portal框架,确保背景元素正常显示且被裁剪:

// 创建背景世界(承载需裁剪的元素)
let backgroundWorld = Entity()
backgroundWorld.components.set(WorldComponent())
// 添加岩石、环境等实体到backgroundWorld
content.add(backgroundWorld)

// 创建Portal实体
let portalEntity = Entity()
portalEntity.components.set(PortalComponent(world: backgroundWorld))
// 创建Portal平面网格
let portalMesh = MeshResource.generatePlane(width: 2.0, height: 3.0)
let portalMaterial = PortalMaterial()
portalEntity.components.set(MeshComponent(mesh: portalMesh, materials: [portalMaterial]))
// 定位Portal到真实空间的目标位置
portalEntity.transform = Transform(translation: [0, 1.5, -2])
content.add(portalEntity)

3. 配置可突出的前景实体

将前景实体直接加入主场景,对齐到虚拟世界的空间位置,同时调整渲染属性避免被裁剪:

// 加载恐龙实体
let dinosaurEntity = try! Entity.load(named: "Dinosaur")
// 将恐龙的空间位置与背景世界对齐
dinosaurEntity.transform = Transform(matrix: backgroundWorld.transform.matrix)
// 移除默认的裁剪限制(若有)
dinosaurEntity.components.remove(PortalComponent.self)
content.add(dinosaurEntity)

4. 动态调整遮挡逻辑

监听相机位置变化,根据相机相对于Portal平面的位置,动态切换前景实体的遮挡状态,确保相机在Portal前后都有正确表现:

// 订阅场景更新事件,实时检测相机位置
arView.scene.subscribe(to: SceneEvents.Update.self) { [weak self] _ in
    guard let self = self, let portalTransform = self.portalEntity?.transform else { return }
    
    let cameraPosition = self.arView.cameraTransform.translation
    let portalPosition = portalTransform.translation
    // 获取Portal平面的法向量(假设Portal朝向相机,即Z轴负方向)
    let portalNormal = portalTransform.matrix.columns.2.xyz
    
    // 计算相机到Portal平面的距离:正数表示相机在真实空间侧,负数表示在虚拟空间侧
    let distanceToPortal = simd_dot(cameraPosition - portalPosition, portalNormal)
    
    if distanceToPortal > 0 {
        // 相机在真实空间侧:让恐龙被Portal遮挡后方部分,同时突出的部分正常显示
        dinosaurEntity.components.set(OcclusionComponent(occlusionPriority: .high))
        // 调整材质的深度属性,确保恐龙在Portal前方的部分优先渲染
        for (index, material) in dinosaurEntity.model?.materials.enumerated() ?? [] {
            if var standardMat = material as? StandardMaterial {
                standardMat.depthTestEnabled = true
                standardMat.depthWriteEnabled = true
                standardMat.zOffset = -0.01 // 避免与Portal平面的深度冲突
                dinosaurEntity.model?.materials[index] = standardMat
            }
        }
    } else {
        // 相机在虚拟空间侧:移除遮挡,让恐龙完全可见
        dinosaurEntity.components.remove(OcclusionComponent.self)
    }
}

5. 细节优化

  • 视觉过渡:给Portal添加半透明边框实体,增强恐龙突出部分的自然感。
  • 交互适配:为前景实体添加合适的碰撞体,确保在真实空间中也能响应手势交互。
  • 性能优化:避免在每帧更新中重复修改材质,可缓存材质实例,仅在相机位置切换时更新。

内容的提问来源于stack exchange,提问作者Reality-Dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 13:16:24