" SceneKit:错误,丢失缓冲区[-1/2]"试图将制服传递给金属着色器时

时间:2016-03-31 23:02:39

标签: ios swift shader scenekit metal

我已将SCNProgram附加到SceneKit的几何体上,并且我试图将制服传递给片段着色器。在我的简单代码片段中,我只是将输出颜色作为一个统一体传递给片段着色器,它将其作为输出值返回。

我已经测试了着色器并且它们可以工作,因为我可以在顶点着色器中成功旋转对象,或者在片段着色器中以不同颜色绘制对象等等...但是问题是我通过制服。这是我的片段着色器:

struct Uniforms
{
    float4 color;
};


fragment float4 myFragment(MyVertexOutput in [[ stage_in ]],
                           constant Uniforms& uniforms [[buffer(2)]])
{
    return uniforms.color;
}

这就是我尝试在SceneKit + Swift代码中传递制服的方法:

SCNTransaction.begin()
cube.geometry?.setValue(NSValue(SCNVector4:SCNVector4(0.0,1.0,0.0,1.0)), forKey: "uniforms.color")
SCNTransaction.commit()

但是我的对象(它是一个立方体)甚至没有绘制(它是黑色的),我收到了这个错误:

  

2016-04-01 01:00:34.485 Shaded Cube [30266:12687154] SceneKit:错误,缺少缓冲区[-1/0]

修改 我试图按照@lock的建议,但我仍然得到同样的错误。这是完整的项目存储库:https://github.com/ramy89/Shaded-Cube.git

1 个答案:

答案 0 :(得分:3)

你的着色器代码看起来很好,你穿制服的方式接近于工作。

着色器制服的名称必须与setValue中使用的名称相匹配。在这种情况下,您的Metal变量名称为uniforms,您在setValue中使用的值为uniforms.color。这些不匹配,因此您看到的错误。建议更改快速代码,只需在uniforms调用中使用setValue

接下来,您需要确保传入的数据,因为setValue的值在Metal和Swift中都是相同的。 SCNVector4是四个64位双精度(CGFloats)的结构,而Metal的float4是四个32位浮点数的结构。该文档似乎表明您可以将SCNVector作为NSValue传递,但我从来没有让它工作。

在你的swift代码中,我创建了一个包含你想要传入的制服的结构。vector_float4结构上没有很多文档,但它与金属float4结构相匹配它是四个32位浮点数。

struct Uniforms {
    var color:vector_float4
}

将其作为setValue传递到NSData函数。

let myColor = vector_float4(0,1,0,1)
var myUniforms = Uniforms(color:myColor)
var myData = NSData(bytes:&myUniforms, length:sizeof(Uniforms))
cube.geometry?.setValue(myData, forKey: "uniforms")

我不确定您是否需要SCNTransaction来电,过去我不需要它们,而且性能成本也很高。

<强>更新

在查看Ramy的代码之后,当应用于几何体时,似乎存在setValue和SCNPrograms的问题。我不能告诉你原因,但是将自定义着色器设置为材料似乎可以解决这个问题,例如;

func makeShaders()
{
    let program = SCNProgram()
    program.vertexFunctionName = "myVertex"
    program.fragmentFunctionName = "myFragment"
    //cube.geometry?.program = program
    cube.geometry?.firstMaterial?.program = program

    var uniforms = Uniforms(color: vector_float4(0.0,1.0,0.0,1.0))
    let uniformsData = NSData(bytes: &uniforms, length: sizeof(Uniforms))
    //cube.geometry?.setValue(uniformsData, forKey: "uniforms")
    cube.geometry?.firstMaterial?.setValue(uniformsData, forKey: "uniforms")
}