【问题标题】:How to Extract SceneKit Depth Buffer at runtime in AR scene?如何在 AR 场景中在运行时提取 SceneKit 深度缓冲区?
【发布时间】:2019-09-18 21:18:56
【问题描述】:

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

【问题讨论】:

    标签: swift scenekit augmented-reality arkit depth-buffer


    【解决方案1】:

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

    VR 视图的深度通道

    如果您需要从 SceneKit 的 3D 环境中渲染深度通道,那么您应该使用例如SCNGeometrySource.Semantic 结构。有vertexnormaltexcoordcolortangent 类型属性。让我们看看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 用于前置摄像头的情况下才有可能。如果是这样,那么您可以使用 capturedDepthData 实例属性,该属性会为您提供与视频帧一起捕获的深度图。

    var capturedDepthData: AVDepthData? { get }
    

    但是这个深度图图像是only 15 fps and of lower resolution,而不是对应的 60 fps 的 RGB 图像

    基于面部的 AR 在兼容设备上使用前置深度感应摄像头。运行这样的配置时,会话提供的帧除了彩色相机捕获的彩色像素缓冲区(请参阅capturedImage)之外,还包含深度相机捕获的深度图。运行其他 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 框架提取真实的深度通道。

    看看Image Depth Map教程,Disparity概念将介绍给你。

    【讨论】:

    • 感谢您提供如此广泛的答案!我只是在为我传递到我的后置摄像头馈送中的 3D 对象的深度缓冲区之后 - 所以这将是你的第一个选择吗?这有点问,但如果你能进一步澄清这个选项please - 其他两种技术似乎相当有据可查,但是我的需要,我预计这将是很正常的请求不是。我只需要一个灰度 UIImage 甚至 CVImage 等,如果有必要可以调整大小以匹配我正在捕获的 RGB 缓冲区。谢谢。
    • 确切地说,您到底需要什么:来自 SceneKit 的 DepthPass(用于虚拟对象)或来自 ARSCNView 的 DepthPass(用于现实世界的对象)?还是两者兼而有之?
    • 只有虚拟对象,不幸的是我的手机太旧了。
    • 我更新了我的答案。有一个链接到真正的depth-channel-extraction 项目。您可以在那里找到您正在寻找的所有功能。
    • 我尽量简洁,但我只需要虚拟对象! - 无论如何,它仍然是一个很好的答案!例如,只是让我的运行时增加了地理的深度。非常抱歉,非常感谢您的回答。 -- 现在刚刚编译,但 Readme.md 似乎只是用于相机视图而不是虚拟对象。
    猜你喜欢
    • 2017-03-21
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 2012-04-12
    • 2011-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多