LiDAR和RealityKit –捕获扫描模型的真实纹理

时间:2020-09-08 12:25:27

标签: swift augmented-reality arkit metal realitykit

任务

我要 capture 真实世界的纹理,并将其应用于借助LiDAR扫描仪生成的3D网格。我想应该使用Projection-View-Model矩阵。必须从固定的视点(例如从房间中心)制作纹理。但是,如果我们可以应用场景中以environmentTexturing纹理收集的cube-map数据,那将是一个理想的解决方案。

enter image description here

查看3D Scanner App。这是一个参考应用程序,允许我们导出带有纹理的模型。

我需要一次迭代捕获一个纹理。我不需要实时更新。我意识到改变PoV会导致错误的纹理感知,换句话说,会导致纹理变形。我还意识到RealityKit中存在动态镶嵌,并且存在自动纹理贴图(纹理的分辨率取决于其捕获的距离)。

import RealityKit
import ARKit
import MetalKit
import ModelIO

class ViewController: UIViewController, ARSessionDelegate {
    
    @IBOutlet var arView: ARView!

    override func viewDidLoad() {
        super.viewDidLoad()

        arView.session.delegate = self
        arView.debugOptions.insert(.showSceneUnderstanding)

        let config = ARWorldTrackingConfiguration()
        config.sceneReconstruction = .mesh
        config.environmentTexturing = .manual
        arView.session.run(config)
    }
}

问题

  • 如何为重建的3D网格捕获和应用真实世界的纹理?


2 个答案:

答案 0 :(得分:8)

这是一个初步的解决方案(不是最终解决方案):

import MetalKit
import ARKit

/*  Color model YCbCr  */
var capturedTextureChannelY: CVMetalTexture?      /*  Luma               */
var capturedTextureChannelCbCr: CVMetalTexture?   /*  Chroma difference  */

lazy var rgbUniforms: RGBUniforms = {
    var uniforms = RGBUniforms()
    uniforms.radius = rgbRadius
    uniforms.viewToCamera.copy(from: viewToCamera)
    uniforms.viewRatio = Float(viewportSize.width / viewportSize.height)
    return uniforms
}()

func updateTextures(frame: ARFrame) {
    let pixelBuffer = frame.capturedImage
    guard CVPixelBufferGetPlaneCount(pixelBuffer) >= 2 else { return }  
  
    capturedTextureChannelY = makeTexture(fromPixelBuffer: pixelBuffer, 
                                              pixelFormat: .r8Unorm, 
                                               planeIndex: 0)
    capturedTextureChannelCbCr = makeTexture(fromPixelBuffer: pixelBuffer, 
                                                 pixelFormat: .rg8Unorm, 
                                                  planeIndex: 1)
}

func makeTexture(fromPixelBuffer pixelBuffer: CVPixelBuffer, 
                                 pixelFormat: MTLPixelFormat, 
                                  planeIndex: Int) -> CVMetalTexture? {

    let width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex)
    let height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex)
    
    var texture: CVMetalTexture? = nil
    let status = CVMetalTextureCacheCreateTextureFromImage(nil, 
                                                           textureCache, 
                                                           pixelBuffer, 
                                                           nil, 
                                                           pixelFormat, 
                                                           width, 
                                                           height, 
                                                           planeIndex, 
                                                           &texture)
      
    if status != kCVReturnSuccess {
        texture = nil
    }
    return texture
}

func draw() {
    guard let currentFrame = session.currentFrame,
          let commandBuffer = commandQueue.makeCommandBuffer(),
          let renderDescriptor = renderDestination.currentRenderPassDescriptor,
          let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderDescriptor)
    else { return }

    self.updateTextures(frame: currentFrame)
    
    if rgbUniforms.radius > 0 {
        var retainingTextures = [capturedTextureChannelY, 
                                 capturedTextureChannelCbCr]

        commandBuffer.addCompletedHandler { buffer in
            retainingTextures.removeAll()
        }

        renderEncoder.setFragmentTexture(CVMetalTextureGetTexture(capturedTextureChannelY!), 
                                                                  index: Int(kTextureY.rawValue))
        renderEncoder.setFragmentTexture(CVMetalTextureGetTexture(capturedTextureChannelCbCr!), 
                                                                  index: Int(kTextureCbCr.rawValue))
        renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
    }
}

P.S。

我在Apple Developer Forum上找到了名为LiDAR equipped for 3D modelling的帖子。它说:

问题

Camera和LiDAR传感器可以一起工作以获得具有纹理的3D模型吗?

答案

是的(部分)可行。您可以将锚的任何几何体投影回相机图像中,以推断出纹理。但是,这需要多个观点和某种形式的高级逻辑,才能通过投影来决定将其应用于几何的哪一部分。

Frameworks工程师

答案 1 :(得分:3)

如何在Unity中完成

我想与一些来自LiDAR的网格分享有关 Unity的AR Foundation 的有趣信息。目前-2020年11月1日-情况荒唐。这与以下事实有关:本机ARKit开发人员无法使用标准的高级RealityKit工具捕获扫描对象的纹理,但是Unity的AR Foundation用户(创建ARKit应用程序)可以使用ARMeshManager脚本执行此操作。我不知道此脚本是由AR Foundation团队开发的,还是仅由一家小型创意创业公司的开发人员开发(然后购买),但事实仍然存在。

要将ARKit网格与AR Foundation一起使用,只需将ARMeshManager组件添加到场景中。如您在图片上看到的,这些功能包括Texture CoordinatesColorMesh Density

enter image description here

如果有人对必须如何在Unity中配置或编写脚本有更详细的信息,请在此线程中发布有关此信息。