2016-11-07 4 views
2

SceneKitを使用して拡張現実感アプリを作成しようとしていますが、SCNSceneRenderer's unprojectPointメソッドを使用して2Dピクセルとデプスが与えられていると、現在のレンダリングフレームから正確な3Dポイントが必要です。これにはx、y、zが必要です。ここで、xとyはピクセル座標で、通常zはそのフレームでデプスバッファから読み取られた値です。この作品SceneKitメタルデプスバッファ

func renderer(_ renderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: TimeInterval) { 
    renderDepthFrame() 
} 

func renderDepthFrame(){ 

    // setup our viewport 
    let viewport: CGRect = CGRect(x: 0, y: 0, width: Double(SettingsModel.model.width), height: Double(SettingsModel.model.height)) 

    // depth pass descriptor 
    let renderPassDescriptor = MTLRenderPassDescriptor() 

    let depthDescriptor: MTLTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: MTLPixelFormat.depth32Float, width: Int(SettingsModel.model.width), height: Int(SettingsModel.model.height), mipmapped: false) 
    let depthTex = scnView!.device!.makeTexture(descriptor: depthDescriptor) 
    depthTex.label = "Depth Texture" 
    renderPassDescriptor.depthAttachment.texture = depthTex 
    renderPassDescriptor.depthAttachment.loadAction = .clear 
    renderPassDescriptor.depthAttachment.clearDepth = 1.0 
    renderPassDescriptor.depthAttachment.storeAction = .store 



    let commandBuffer = commandQueue.makeCommandBuffer() 

    scnRenderer.scene = scene 
    scnRenderer.pointOfView = scnView.pointOfView! 

    scnRenderer!.render(atTime: 0, viewport: viewport, commandBuffer: commandBuffer, passDescriptor: renderPassDescriptor) 


    // setup our depth buffer so the cpu can access it 
    let depthImageBuffer: MTLBuffer = scnView!.device!.makeBuffer(length: depthTex.width * depthTex.height*4, options: .storageModeShared) 
    depthImageBuffer.label = "Depth Buffer" 
    let blitCommandEncoder: MTLBlitCommandEncoder = commandBuffer.makeBlitCommandEncoder() 
    blitCommandEncoder.copy(from: renderPassDescriptor.depthAttachment.texture!, sourceSlice: 0, sourceLevel: 0, sourceOrigin: MTLOriginMake(0, 0, 0), sourceSize: MTLSizeMake(Int(SettingsModel.model.width), Int(SettingsModel.model.height), 1), to: depthImageBuffer, destinationOffset: 0, destinationBytesPerRow: 4*Int(SettingsModel.model.width), destinationBytesPerImage: 4*Int(SettingsModel.model.width)*Int(SettingsModel.model.height)) 
    blitCommandEncoder.endEncoding() 

    commandBuffer.addCompletedHandler({(buffer) -> Void in 
     let rawPointer: UnsafeMutableRawPointer = UnsafeMutableRawPointer(mutating: depthImageBuffer.contents()) 
     let typedPointer: UnsafeMutablePointer<Float> = rawPointer.assumingMemoryBound(to: Float.self) 
     self.currentMap = Array(UnsafeBufferPointer(start: typedPointer, count: Int(SettingsModel.model.width)*Int(SettingsModel.model.height))) 

    }) 

    commandBuffer.commit() 

} 

SCNViewのデリゲートは、奥行きフレームをレンダリングするために、このメソッドを持っています。私は0と1の間の深さの値を取得します。問題は、同じSCNSceneとSCNCameraを使用しているにもかかわらず、最初のパスと同じ縮尺で表示されないため、unprojectPointで使用できないということです。

私の質問:

  1. 別々SCNRendererと、余分なパスを行うことなく、SceneKit SCNViewのメインパスから直接、深度値を取得する方法はありますか?

  2. なぜ私のパスの深度の値はヒットテストをしてから投げ出しても得られない値なのですか?私のパスからの深さの値は0.78から0.94になります。ヒットテストの深度値は0.89から0.97までの範囲で、奇妙なことに、PythonでレンダリングしたときのシーンのOpenGLデプス値と一致します。

これはビューポートの違いで、SceneKitはOpenGLのように-1から1までの奥行き値をスケーリングする何かをしています。

EDIT:あなたが疑問に思っている場合は、直接hitTestメソッドを使用することはできません。私が達成しようとしているものは遅すぎます。

答えて

1

回避策として、OpenGL ESに切り替え、デプス値をRGBAレンダバッファSCNShadableにパックするフラグメントシェーダを追加してデプスバッファを読み込みました。 http://concord-consortium.github.io/lab/experiments/webgl-gpgpu/webgl.html

私はそれは、OpenGL ESデバイスやWebGLの上でかなり頻繁にシャドウマッピングに使用され、これは有効なアプローチであることを理解するが、これは私にはハック感じ、私は持つべきではない。

は、詳細はこちらを参照してください。これをする。誰かがメタルのビューポート変換を理解できるなら、私はまだ別の答えに興味があるでしょう。