如何在SCNTechnique中引用任意id <mtltexture>?

时间:2019-04-23 13:33:17

标签: 3d shader scenekit metal

TL; DR:

如何引用“不在磁盘上” sampler2D符号并将其传递给SCNTechnique?如果我从捆绑中引用图像,则我的技术有效,但如果不引用,我将找不到将现有id<MTLTexture>传递到我的技术已设置的采样器符号的方法。

我有一个有效的工作SCNTechnique,它使用自定义的sampler2D符号作为金属碎片通道的输入。我正在尝试传入从硬件传感器获得的外部(不是来自Scenekit)id<MTLTexture>作为后期处理过程中的输入。

遵循SCNShadable文档,声明一个id<MTLTexture>可以通过设置了适当内容的SCNMaterialProperty作为着色器输入传递。此100%可以在着色器修改器遍历中使用-但对SCNTecnique无效!

let texture = CVMetalTextureGetTexture(textureRef!)

if self.material == nil
{
    self.material = SCNMaterialProperty(contents:texture)
}
else
{
    self.material?.contents = texture
}

self.currentTechnique?.setObject(self.material, forKeyedSubscript: "myTextureSamplerSymbol" as NSCopying)

对于SCNTechnique,我收到错误日志,指出“没有存储纹理”,并且Metal GPU帧捕获指示为采样器设置了默认的4x4像素白色纹理(大概来自SCNTecnique ?)。但是,我已经能够验证我的自定义id<MTLTexture>是否有效并且调试器中是否包含内容-其格式,宽度,高度和内容均符合预期,我似乎无法引用任意纹理进入场景工具箱技术可以正确通过。

如果我在SCNTechnique plist文件中声明我的符号以引用如下图像:

<dict>
    <key>type</key>
    <string>sampler2D</string>
    <key>image</key>
    <string>star.png</string>
</dict>

我的通行证输入如下:

<dict>
    <key>colorSampler</key>
    <string>COLOR</string>
    <key>depthSampler</key>
    <string>DEPTH</string>
    <key> myTextureSampler</key>
    <dict>
        <key>target</key>
        <string> myTextureSamplerSymbol </string>
    </dict>
</dict>

然后我的通行证开始工作,并引用star.png纹理。

有人能像这样工作吗?

谢谢。

1 个答案:

答案 0 :(得分:1)

可以肯定的是我能做到这一点。

快速代码以设置MTLTexture并将其设置为SCNTechnique

let tech:SCNTechnique = getTechnique()

let textureLoader = MTKTextureLoader(device: MTLCreateSystemDefaultDevice()!)
let filePath = Bundle.main.url(forResource: "gradient", withExtension: "png")!

do {
    let gradTexture: MTLTexture = try textureLoader.newTexture(URL: filePath, options: nil)
    let matPropTexture = SCNMaterialProperty(contents: gradTexture)
    tech.setObject(matPropTexture, forKeyedSubscript: "myTexture" as NSCopying)
} catch {
    print("Unexpected error: \(error).")
}

scnView.technique = tech

gradient.png是256 x 1px的颜色渐变(蓝色->绿色->红色)图像,用于将单个值映射为伪色。 (例如enter image description here

这是我的plist中的传球字典的完整技术传球定义。

<key>mix_outline</key>
<dict>
    <key>draw</key>
    <string>DRAW_QUAD</string>
    <key>metalVertexShader</key>
    <string>pass_through_vertex</string>
    <key>metalFragmentShader</key>
    <string>mix_outline_fragment</string>
    <key>inputs</key>
    <dict>
        <key>colorSampler</key>
        <string>color_scene</string>
        <key>depthSampler</key>
        <string>depth_outline</string>
        <key>myTextureSampler</key>
        <string>myTexture</string>
    </dict>
    <key>outputs</key>
    <dict>
        <key>color</key>
        <string>COLOR</string>
    </dict>
</dict>

myTexture也需要在技术plist的符号部分中定义。

<key>symbols</key>
<dict>
    <key>myTexture</key>
    <dict>
        <key>type</key>
        <string>sampler2D</string>
    </dict>
</dict>

当不包含此符号块时,我也看到“通行证没有存储输入myTextureSampler的错误”消息。 这可能是您的问题吗?

最后是片段着色器定义。

fragment half4 mix_outline_fragment(out_vertex_t vert [[stage_in]],
                                    texture2d<float, access::sample> colorSampler [[texture(0)]],
                                    texture2d<float, access::sample> depthSampler [[texture(1)]],
                                    texture2d<float, access::sample> myTextureSampler [[texture(2)]])
{
    float4 myTextureColor = myTextureSampler.read(uint2(55, 0));
    float4 outline = depthSampler.sample( s, vert.uv);
    float4 scene_color = colorSampler.sample( s, vert.uv);
    // float4 fragment_color = mix(scene_color, float4(0.0, 0.0, 0.0, 0.0), outline.r);
    float4 fragment_color = mix(scene_color, myTextureColor, outline.r);
    return half4(fragment_color);
}

我还没有包括此技术中涉及的其他几个步骤;只是为了提供一些上下文,它在一个过程中渲染了场景,并在另一个过程中与Sobel运算符一起使用了输出深度缓冲区,以将边缘生成到depthSampler纹理中。上面的pass +片段着色器在场景的原始渲染之上绘制这些边缘。我在边缘上使用了纯黑色,但是仔细研究之后,似乎可以从MTLTexture提供的SCNTechnique中读取颜色并将其用于边缘。