首页 > 解决方案 > 如何在 AR 场景中在运行时提取 SceneKit 深度缓冲区?

问题描述

如何提取 SceneKit 深度缓冲区?我制作了一个运行 Metal 的基于 AR 的应用程序,我真的很难找到有关如何提取 2D 深度缓冲区的任何信息,以便渲染出我的场景的精美 3D 照片。非常感谢任何帮助。

标签: swiftscenekitaugmented-realityarkitdepth-buffer

解决方案


你的问题不清楚,但我会尽力回答。

VR 视图的深度通道

如果您需要从 SceneKit 的 3D 环境中渲染深度通道,那么您应该使用例如SCNGeometrySource.Semantic结构。有vertexnormaltexcoordcolortypetangent属性。让我们看看什么是vertex类型属性:

static let vertex: SCNGeometrySource.Semantic

此语义标识包含几何中每个顶点的位置的数据。对于自定义着色器程序,您可以使用此语义将 SceneKit 的顶点位置数据绑定到着色器的输入属性。顶点位置数据通常是三分量或四分量向量的数组。

这是iOS Depth Sample项目的代码摘录。

更新:使用此代码,您可以获得 SCNScene 中每个点的位置并为这些点分配颜色(这就是 zDepth 通道的真正含义):

import SceneKit

struct PointCloudVertex {
    var x: Float, y: Float, z: Float
    var r: Float, g: Float, b: Float
}

@objc class PointCloud: NSObject {

    var pointCloud : [SCNVector3] = []
    var colors: [UInt8] = []

    public func pointCloudNode() -> SCNNode {
        let points = self.pointCloud
        var vertices = Array(repeating: PointCloudVertex(x: 0,
                                                         y: 0,
                                                         z: 0,
                                                         r: 0,
                                                         g: 0,
                                                         b: 0), 
                                                     count: points.count)

        for i in 0...(points.count-1) {
            let p = points[i]
            vertices[i].x = Float(p.x)
            vertices[i].y = Float(p.y)
            vertices[i].z = Float(p.z)
            vertices[i].r = Float(colors[i * 4]) / 255.0
            vertices[i].g = Float(colors[i * 4 + 1]) / 255.0
            vertices[i].b = Float(colors[i * 4 + 2]) / 255.0
        }

        let node = buildNode(points: vertices)
        return node
    }

    private func buildNode(points: [PointCloudVertex]) -> SCNNode {
        let vertexData = NSData(
            bytes: points,
            length: MemoryLayout<PointCloudVertex>.size * points.count
        )
        let positionSource = SCNGeometrySource(
            data: vertexData as Data,
            semantic: SCNGeometrySource.Semantic.vertex,
            vectorCount: points.count,
            usesFloatComponents: true,
            componentsPerVector: 3,
            bytesPerComponent: MemoryLayout<Float>.size,
            dataOffset: 0,
            dataStride: MemoryLayout<PointCloudVertex>.size
        )
        let colorSource = SCNGeometrySource(
            data: vertexData as Data,
            semantic: SCNGeometrySource.Semantic.color,
            vectorCount: points.count,
            usesFloatComponents: true,
            componentsPerVector: 3,
            bytesPerComponent: MemoryLayout<Float>.size,
            dataOffset: MemoryLayout<Float>.size * 3,
            dataStride: MemoryLayout<PointCloudVertex>.size
        )
        let element = SCNGeometryElement(
            data: nil,
            primitiveType: .point,
            primitiveCount: points.count,
            bytesPerIndex: MemoryLayout<Int>.size
        )

        element.pointSize = 1
        element.minimumPointScreenSpaceRadius = 1
        element.maximumPointScreenSpaceRadius = 5

        let pointsGeometry = SCNGeometry(sources: [positionSource, colorSource], elements: [element])

        return SCNNode(geometry: pointsGeometry)
    }
}

来自 AR 视图的深度通道

如果您需要从 ARSCNView 渲染深度通道,只有在您将ARFaceTrackingConfiguration用于前置摄像头的情况下才有可能。如果是这样,那么您可以使用captureDepthData实例属性,该属性会为您带来与视频帧一起捕获的深度图。

var capturedDepthData: AVDepthData? { get }

但是这个深度图图像only 15 fps and of lower resolution比 60 fps 的相应 RGB 图像

基于面部的 AR 在兼容设备上使用前置深度感应摄像头。运行此类配置时,会话提供的帧除了包含彩色相机捕获的彩色像素缓冲区(请参阅 captureImage )之外,还包含由深度相机捕获的深度。运行其他 AR 配置时,此属性的值始终为零。

真正的代码可能是这样的:

extension ViewController: ARSCNViewDelegate {

    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {

        DispatchQueue.global().async {

            guard let frame = self.sceneView.session.currentFrame else {
                return
            }
            if let depthImage = frame.capturedDepthData {
                self.depthImage = (depthImage as! CVImageBuffer)
            }
        }
    }
}

视频视图的深度通道

此外,您可以使用 2 个后置摄像头和AVFoundation框架提取真正的深度通道。

查看将向您介绍视差概念的图像深度图教程。


推荐阅读