如何在 SceneKit 中使用 SCNBufferBindingBlock?

Posted

技术标签:

【中文标题】如何在 SceneKit 中使用 SCNBufferBindingBlock?【英文标题】:How to use SCNBufferBindingBlock in SceneKit? 【发布时间】:2018-10-27 11:34:57 【问题描述】:

我正在查看带有 SCNBufferBindingBlock 回调的 SceneKit 的句柄绑定方法,如下所述:

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

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

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

    

在我看来,我可以在 SCNNode 上使用 *.metal 着色器,而无需经历 SCNTechniques 的麻烦......任何接受者?

【问题讨论】:

【参考方案1】:

只是发布这个以防其他人来这里寻找一个简洁的例子。以下是 SCNProgramhandleBinding() 方法如何与 Metal 一起使用:

首先在你的.metal着色器文件中定义一个数据结构:

struct MyShaderUniforms 
    float myFloatParam;
    float2 myFloat2Param;
;

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

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

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

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 属性然后包含用户定义的数据类型MyShaderUniforms,然后可以使用更新的值写入。

【讨论】:

【参考方案2】:

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

这里是 SCNProgram 课程中Developer Documentation 的链接。

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

SCNTechniqueclass 专门用于使用带有自定义 Metal 或 OpenGL 着色器的附加绘图通道对 SceneKit 的场景渲染进行后期处理。使用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 帖子。

【讨论】:

以上是关于如何在 SceneKit 中使用 SCNBufferBindingBlock?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建要在 SceneKit 中使用的 DAE 文件?

如何使用 Xcode 10 在 SceneKit 编辑器中旋转对象

如何在 SceneKit 中使用 OBJ 文件或 CTM 文件而不是 DAE 文件?

如何在 iOS SceneKit 中使用具有不同场景图形的 DAE 文件

如何同时使用 SwiftUI 和 SceneKit?

如何在 SceneKit 中移动旋转的 SCNNode?