【问题标题】:Save ARFaceGeometry to OBJ file将 ARFaceGeometry 保存到 OBJ 文件
【发布时间】:2020-04-15 21:58:53
【问题描述】:

在 iOS ARKit 应用程序中,我一直在尝试将 ARFaceGeometry 数据保存到 OBJ 文件中。我按照这里的解释:How to make a 3D model from AVDepthData?。但是,未正确创建 OBJ。这是我所拥有的:

    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        guard let faceAnchor = anchor as? ARFaceAnchor else { return }
        currentFaceAnchor = faceAnchor

        // If this is the first time with this anchor, get the controller to create content.
        // Otherwise (switching content), will change content when setting `selectedVirtualContent`.
        if node.childNodes.isEmpty, let contentNode = selectedContentController.renderer(renderer, nodeFor: faceAnchor) {
            node.addChildNode(contentNode)
        }

        // https://stackoverflow.com/questions/52953590/how-to-make-a-3d-model-from-avdepthdata
        let geometry = faceAnchor.geometry        
        let allocator = MDLMeshBufferDataAllocator()
        let vertices = allocator.newBuffer(with: Data(fromArray: geometry.vertices), type: .vertex)
        let textureCoordinates = allocator.newBuffer(with: Data(fromArray: geometry.textureCoordinates), type: .vertex)
        let triangleIndices = allocator.newBuffer(with: Data(fromArray: geometry.triangleIndices), type: .index)
        let submesh = MDLSubmesh(indexBuffer: triangleIndices, indexCount: geometry.triangleIndices.count, indexType: .uInt16, geometryType: .triangles, material: MDLMaterial(name: "mat1", scatteringFunction: MDLPhysicallyPlausibleScatteringFunction()))

        let vertexDescriptor = MDLVertexDescriptor()
        // Attributes
        vertexDescriptor.addOrReplaceAttribute(MDLVertexAttribute(name: MDLVertexAttributePosition, format: .float3, offset: 0, bufferIndex: 0))
        vertexDescriptor.addOrReplaceAttribute(MDLVertexAttribute(name: MDLVertexAttributeNormal, format: .float3, offset: MemoryLayout<float3>.stride, bufferIndex: 0))
        vertexDescriptor.addOrReplaceAttribute(MDLVertexAttribute(name: MDLVertexAttributeTextureCoordinate, format: .float2, offset: MemoryLayout<float3>.stride + MemoryLayout<float3>.stride, bufferIndex: 0))
        // Layouts
        vertexDescriptor.layouts.add(MDLVertexBufferLayout(stride: MemoryLayout<float3>.stride + MemoryLayout<float3>.stride + MemoryLayout<float2>.stride))

        let mdlMesh = MDLMesh(vertexBuffers: [vertices, textureCoordinates], vertexCount: geometry.vertices.count, descriptor: vertexDescriptor, submeshes: [submesh])
        mdlMesh.addNormals(withAttributeNamed: MDLVertexAttributeNormal, creaseThreshold: 0.5)
        let asset = MDLAsset(bufferAllocator: allocator)
        asset.add(mdlMesh)

        let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let exportUrl = documentsPath.appendingPathComponent("face.obj")
        try! asset.export(to: exportUrl)
    }

生成的 OBJ 文件如下所示:

# Apple ModelIO OBJ File: face
mtllib face.mtl
g 
v -0.000128156 -0.0277879 0.0575149
vn 0 0 0
vt -9.36008e-05 -0.0242016
usemtl material_1
f 1/1/1 1/1/1 1/1/1
f 1/1/1 1/1/1 1/1/1
f 1/1/1 1/1/1 1/1/1
... and many more lines

我希望有更多的顶点,而且索引值看起来不对。

【问题讨论】:

  • 你能把人脸几何数据导出到obj文件吗?我的子网格未初始化,因此它崩溃了。请指导我如何导出这些数据。
  • @Daniel 运气好吗?

标签: swift scenekit arkit metal scenekit-modelio


【解决方案1】:

核心问题是您的顶点数据描述不正确。当您在构建网格时向模型 I/O 提供顶点描述符时,它表示数据实际具有的布局,而不是您想要的布局。您提供了两个顶点缓冲区,但您的顶点描述符描述了一个只有一个顶点缓冲区的交错数据布局。

解决此问题的最简单方法是修复顶点描述符以反映您提供的数据:

let vertexDescriptor = MDLVertexDescriptor()
// Attributes
vertexDescriptor.attributes[0] = MDLVertexAttribute(name: MDLVertexAttributePosition,
                                                    format: .float3,
                                                    offset: 0,
                                                    bufferIndex: 0)
vertexDescriptor.attributes[1] = MDLVertexAttribute(name: MDLVertexAttributeTextureCoordinate,
                                                    format: .float2,
                                                    offset: 0,
                                                    bufferIndex: 1)
// Layouts
vertexDescriptor.layouts[0] = MDLVertexBufferLayout(stride: MemoryLayout<float3>.stride)
vertexDescriptor.layouts[1] = MDLVertexBufferLayout(stride: MemoryLayout<float2>.stride)

当您稍后调用addNormals(...) 时,模型 I/O 将分配必要的空间并更新顶点描述符以反映新数据。由于您不是从数据中渲染而是立即将其导出,因此它为法线选择的内部布局并不重要。

【讨论】:

  • 能否请您告诉我,我们是否也可以捕获一系列面部表情并导出到文件中,然后当我们使用某些面部网格加载此文件时复制相同的表情?如果可以,我们可以这样做吗?请指导我如何实现这一目标
  • 当然。只需在您的会话委托中实现session:didUpdateAnchors:,抓住面部锚点,然后使用上述技术每帧编写一个模型文件。您可能希望使用更节省空间的格式,例如 USDC 或 Alembic,因为 OBJ 非常冗长。
  • 非常感谢您的建议。我能够将面部锚系列转换为 usdc 格式。我们可以把这个系列分成usdz播放动画吗?
  • 我不确定 USD 格式对动画一系列网格的支持,但看起来您可以将多个 usdc 文件组合成一个存储在 USDZ 资产中的单个 USD 场景,然后依次通过网格循环制作动画,“翻书”风格。我唯一关心的是如何解决一次加载所有网格(高内存使用)和按需加载(从磁盘读取时可能会卡顿)之间的权衡。
  • 我也想导出贴图。我该怎么做
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-31
  • 2020-06-27
  • 1970-01-01
  • 1970-01-01
  • 2012-09-27
  • 2010-10-16
  • 2016-06-11
相关资源
最近更新 更多