SceneKit - 从SCNView获取渲染场景作为MTLTexture而不使用单独的SCNRenderer

Kev*_*Bui 6 ios scenekit metal

我的SCNView使用Metal作为渲染API,我想知道是否有办法将渲染场景作为MTLTexture抓取而无需使用单独的SCNRenderer?性能下降,当我试图通过二者的显示场景SCNView和重新渲染场景屏幕外的MTLTexture通过SCNRenderer(我试图抓住每一个输出帧).

SCNView使我能够访问它使用的MTLDevice,MTLRenderCommandEncoderMTLCommandQueue,但不能访问我需要的底层MTLRenderPassDescriptor以获取MTLTexture(via renderPassDescriptor.colorAttachments[0].texture)

我尝试的一些替代方案是尝试使用SCNView.snapshot()UIImage并转换它,但性能更差.

小智 1

** 警告:这可能不是 App Store 的正确方法。但它正在发挥作用。

步骤 1:使用 swizzling 将 CAMetalLayer 的 nextDrawable 方法替换为新方法。为每个渲染循环保存 CAMetalDrawable。

extension CAMetalLayer {
  public static func setupSwizzling() {
    struct Static {
      static var token: dispatch_once_t = 0
    }

    dispatch_once(&Static.token) {
      let copiedOriginalSelector = #selector(CAMetalLayer.orginalNextDrawable)
      let originalSelector = #selector(CAMetalLayer.nextDrawable)
      let swizzledSelector = #selector(CAMetalLayer.newNextDrawable)

      let copiedOriginalMethod = class_getInstanceMethod(self, copiedOriginalSelector)
      let originalMethod = class_getInstanceMethod(self, originalSelector)
      let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)

      let oldImp = method_getImplementation(originalMethod)
      method_setImplementation(copiedOriginalMethod, oldImp)
      method_exchangeImplementations(originalMethod, swizzledMethod)
    }
  }


  func newNextDrawable() -> CAMetalDrawable? {
    let drawable = orginalNextDrawable()
    // Save the drawable to any where you want
    AppManager.sharedInstance.currentSceneDrawable = drawable
    return drawable
  }

  func orginalNextDrawable() -> CAMetalDrawable? {
    // This is just a placeholder. Implementation will be replaced with nextDrawable.
    return nil
  }
}
Run Code Online (Sandbox Code Playgroud)

第 2 步:在 AppDelegate 中设置 swizzling:didFinishLaunchingWithOptions

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  CAMetalLayer.setupSwizzling()
  return true
}
Run Code Online (Sandbox Code Playgroud)

步骤 3:仅为您的 SCNView 的 CAMetalLayer 禁用帧缓冲区(以便为 MTLTexture 调用 getBytes)

if let metalLayer = scnView.layer as? CAMetalLayer {
  metalLayer.framebufferOnly = false
}
Run Code Online (Sandbox Code Playgroud)

第 4 步:在 SCNView 的委托 (SCNSceneRendererDelegate) 中,使用纹理

func renderer(renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: NSTimeInterval) {
    if let texture = AppManager.sharedInstance.currentSceneDrawable?.texture where !texture.framebufferOnly {
      AppManager.sharedInstance.currentSceneDrawable = nil
      // Play with the texture
    }
}
Run Code Online (Sandbox Code Playgroud)

第 5 步(可选):您可能需要确认您在 CAMetalLayer 获得的可绘制对象是您的目标。(如果同时有多个CAMetalLayer)