【问题标题】:Use SCNTechnique with Metal Shaders in Swift Playgrounds在 Swift Playgrounds 中使用 SCNTechnique 和金属着色器
【发布时间】:2023-03-15 09:02:02
【问题描述】:

我想在 SCNView 上的 Swift Playgrounds 中使用带有金属着色器的 SCNTechnique。因此我尝试使用以下代码编译我的着色器:

guard let metalDevice = sceneView.device else {
    return
}

if let path = Bundle.main.path(forResource: "distortTechnique", ofType: "metal") {
    do {
        let contents = try String(contentsOf: URL(fileURLWithPath: path))
        do {
            try metalDevice.makeLibrary(source: contents, options: nil)
        } catch { error
            print(error)
        }
    } catch {
        // contents could not be loaded
    }
}

文件已加载,但出现以下编译器错误:

Error Domain=MTLLibraryErrorDomain Code=3 "Compilation failed: 

program_source:11:10: fatal error: 'SceneKit/scn_metal' file not found
#include <SceneKit/scn_metal>
         ^
" UserInfo={NSLocalizedDescription=Compilation failed: 

program_source:11:10: fatal error: 'SceneKit/scn_metal' file not found
#include <SceneKit/scn_metal>
         ^
}

所以似乎找不到 »scn_metal« 库。 知道如何解决这个问题吗? 相同的设置在 iOS 应用项目中运行得非常好。

这是着色器代码:

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

struct custom_vertex_t
{
    float4 position [[attribute(SCNVertexSemanticPosition)]];
};

constexpr sampler s = sampler(coord::normalized,
                              address::repeat,
                              filter::linear);

struct out_vertex_t
{
    float4 position [[position]];
    float2 uv;
    float time;
    float random;
};

vertex out_vertex_t pass_through_vertex(custom_vertex_t in [[stage_in]],
                                        constant SCNSceneBuffer& scn_frame [[buffer(0)]])
{
    out_vertex_t out;
    out.position = in.position;
    out.uv = float2((in.position.x + 1.0) * 0.5 , (in.position.y + 1.0) * -0.5);
    out.time = scn_frame.time;

    return out;
};

fragment half4 pass_through_fragment(out_vertex_t vert [[stage_in]],
                            texture2d<float, access::sample> colorSampler [[texture(0)]])
{

    float4 fragment_color = colorSampler.sample( s, vert.uv);
    return half4(fragment_color);
};

fragment half4 distort_fragment(out_vertex_t vert [[stage_in]],
                                              texture2d<float, access::sample> colorSampler [[texture(0)]])
{
    float multiplier = sin(vert.time * 0.5);
    float effectRadius = 0.75;
    float effectAmount = 360. * 2.;
    float effectAngle = multiplier * effectAmount;
    effectAngle = effectAngle * M_PI_F / 180;

    float2 resolution = float2(colorSampler.get_width(), colorSampler.get_height());
    float2 center = float2(0.5, 0.5);//iMouse.xy / iResolution.xy;

    float2 uv = vert.position.xy / resolution - center;
    float len = length(uv * float2(resolution.x / resolution.y, 1.));
    float angle = atan2(uv.y, uv.x) + effectAngle * smoothstep(effectRadius, 0., len);
    float radius = length(uv);

    float4 fragment_color = colorSampler.sample(s, float2(radius * cos(angle), radius * sin(angle)) + center);

    return half4(fragment_color);

};

附上一张操场的图片。

谢谢!

