在 SceneKit 中使用金属着色器

Using a Metal shader in SceneKit

我想使用 Metal 着色器对场景中使用的材质应用 toon/cell 着色。我要实现的着色器是在 MetalShaderShowcase 中找到的 Apple 自己的 AAPLCelShader。我对在 SceneKit 中实现着色器有点陌生,所以我做的每件事都可能完全错误。

根据我在文档中的理解,Metal 或 GL 着色器可用于修改 SceneKit 从 iOS 9 和 Xcode 7 开始的渲染。文档建议这是以与 GL 着色器相同的方式完成的。

我的方法是尝试获取 .metal 文件的路径,然后将其加载到要在 SCNMaterial shaderModifiers 属性 中使用的字符串中[String: String] 字典的形式,就像使用 GL 着色器一样(尽管我还没有让它正常工作)。

我的代码是:

var shaders = [String: String]()
let path = NSBundle.mainBundle().pathForResource("AAPLCelShader", ofType: "metal")!

do{
  let celShader = try String(contentsOfFile: path, encoding: NSUTF8StringEncoding)
  shaders[SCNShaderModifierEntryPointLightingModel] = celShader
}catch let error as NSError{
  error.description
}
scene.rootNode.enumerateChildNodesUsingBlock({ (node, stop) -> Void in
  node.geometry?.firstMaterial?.shaderModifiers = shaders
})

这是在一个测试场景中 运行,其中有一个从 .scn 文件加载的盒子几何体。文件加载工作正常,所以这不是问题。

错误来自我尝试找到路径的第二行,它给了我 "found nil while unwrapping optional value" 强制展开时预期的结果,而不是它试图加载的文件肯定在捆绑包中并且相同的代码适用于其他文件类型,而不是 .metal 文件类型。

我是不是完全错了?除了我滥用金属文件的问题外,我不明白为什么它不能访问该文件。

是否有更简单或更好的方法来实现单元格着色并最终在边缘上形成粗体轮廓?

AAPLCelShader.metal 是一个完整的顶点/rastex/片段实现,这不是 shaderModifiers 是:将被注入到已经完成的着色器中的源代码。

相反,您可以创建一个 SCNProgram, using the vertex and fragment functions in AAPLCelShader.metal. What's nice about Metal, versus GLSL, is that you can use names of Metal functions,而不是源代码字符串,这样更容易使用,并且会导致函数在运行时之前编译,这是 GLSL 不支持的。 (这仍然没有达到应有的水平,Swift 会将 Metal 函数识别为正确类型、可重构、命名空间的闭包。)当然,虽然 GLSL SCNPrograms 将转换为 Metal,但对于兼容的硬件, Metal SCNPrograms 不会为过时的设备翻译。

至于从 SceneKit 映射到 rastex 的(错误命名的 ColorInOut)成员:

float4 position [[position]];
float shine;
float3 normal_cameraspace;
float3 eye_direction_cameraspace;
float3 light_direction_cameraspace;

…很遗憾,我还没有给你答案。