我正在尝试使用带有SCNTechnique的Metal片段着色器,根据顶点Y世界的位置来修改片段颜色。
SCNTechnique可以配置一系列渲染过程。渲染过程允许注入顶点和片段着色器。这些着色器是用Metal编写的。 Metal Shading Language Specification描述了这两个都支持哪些输入/输出。对于要渲染的每个顶点,都会调用顶点着色器。我们可以将其他信息从顶点着色器传递到片段着色器(例如3D空间中的位置,请参见MSLS section 5.2)。片段着色器最接近像素,如果存在多个“限定”该像素的三角形,则它可能被称为单个“像素”的倍数时间。 (通常)在片段着色之后,如果片段未通过深度或模板测试,则可能会将其丢弃。
这是我尝试过的。 (我希望可以弄清楚我缺乏了解的地方)。
struct VertexOut {
float4 position [[position]];
};
vertex VertexOut innerVertexShader(VertexIn in [[stage_in]]) {
VertexOut out;
out.position = in.position;
return out;
};
fragment half4 innerFragmentShader(VertexOut in [[stage_in]],
half4 color [[color(0)]]) {
half4 output;
output = color; // test to see if getting rendered color works
output.g = in.position.y; // test to see if getting y works
return output;
}
这些着色器在SCNTechnique词典中被引用。
[
"passes": [
"innerPass: [
"draw": "DRAW_NODE",
"node": "inner",
"metalVertexShader": "innerVertexShader",
"metalFragmentShader": "innerFragmentShader"
]
],
"sequence": ["innerPass"],
"symbols": [:],
"targets": [:],
]
// ...
let technique = SCNTechnique(dictionary: techniqueDictionary)
这将执行以下操作:该技术已正确实例化并附加到场景(因为它会影响渲染)。但是,似乎没有将相机变换或节点位置变换应用于顶点。而是将每个节点呈现为从(0,0,0)的位置(0,0,0)进行查看。颜色是错误的。如果我从SCNTechnique中删除了着色器,则每个渲染都将达到我的期望。
我如何利用常规的SceneKit行为(相机变换等),仅根据片段的y世界位置修改颜色输出?我希望使用在顶点着色器中获得的世界位置,需要在片段级别上发生。我已经搜索了诸如“金属基本顶点着色器”之类的内容,但没有结果。我已经看到了着色器like this,但是我坚信我应该能够依靠SceneKit渲染来进行诸如照明,PBR材料,相机变换等操作。在这一点上,我感觉就像每当我搜索一些Metal主题时,我都会结束在尚未成功将我的理解提高到另一个层次的相同网站上进行。因此,任何新/附加资源也将受到赞赏。
在过去的两个月中,我一直在从事自己的游戏项目,该项目使用SceneKit作为主要的图形框架。我已将SCNTechnique和Metal着色器用于自定义效果。尤其是后两个,让我感到头疼,因为缺少示例代码/文档/运行时反馈。因此,我曾考虑过迁移到Unity / Unreal,甚至完全取消此项目。但是由于我很固执,也因为我真的不想将Swift代码移植到C#/ C ++,所以我还没有放弃SceneKit。
过去两天一直在研究这个主题,我对顶点和片段阴影以及SceneKit如何处理这些问题的理解已显着发展。
正如@mnuages在评论中指出的那样,对于此用例,着色器修改器是一种解决方法。它们利用默认的SceneKit阴影(按OP的要求),并允许着色器代码注入。
为了弥补SceneKit文档的某些局限性,我将为其他研究该主题的人做一些说明。
有关着色器修改器如何绑定到SceneKit默认顶点/片段着色器的更多信息,请参见my answer to a related question或SceneKit's default shaders。第二个链接演示了利用着色器修改器而不是编写自己的着色器时免费获得的SceneKit渲染逻辑的范围。
[This page帮助我建立了从顶点到片段的矢量变换的不同阶段的理解(模型空间➡️世界空间➡️相机空间➡️投影空间)。
如果您想通过完全定制的着色器进行单次传递,这是一个简单的示例。它将世界y位置从顶点着色器传递到片段着色器。
// Shaders.metal file in your Xcode project
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
typedef struct {
float4x4 modelTransform;
float4x4 modelViewTransform;
} commonprofile_node;
struct VertexIn {
float3 position [[attribute(SCNVertexSemanticPosition)]];
};
struct VertexOut {
float4 fragmentPosition [[position]];
float height;
};
vertex VertexOut myVertex(
VertexIn in [[stage_in]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant commonprofile_node& scn_node [[buffer(1)]]
) {
VertexOut out;
float4 position = float4(in.position, 1.f);
out.fragmentPosition = scn_frame.viewProjectionTransform * scn_node.modelTransform * position;
// store world position for fragment shading
out.height = (scn_node.modelTransform * position).y;
return out;
}
fragment half4 myFragment(VertexOut in [[stage_in]]) {
return half4(in.height);
}
let dictionary: [String: Any] = [
"passes" : [
"y" : [
"draw" : "DRAW_SCENE",
"inputs" : [:],
"outputs" : [
"color" : "COLOR"
],
"metalVertexShader": "myVertex",
"metalFragmentShader": "myFragment",
]
],
"sequence" : ["y"],
"symbols" : [:]
]
let technique = SCNTechnique(dictionary: dictionary)
scnView.technique = technique
您可以将此渲染过程与其他过程结合使用(请参阅SCNTechnique。