使用SceneKit SCNProgram的金属着色器

11

我只需要一个在SceneKit中与SCNProgram一起工作的Metal着色器。

有人能向我展示正确的方法声明/如何挂钩吗?

let program = SCNProgram()
program.vertexFunctionName = "myVertex"
program.fragmentFunctionName = "myFragment"
material.program = program

然后是着色器

//MyShader.metal

vertex something myVertex(something)
{
    return something;
}

fragment float4 myFragment(something)
{
    return something
}

我只是在寻找最基础的例子。

2个回答

15

@lock的上面的回答很好,所以我想通过提供一个纹理的示例来扩展它,正如OP在评论中要求的那样。

以下是如何配置您的材质以利用着色器并连接自定义纹理的方式:

let program = SCNProgram()
program.fragmentFunctionName = "myFragment"
program.vertexFunctionName = "myVertex"

material.program = program

let image = UIImage(named: "diffuse")!
let imageProperty = SCNMaterialProperty(contents: image)
// The name you supply here should match the texture parameter name in the fragment shader
material.setValue(imageProperty, forKey: "diffuseTexture")

这里是修改过的着色器,它们从纹理中进行采样:

#include <metal_stdlib>

using namespace metal;

#include <SceneKit/scn_metal>

struct MyNodeBuffer {
    float4x4 modelTransform;
    float4x4 modelViewTransform;
    float4x4 normalTransform;
    float4x4 modelViewProjectionTransform;
};

typedef struct {
    float3 position [[ attribute(SCNVertexSemanticPosition) ]];
    float2 texCoords [[ attribute(SCNVertexSemanticTexcoord0) ]];
} MyVertexInput;

struct SimpleVertex
{
    float4 position [[position]];
    float2 texCoords;
};

vertex SimpleVertex myVertex(MyVertexInput in [[ stage_in ]],
                             constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                             constant MyNodeBuffer& scn_node [[buffer(1)]])
{
    SimpleVertex vert;
    vert.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
    vert.texCoords = in.texCoords;

    return vert;
}

fragment half4 myFragment(SimpleVertex in [[stage_in]],
                          texture2d<float, access::sample> diffuseTexture [[texture(0)]])
{
    constexpr sampler sampler2d(coord::normalized, filter::linear, address::repeat);
    float4 color = diffuseTexture.sample(sampler2d, in.texCoords);
    return half4(color);
}

谢谢@warrenm,这基本上就是我最终会打出来的东西。 - lock
1
@Chris 是的,您所提到的功能已打包在 SCNTechnique 类中。您可能可以依赖默认的 SceneKit shader,但最好还是编写自己的shader。 - lock
1
@Chris 当然可以。将对象的材质设置为包含“MTLTexture”,然后通过计算着色器进行修改。效果良好,性能良好(无需CPU往返),并且比解决“SCNTechnique”更容易。虽然“SCNTechnique”是屏幕空间效果(模糊、泛光等)的最佳选择。 - lock
@warrenm:问题有点儿简单,但是在设置程序的代码片段中,你如何引用金属文件? - Guig
2
虽然理论上看起来很好,但我得到的只是一个没有阴影或纹理的纯白色轮廓。你有什么想法我做错了什么吗?最近的iOS版本中可能有些变化吗? - Ash
显示剩余7条评论

15

我挑选出所有“不必要”的内容,这是最基础的部分,也是我第一个金属着色器的内容。

接下来,我会开始考虑连接其他顶点属性(颜色、法线),可能进行一些基本的光照计算。

#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>

struct MyNodeBuffer {
    float4x4 modelTransform;
    float4x4 modelViewTransform;
    float4x4 normalTransform;
    float4x4 modelViewProjectionTransform;
};

typedef struct {
    float3 position [[ attribute(SCNVertexSemanticPosition) ]];
} MyVertexInput;

struct SimpleVertex
{
    float4 position [[position]];
};


vertex SimpleVertex myVertex(MyVertexInput in [[ stage_in ]],
                             constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                             constant MyNodeBuffer& scn_node [[buffer(1)]])
{
    SimpleVertex vert;
    vert.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);

    return vert;
}

fragment half4 myFragment(SimpleVertex in [[stage_in]])
{
    half4 color;
    color = half4(1.0 ,0.0 ,0.0, 1.0);

    return color;
}

对于任何错别字表示歉意,我是在手机上编辑的......


这真的很棒,您是否有传递SCNNode材质设置的纹理的示例?可以理解目前它只会将我的对象涂成红色。 - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接