如何在 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】:只是发布这个以防其他人来这里寻找一个简洁的例子。以下是 SCNProgram
的 handleBinding()
方法如何与 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 缓冲区中以供着色器使用。
SCNTechnique
class 专门用于使用带有自定义 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?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Xcode 10 在 SceneKit 编辑器中旋转对象
如何在 SceneKit 中使用 OBJ 文件或 CTM 文件而不是 DAE 文件?