如何将SCNRenderer与现有的MTLCommandBuffer结合起来?

7

我将Vuforia SDK图像目标跟踪特性成功地整合到了iOS项目中,通过将SDK提供的OpenGL上下文(EAGLContext)与SceneKit的SCNRenderer实例相结合。这使我能够利用SceneKit的3D API的简单性,同时又从Vuforia的高精度图像检测中受益。现在,我想通过使用Metal来替换OpenGL来做同样的事情。

一些背景故事:

我能够使用OpenGL在Vuforia绘制的实时视频纹理上方绘制SceneKit对象而没有遇到主要问题。

以下是我使用的简化设置与OpenGL

func configureRenderer(for context: EAGLContext) {
    self.renderer = SCNRenderer(context: context, options: nil)
    self.scene = SCNScene()
    renderer.scene = scene

    // other scenekit setup
}

func render() {
    // manipulate scenekit nodes

    renderer.render(atTime: CFAbsoluteTimeGetCurrent())
}

苹果在iOS 12上弃用了OpenGL

自从苹果宣布在iOS 12上弃用OpenGL,我认为将该项目迁移到使用Metal而不是OpenGL是一个好主意。

理论上应该很简单,因为Vuforia支持开箱即用的Metal。但是,在尝试集成它时,我遇到了困难。

问题

视图似乎只会渲染SceneKit渲染器的结果或由Vuforia编码的纹理,但两者永远不会同时出现。这取决于哪个先编码。我该怎么做才能混合两个结果?

以下是问题设置的要点:

func configureRenderer(for device: MTLDevice) {
    let renderer = SCNRenderer(device: device, options: nil)
    self.scene = SCNScene()
    renderer.scene = scene

    // other scenekit setup
}

func render(viewport: CGRect, commandBuffer: MTLCommandBuffer, drawable: CAMetalDrawable) {
    // manipulate scenekit nodes

    let renderPassDescriptor = MTLRenderPassDescriptor()
    renderPassDescriptor.colorAttachments[0].texture = drawable.texture
    renderPassDescriptor.colorAttachments[0].loadAction = .load
    renderPassDescriptor.colorAttachments[0].storeAction = .store
    renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0, blue: 0, alpha: 0)

    renderer!.render(withViewport: viewport, commandBuffer: commandBuffer, passDescriptor: renderPassDescriptor)
}

我尝试在 encoder.endEncoding 之后或在 commandBuffer.renderCommandEncoderWithDescriptor 之前调用 render 方法:

metalDevice = MTLCreateSystemDefaultDevice();
metalCommandQueue = [metalDevice newCommandQueue];
id<MTLCommandBuffer>commandBuffer = [metalCommandQueue commandBuffer];

//// -----> call the `render(viewport:commandBuffer:drawable) here <------- \\\\

id<MTLRenderCommandEncoder> encoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

// calls to encoder to render textures from Vuforia

[encoder endEncoding];

//// -----> or here <------- \\\\

[commandBuffer presentDrawable:drawable];
[commandBuffer commit];

无论哪种情况,我只能看到SCNRenderer的结果或者encoder的结果,但是从来没有在同一个视图中同时显示两者的结果。
对我来说,好像上面的编码过程和SCNRenderer.render会相互覆盖各自的缓冲区。
我错过了什么吗?

2
在我看来,您肯定想先绘制Vuforia内容。当您第二次绘制SceneKit时,SceneKit没有绘制的部分是什么颜色?此外,您尝试在Xcode中捕获GPU帧并双重检查渲染通道属性是否正确了吗?这将告诉您比我们从这些代码片段中推断出的更多信息。 - warrenm
你尝试过将SceneKit场景渲染到单独的纹理上,然后再与Vuforia结果混合吗?此外,GPU帧捕获的想法也是不错的。 - Dennis L
你解决了吗? - masaldana2
1个回答

0

我想我找到了答案。 在 endEncoding 之后,我正在渲染 scnrenderer,但我正在创建一个新的描述符。

    // Pass Metal context data to Vuforia Engine (we may have changed the encoder since
    // calling Vuforia::Renderer::begin)
    finishRender(UnsafeMutableRawPointer(Unmanaged.passRetained(drawable!.texture).toOpaque()), UnsafeMutableRawPointer(Unmanaged.passRetained(encoder!).toOpaque()))
    
    // ========== Finish Metal rendering ==========
    encoder?.endEncoding()
    
    // Commit the rendering commands
    // Command completed handler
    commandBuffer?.addCompletedHandler { _ in self.mCommandExecutingSemaphore.signal()}
    let screenSize = UIScreen.main.bounds.size
    let newDescriptor = MTLRenderPassDescriptor()
    
    // Draw to the drawable's texture
    newDescriptor.colorAttachments[0].texture = drawable?.texture

    // Store the data in the texture when rendering is complete
    newDescriptor.colorAttachments[0].storeAction = MTLStoreAction.store
    // Use textureDepth for depth operations.
    newDescriptor.depthAttachment.texture = mDepthTexture;
    renderer?.render(atTime: 0, viewport: CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height), commandBuffer: commandBuffer!, passDescriptor: newDescriptor)
    
    // Present the drawable when the command buffer has been executed (Metal
    // calls to CoreAnimation to tell it to put the texture on the display when
    // the rendering is complete)
    commandBuffer?.present(drawable!)
    
    // Commit the command buffer for execution as soon as possible
    commandBuffer?.commit()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接