如何在SceneKit中使用SCNBufferBindingBlock?

时间:2018-05-17 09:00:26

标签: swift shader scenekit metal

我正在查看SceneKit的句柄绑定方法,并按照此处所述回调SCNBufferBindingBlock:

https://developer.apple.com/documentation/scenekit/scnbufferbindingblock

有没有人举例说明这是如何运作的?

    let program = SCNProgram()
    program.handleBinding(ofBufferNamed: "", frequency: .perFrame) { (steam, theNode, theShadable, theRenderer) in

    }

对我来说,就像我可以在SCNNode上使用* .metal着色器而不必经历SCNTechniques的麻烦....任何接受者?

2 个答案:

答案 0 :(得分:1)

.handleBinding(ofBufferNamed:frequency:handler:)方法为SceneKit注册一个块,以便在渲染时调用Metal缓冲区到着色器程序。此方法只能与基于Metal或OpenGL着色语言的程序一起使用。 SCNProgram对象有助于执行此自定义呈现。程序对象包含顶点着色器和片段着色器。使用程序对象完全取代了SceneKit的渲染。着色器从SceneKit获取输入,并负责您想要生成的所有变换,光照和着色效果。使用.handleBinding()方法将块与Metal Shader程序相关联,以处理该着色器中使用的缓冲区的设置。

  

这是SCNProgram类Developer Documentation的链接。

此外,您还需要一个实例方法writeBytes(_:count:),它将所有必要的数据字节复制到底层的Metal缓冲区中以供着色器使用。

SCNTechnique专门用于后处理SceneKit使用自定义Metal或OpenGL着色器的附加绘图过程渲染场景。使用SCNTechnique,您可以创建颜色分级或置换,运动模糊和渲染环境遮挡以及其他渲染过程等效果。

以下是第一段代码摘录如何正确使用.handleBinding()方法:

func useTheseAPIs(shadable: SCNShadable,
                  bufferStream: SCNBufferStream
                  voidPtr: UnsafeMutableRawPointer,
                  bindingBlock: @escaping SCNBindingBlock, 
                  bufferFrequency: SCNBufferFrequency,
                  bufferBindingBlock: @escaping SCNBufferBindingBlock,
                  program: SCNProgram) {

    bufferStream.writeBytes(voidPtr, count: 4) 

    shadable.handleBinding!(ofSymbol: "symbol", handler: bindingBlock)
    shadable.handleUnbinding!(ofSymbol: "symbol", handler: bindingBlock)

    program.handleBinding(ofBufferNamed: "pass", 
                          frequency: bufferFrequency,
                          handler: bufferBindingBlock)
}

这是第二段代码的摘录:

let program = SCNProgram()
program.delegate = self as? SCNProgramDelegate
program.vertexShader = NextLevelGLContextYUVVertexShader
program.fragmentShader = NextLevelGLContextYUVFragmentShader

program.setSemantic(
    SCNGeometrySource.Semantic.vertex.rawValue, 
    forSymbol: NextLevelGLContextAttributeVertex, 
    options: nil)

program.setSemantic(
    SCNGeometrySource.Semantic.texcoord.rawValue, 
    forSymbol: NextLevelGLContextAttributeTextureCoord, 
    options: nil)

if let material = self._material {
    material.program = program

    material.handleBinding(ofSymbol: NextLevelGLContextUniformTextureSamplerY, handler: { 
        (programId: UInt32, location: UInt32, node: SCNNode?, renderer: SCNRenderer) in
            glUniform1i(GLint(location), 0);
    }) 
    material.handleBinding(ofSymbol: NextLevelGLContextUniformTextureSamplerUV, handler: { 
        (programId: UInt32, location: UInt32, node: SCNNode?, renderer: SCNRenderer) in
            glUniform1i(GLint(location), 1);
    })
}
  

另外,请查看Simulating refraction in SceneKit SO帖子。

答案 1 :(得分:1)

只需发布此信息,以防其他人来这里寻找简洁的示例。这是SCNProgram的{​​{1}}方法如何与 Metal 一起使用的方法:

首先在handleBinding()着色器文件中定义一个数据结构:

.metal

然后将其作为参数传递给着色器函数:

struct MyShaderUniforms {
    float myFloatParam;
    float2 myFloat2Param;
};

接下来,在您的 Swift 文件中定义相同的数据结构:

fragment half4 myFragmentFunction(MyVertex vertexIn [[stage_in]], 
                                  constant MyShaderUniforms& shaderUniforms [[buffer(0)]]) {
    ...
}

现在创建此数据结构的实例,更改其值并定义struct MyShaderUniforms { var myFloatParam: Float = 1.0 var myFloat2Param = simd_float2() }

SCNBufferBindingBlock

在这里,传递给var myUniforms = MyShaderUniforms() myUniforms.myFloatParam = 3.0 ... program.handleBinding(ofBufferNamed: "shaderUniforms", frequency: .perFrame) { (bufferStream, node, shadable, renderer) in bufferStream.writeBytes(&myUniforms, count: MemoryLayout<MyShaderUniforms>.stride) } 的字符串与片段函数中的参数名称相对应。然后,该块的ofBufferNamed:属性包含用户定义的数据类型bufferStream,然后可以使用更新后的值将其写入。