2015-11-05 45 views
5

我想使用金屬計算着色器來計算一些位置,然後將它們饋送到金屬着色器中。聽起來很簡單,但我無法將MTLBuffer數據導入基於金屬的SCNProgram中。將金屬緩衝區傳遞給SceneKit着色器

計算內核如下所示,在這個人爲設計的例子中,它採用了三個3D矢量(在兩個緩衝區中)。

kernel void doSimple(const device float3 *inVector [[ buffer(0) ]], 
        device float3 *outVector [[ buffer(1) ]], 
        uint id [[ thread_position_in_grid ]]) { 

    float yDisplacement = 0; 
    . . . . 

    outVector[id] = float3(
     inVector[id].x, 
     inVector[id].y + yDisplacement, 
     inVector[id].z); 
} 

這個內核功能運行在我SCNSceneRendererDelegate- renderer:willRenderScene:atTime:方法中的每個幀。有兩個緩衝區,並在每幀之後切換。

緩衝區創建如下;

func setupBuffers() { 
    positions = [vector_float3(0,0,0), vector_float3(1,0,0), vector_float3(2,0,0)] 

    let bufferSize = sizeof(vector_float3) * positions.count 
    //copy same data into two different buffers for initialisation 
    buffer1 = device.newBufferWithBytes(&positions, length: bufferSize, options: .OptionCPUCacheModeDefault) 
    buffer2 = device.newBufferWithBytes(&positions, length: bufferSize, options: .OptionCPUCacheModeDefault) 
} 

並且計算着色器使用以下函數(在willRenderScene函數中)運行;

let computeCommandBuffer = commandQueue.commandBuffer() 
    let computeCommandEncoder = computeCommandBuffer.computeCommandEncoder() 

    computeCommandEncoder.setComputePipelineState(pipelineState) 
    computeCommandEncoder.setBuffer(buffer1, offset: 0, atIndex: 0) 
    computeCommandEncoder.setBuffer(buffer2, offset: 0, atIndex: 1) 

    computeCommandEncoder.dispatchThreadgroups(numThreadgroups, threadsPerThreadgroup: threadsPerGroup) 
    computeCommandEncoder.endEncoding() 
    computeCommandBuffer.commit() 
    computeCommandBuffer.waitUntilCompleted() 

    let bufferSize = positions.count*sizeof(vector_float3) 
    var data = NSData(bytesNoCopy: buffer2.contents(), length: bufferSize, freeWhenDone: false) 
    var resultArray = [vector_float3](count: positions.count, repeatedValue: vector_float3(0,0,0)) 
    data.getBytes(&resultArray, length:bufferSize) 

    for outPos in resultArray { 
     print(outPos.x, ", ", outPos.y, ", ", outPos.z) 
    } 

這是有效的,我可以看到我的計算着色器正在更新數組中每個向量的y座標。

這個場景由三個均勻間隔的球體組成。頂點着色器只需要在計算着色器中計算出的位置,並將其添加到每個頂點位置(無論如何是y組件)。我給每個球體一個索引,頂點着色器使用這個索引從我的計算數組中拉出適當的位置。

金屬頂點函數如下所示,它由SCNProgram引用並設置爲每個球體的材質。

vertex SimpleVertex simpleVertex(SimpleVertexInput in [[ stage_in ]], 
          constant SCNSceneBuffer& scn_frame [[buffer(0)]], 
          constant MyNodeBuffer& scn_node [[buffer(1)]], 
          constant MyPositions &myPos [[buffer(2)]], 
          constant uint &index [[buffer(3)]] 
          ) 
{ 

    SimpleVertex vert; 
    float3 posOffset = myPos.positions[index]; 
    float3 pos = float3(in.position.x, 
         in.position.y + posOffset.y, 
         in.position.z); 

    vert.position = scn_node.modelViewProjectionTransform * float4(pos,1.0); 

    return vert; 
} 

MyPositions是含有float3s陣列的簡單的結構。

struct MyPositions 
{ 
    float3 positions[3]; 
}; 

我有將數據傳遞到使用如下所示的各球體的材料的setValue方法(在willRenderScene方法還完成)的頂點着色器沒有問題。一切都按預期工作(三個球體向上移動)。

var i0:UInt32 = 0 
    let index0 = NSData(bytes: &i0, length: sizeof(UInt32)) 
    sphere1Mat.setValue(index0, forKey: "index") 
    sphere1Mat.setValue(data, forKey: "myPos") 

這需要將數據從GPU拷貝到CPU到GPU和真的東西,我寧願避免。所以我的問題是... 如何將MTLBuffer傳遞給SCNProgram?

試過在willRenderScene以下,但什麼也沒得到,但EXEC_BAD...

let renderCommandEncoder = renderer.currentRenderCommandEncoder! 
renderCommandEncoder.setVertexBuffer(buffer2, offset: 0, atIndex: 2) 
renderCommandEncoder.endEncoding() 

完全example is over on GitHub

感謝您的閱讀,一直在努力與這一個。解決方法是使用MTLTexture代替MTLBuffer,因爲我已經能夠通過漫反射墊道具將這些傳遞到SCNProgram中。

+1

仍然對在知道這個答案的情況下,如果可能的話......目前我已經通過創建一個由'MTLBuffer'支持的'SCNGeometrySource'來存儲我的頂點數據。渲染後的頂點數據直接由計算着色器修改。沒有自定義的頂點着色器,因此不需要傳入「MTLBuffer」。 – lock

回答

-1

只是逐步切換緩衝區的綁定。

step1 computeCommandEncoder。setBuffer(緩衝器1,偏移量:0,atIndex:) computeCommandEncoder.setBuffer(緩衝器2,偏移:0,atIndex:)

步驟2 computeCommandEncoder.setBuffer(緩衝器1,偏移量:0,atIndex:) computeCommandEncoder.setBuffer(緩衝器2,偏移:0,atIndex:)

步驟3 computeCommandEncoder.setBuffer(緩衝器1,偏移量:0,atIndex:) computeCommandEncoder.setBuffer(緩衝器2,偏移:0,atIndex:)

等等...

的出緩衝器成爲緩衝反之亦然新...

+0

這就是我爲我的大多數計算功能所做的。但在這種情況下,計算函數可以正常工作,但問題是要從計算着色器中獲取輸出並將其傳遞給渲染函數。使用SceneKit,我們有支持自定義着色器的'SCNProgram'抽象,不幸的是,它似乎缺少一種傳遞類似'MTLBuffer'的方法。如果我在沒有SceneKit的情況下使用Metal,將會是一個完整的非問題。謝謝 – lock

+0

對不起,我應該更仔細地閱讀這個問題。 – Oliver

+0

您在源代碼中寫道: //不起作用 let renderCommandEncoder = renderer.currentRenderCommandEncoder! renderCommandEncoder.setVertexBuffer(buffer2,offset:0,atIndex:2) renderCommandEncoder.endEncoding() 您是否還在renderEncoder中設置了另外兩個緩衝區(0和1)?或者你是否只爲計算編碼器完成了這個工作? 我無法嘗試這個,因爲我沒有適當的iOS硬件。我使用OS X. – Oliver

相關問題