ARSCNView のレンダリング パス中に作成される深度テクスチャをエクスポートしようとしています。そのために、SCNScene をバックグラウンドでカスタム .xml でレンダリングするコードを作成しましたMTLRenderPassDescriptor
。「GPU キャプチャ ツール」を使用して GPU にバインドされたリソースをトレースすると、 SCNRenderer.renderメソッドでカスタムMTLRenderPassDescriptor
が無視されることがわかりました。
このコードを使用して、SCNScene をオフスクリーンにレンダリングしました。
// Render Pass - render sceneView
renderer.scene = sceneView.scene
renderer.pointOfView = sceneView.pointOfView
renderer.render(atTime: 0, viewport: viewport, commandBuffer: commandBuffer, passDescriptor: renderPassDescriptor)
GPU キャプチャでリソースを確認すると、レンダラーは で説明されていない独自のフレーム テクスチャと深度テクスチャを生成しrenderPassDescriptor
ます。ドキュメントによると、それは奇妙です。また、ARKit セッションなしでこれをテストしたところ、期待どおりに動作しました (レンダラーは、 で説明されているテクスチャ リソースを使用しますrenderPassDescriptor
)。どうすればこれを修正できますか? これは SceneKit のバグですか?
画像 1. GPU キャプチャで、深度テクスチャがブリット パスにリンクされていないことがわかりました。
画像 2. カラー アタッチメント テクスチャ アドレスは 0x144a4f310 です
画像 3. 深度アタッチメント テクスチャ アドレスは 0x144a50050 です
画像 4. render メソッドにバインドされたテクスチャには異なるアドレスがあります
これが最小の作業例です。
import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate {
@IBOutlet var sceneView: ARSCNView!
var ship: SCNNode!
var device: MTLDevice!
var renderer: SCNRenderer!
var commandQueue: MTLCommandQueue!
let textureSizeX = 2732
let textureSizeY = 2048
lazy var viewport = CGRect(x: 0, y: 0, width: CGFloat(textureSizeX), height: CGFloat(textureSizeY))
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
sceneView.session.delegate = self
sceneView.showsStatistics = true
sceneView.scene = SCNScene()
ship = SCNScene(named: "art.scnassets/ship.scn")?.rootNode.childNode(withName: "shipMesh", recursively: true)!
sceneView.scene.rootNode.addChildNode(ship)
// background renderer
device = MTLCreateSystemDefaultDevice()!
renderer = SCNRenderer(device: device, options: nil)
commandQueue = device.makeCommandQueue()!
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else {
fatalError("Missing expected asset catalog resources.")
}
// Create a session configuration
let configuration = ARWorldTrackingConfiguration()
configuration.detectionImages = referenceImages
// Run the view's session
sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
// MARK: - ARSCNViewDelegate
// Override to create and configure nodes for anchors added to the view's session.
func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) {
doRender()
}
func doRender() {
let renderPassDescriptor = makeRenderPassDescriptor()
let commandBuffer = commandQueue.makeCommandBuffer()!
// Render Pass - render sceneView
renderer.scene = sceneView.scene
renderer.pointOfView = sceneView.pointOfView
renderer.render(atTime: 0, viewport: viewport, commandBuffer: commandBuffer, passDescriptor: renderPassDescriptor)
// Blit Pass - copy depth texture to buffer
let imageWidth = Int(textureSizeX)
let imageHeight = Int(textureSizeY)
let pixelCount = imageWidth * imageHeight
let depthImageBuffer = device.makeBuffer(length: 4 * pixelCount, options: .storageModeShared)!
let blitEncoder = commandBuffer.makeBlitCommandEncoder()!
blitEncoder.copy(from: renderPassDescriptor.depthAttachment.texture!,
sourceSlice: 0,
sourceLevel: 0,
sourceOrigin: MTLOriginMake(0, 0, 0),
sourceSize: MTLSizeMake(imageWidth, imageHeight, 1),
to: depthImageBuffer,
destinationOffset: 0,
destinationBytesPerRow: 4 * imageWidth,
destinationBytesPerImage: 4 * pixelCount,
options: .depthFromDepthStencil)
blitEncoder.endEncoding()
commandBuffer.commit()
// Wait until depth buffer copying is done.
commandBuffer.waitUntilCompleted()
}
func makeRenderPassDescriptor() -> MTLRenderPassDescriptor {
let renderPassDescriptor = MTLRenderPassDescriptor()
let frameBufferDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm_srgb, width: textureSizeX, height: textureSizeY, mipmapped: false)
frameBufferDescriptor.usage = [.renderTarget, .shaderRead]
renderPassDescriptor.colorAttachments[0].texture = device.makeTexture(descriptor: frameBufferDescriptor)!
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].storeAction = .store
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(1, 1, 1, 1.0)
let depthBufferDescriptor: MTLTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .depth32Float, width: textureSizeX, height: textureSizeY, mipmapped: false)
depthBufferDescriptor.usage = .renderTarget
renderPassDescriptor.depthAttachment.texture = device.makeTexture(descriptor: depthBufferDescriptor)
renderPassDescriptor.depthAttachment.loadAction = .clear
renderPassDescriptor.depthAttachment.storeAction = .store
return renderPassDescriptor
}
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func session(_ session: ARSession, didUpdate frame: ARFrame) {
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
}