【问题标题】:How to write the texture() function from ShaderToy to Metal shading language?如何编写从 ShaderToy 到 Metal 着色语言的 texture() 函数?
【发布时间】:2021-06-03 17:20:51
【问题描述】:

我正在开发一个自定义金属着色器,我正在尝试从着色器玩具中复制这种特殊效果:https://www.shadertoy.com/view/3sfcR2

但我似乎无法理解如何将他们的texture() 函数转换为金属着色器格式。有任何想法吗? 这是我目前在 Metal 中所拥有的:

#include <metal_stdlib>
using namespace metal;

kernel void chromaticAberration(texture2d<float, access::read> inTexture [[ texture(0) ]],
                        texture2d<float, access::write> outTexture [[ texture(1) ]],
                        device const float *time [[ buffer(0) ]],
                        uint2 gid [[ thread_position_in_grid ]])
{
    float ChromaticAberration = 0.0 / 10.0 + 8.0;
    
    // get the width and height of the screen texture
    uint width = outTexture.get_width();
    uint height = outTexture.get_height();
    
    // set its resolution
    float2 iResolution = float2(width, height);
    
    float4 orig = inTexture.read(gid);
    float2 uv = orig.xy / iResolution.xy;
    
    float2 texel = 1.0 / iResolution.xy;
    
    float2 coords = (uv - 0.5) * 2.0;
    float coordDot = dot (coords, coords);
    
    float2 precompute = ChromaticAberration * coordDot * coords;
    float2 uvR = uv - texel.xy * precompute;
    float2 uvB = uv + texel.xy * precompute;
    
    // How to convert these texture() functions?
    float r = texture(iChannel0, uvR).r;
    float g = texture(iChannel0, uv).g;
    float b = texture(iChannel0, uvB).b;
    float a = 1.;
    
    const float4 colorAtPixel = float4(r,g,b,1.0);
    outTexture.write(colorAtPixel, gid);
    
}

编辑: 按照@JustSomeGuy 的回答,我能够在Metal 中成功复制这个着色器。这是最终版本:

#include <metal_stdlib>
using namespace metal;

kernel void chromaticAberration(texture2d<float, access::read> inTexture [[ texture(0) ]],
                        texture2d<float, access::write> outTexture [[ texture(1) ]],
                        texture2d<float, access::sample> sampleTexture [[ texture(2) ]],
                        device const float *time [[ buffer(0) ]],
                        uint2 gid [[ thread_position_in_grid ]])
{
    
    float ChromaticAberration = 0.0 / 10.0 + 8.0;
    
    // get the width and height of the screen texture
    uint width = inTexture.get_width();
    uint height = inTexture.get_height();
    
    // set its resolution
    float2 iResolution = float2(width, height);
    
    float2 uv = float2(gid) / iResolution.xy;
    
    float2 texel = 1.0 / iResolution.xy;
    
    float2 coords = (uv - 0.5) * 2.0;
    float coordDot = dot (coords, coords);
    
    float2 precompute = ChromaticAberration * coordDot * coords;
    float2 uvR = uv - texel.xy * precompute;
    float2 uvB = uv + texel.xy * precompute;

    constexpr sampler s(address::clamp_to_edge, filter::linear);
    float r = sampleTexture.sample(s, uvR).r;
    float g = sampleTexture.sample(s, uv).g;
    float b = sampleTexture.sample(s, uvB).b;
    
    const float4 colorAtPixel = float4(r,g,b,1.0);
    outTexture.write(colorAtPixel, gid);
}

向@JustSomeGuy 致敬!感谢您的帮助!

【问题讨论】:

  • 我觉得我还不错,但没那么好。所以考虑到这一点。高水平?你以前和 Metal 合作过吗?您是否从任何东西中完成了这种类型的复制 - 简单与否?问题与着色玩具纹理有关吗?或者将其转换为金属纹理?你有什么工作 - 什么? (老实说,最后一点是恭敬的。)从CoreImage 内核代码转换为 Metal 对我来说已经够难的了——如果它涉及到访问超出正在处理的像素之外的像素。所以我想我真正的问题是:你能让 any 着色器工作吗?我可能会帮助分析这个问题。

标签: swift graphics metal metalkit


【解决方案1】:

好吧,我认为 ShaderToy 使用 glsl 或它的一些变体,所以 texture 函数基本上是 Metal 中的 sample 调用。让我们看一个例子。我正在使用this doc。我们将使用 2D 版本,因为这可能是您想要的。

gvec4 texture(  gsampler2D sampler,
                vec2 P,
                [float bias]);

所以在这种情况下,iChannel0 是您的sampleruvRuvuvB 是纹理坐标 (P)。他们应该是float2

所以这是一个从采样器为我们采样颜色的全局函数。在 Metal 中,我们有单独的纹理和采样器,您需要两者来采样。此外,在 Metal 中,sample 不是全局函数,而是texture2d 的成员函数。让我们看看Metal Language Specification,第 6.10.3 节“2D 纹理”。在那里我们会找到一个方法:

Tv sample(sampler s, float2 coord, int2 offset = int2(0)) const

其中Tv 是您在texture2d 实例化中的模板参数(可能是halffloat)。它还需要一个采样器和 texcoords,因此您的示例中的以下代码:

float r = texture(iChannel0, uvR).r;
float g = texture(iChannel0, uv).g;
float b = texture(iChannel0, uvB).b;

会变成这样:

constexpr sampler mySampler { filter::linear };
float r = iChannel0.sample(mySampler, uvR).r;
float g = iChannel0.sample(mySampler, uv).g;
float b = iChannel0.sample(mySampler, uvB).b;

您还需要将 texture2d&lt;float&gt; iChannel [[texture(N)]](其中 N 是您选择的索引)传递给着色器,就像 shadertoy 一样(它只是一个全局变量,但在 Metal 中您实际上需要将其作为参数传递)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-12
    • 2018-04-02
    • 2014-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-11
    • 1970-01-01
    相关资源
    最近更新 更多