我正在尝试使用带有SCNTechnique的金属碎片着色器来修改基于顶点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,1)观看。颜色不对。如果我从SCNTechnique中移除着色器,所有渲染都会像我预期的那样渲染。
如何利用常规的SceneKit行为(相机变换等),并且仅根据碎片的世界位置修改颜色输出?我预计这需要在片段级别上发生,使用在顶点着色器中以某种方式获得的世界位置。我已经搜索了像"Metal basic vertex shader“这样的东西,但什么也没有找到。我已经看到了着色器like this,但我相信我应该能够依靠SceneKit渲染的东西,如照明,PBR材质,相机变换,等等。在这一点上,我觉得每当我搜索一些金属主题,我结束了相同的网站,但尚未成功地将我的理解提升到一个新的水平。因此,任何新的/额外的资源也是值得感谢的。
背景
在过去的两个月里,我一直在做我自己的游戏项目,它使用SceneKit作为主要的图形框架。我已经转向SCNTechnique和金属着色器来实现自定义效果。后两个尤其让我头疼,都是因为缺乏示例代码/文档/运行时反馈。由于这个原因,我已经考虑转移到Unity/虚幻,甚至完全取消这个项目。但因为我很固执,也因为我真的不想把我的Swift代码移植到C#/C++,所以我还没有放弃SceneKit。
发布于 2020-04-15 23:31:15
在过去的几天里研究了这个话题,我对顶点和片段着色以及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)。


https://stackoverflow.com/questions/61171947
复制相似问题