【问题讨论】:

    标签: shader scenekit swift-playground metal scntechnique


    【解决方案1】:

    我最终通过预编译实际应用程序项目的金属库,然后在 Playground 中使用结果 default.metallib 和包含的着色器函数来让它工作。

    这似乎是必要的,因为顶点和片段的 SCNTechnique 文档 »这些函数必须存在于应用程序的默认 Metal 库中。«

    有点老套,但适合我的目的。

    具体步骤: - 为选定的平台创建一个新的应用程序项目 - 使用所有定义设置 SCNTechnique plist - 添加带有着色器代码的 .metal 文件 - 归档项目 - 打开归档包并将 default.metallib 文件复制到您的 Playground Resources 文件夹中 - 现在只需在 SCNView 上设置您的技术 – 利润。

    再次感谢奥利弗的帮助!

    【讨论】:

    • 很高兴你能成功。遗憾的是,您无法在操场上编辑金属着色器。正如您所说,它必须是 SCNTechnique 要求。 SCNProgram 从字符串编译着色器运行良好。关于您的代码的一个小建议:您不需要将 do/catch 块嵌套在另一个块中。您可以在单个 do/ catch 块中调用多个 try。第一个失败的将打破 do 块并使用适当的错误调用 catch 处理程序。
    • 是的,这绝对是一种耻辱。也许有一些方法可以在运行时在默认库中注入代码。当我有更多时间时会再试一次。并感谢您对 do/catch 块的提醒。我在摆弄的时候也注意到了这一点。
    【解决方案2】:

    通过在金属文件的顶部包含 SCNMetalDefines 的内容,我能够让金属着色器在操场上作为 SCNProgram 工作。我不确定scn_metal 是否比这些定义更多。不过,这拥有我需要的顶点/片段管道的一切。你能发布你的着色器的代码吗?或者至少,您能告诉我们scn_metal 中的着色器引用了什么吗?

    #include <metal_stdlib>
    using namespace metal;
    
    #ifndef __SCNMetalDefines__
    #define __SCNMetalDefines__
    
    enum {
    SCNVertexSemanticPosition,
    SCNVertexSemanticNormal,
    SCNVertexSemanticTangent,
    SCNVertexSemanticColor,
    SCNVertexSemanticBoneIndices,
    SCNVertexSemanticBoneWeights,
    SCNVertexSemanticTexcoord0,
    SCNVertexSemanticTexcoord1,
    SCNVertexSemanticTexcoord2,
    SCNVertexSemanticTexcoord3
    };
    
    // This structure hold all the informations that are constant through a render pass
    // In a shader modifier, it is given both in vertex and fragment stage through an argument named "scn_frame".
    struct SCNSceneBuffer {
    float4x4    viewTransform;
    float4x4    inverseViewTransform; // transform from view space to world space
    float4x4    projectionTransform;
    float4x4    viewProjectionTransform;
    float4x4    viewToCubeTransform; // transform from view space to cube texture space (canonical Y Up space)
    float4      ambientLightingColor;
    float4        fogColor;
    float3        fogParameters; // x:-1/(end-start) y:1-start*x z:exp
    float2      inverseResolution;
    float       time;
    float       sinTime;
    float       cosTime;
    float       random01;
    };
    
    // In custom shaders or in shader modifiers, you also have access to node relative information.
    // This is done using an argument named "scn_node", which must be a struct with only the necessary fields
    // among the following list:
    //
    // float4x4 modelTransform;
    // float4x4 inverseModelTransform;
    // float4x4 modelViewTransform;
    // float4x4 inverseModelViewTransform;
    // float4x4 normalTransform; // This is the inverseTransposeModelViewTransform, need for normal transformation
    // float4x4 modelViewProjectionTransform;
    // float4x4 inverseModelViewProjectionTransform;
    // float2x3 boundingBox;
    // float2x3 worldBoundingBox;
    
    #endif /* defined(__SCNMetalDefines__) */
    

    【讨论】:

    • 谢谢奥利弗!我找到了这个文件:github.com/theos/sdks/blob/master/iPhoneOS11.2.sdk/System/… 并添加了 SCNMetalDefines。它现在正在编译,但仍然无法正常工作。场景现在只是保持粉红色。
    • 你能把操场的其余部分贴出来吗?我认为关于 Playground 的一个棘手的事情是与完整项目相比调试有限。您没有使用 GPU 帧捕获等选项。
    • 当然!这是链接:dropbox.com/s/gpe7jzs882scl3b/…
    • 我必须补充一点,最终目标是在 iPad 上使用整个东西,但我创建了 mac 游乐场以便于调试。
    猜你喜欢
    • 2018-12-02
    • 2015-09-05
    • 1970-01-01
    • 1970-01-01
    • 2020-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-29
    • 2019-01-24
    相关资源
    最近更新 更多