2015-11-05 112 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

相关问题