我想使用金屬計算着色器來計算一些位置,然後將它們饋送到金屬着色器中。聽起來很簡單,但我無法將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()
感謝您的閱讀,一直在努力與這一個。解決方法是使用MTLTexture代替MTLBuffer,因爲我已經能夠通過漫反射墊道具將這些傳遞到SCNProgram中。
仍然對在知道這個答案的情況下,如果可能的話......目前我已經通過創建一個由'MTLBuffer'支持的'SCNGeometrySource'來存儲我的頂點數據。渲染後的頂點數據直接由計算着色器修改。沒有自定義的頂點着色器,因此不需要傳入「MTLBuffer」。 – lock