如何将 texture() 函数从 ShaderToy 写入 Metal 着色语言?

How to write the texture() function from ShaderToy to Metal shading language?

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

但我似乎无法理解如何将他们的 texture() 函数转换为 Metal 着色器格式。有任何想法吗? 到目前为止,这是我在 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!感谢您的帮助!

好吧,我认为 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)。它还需要一个采样器和纹理坐标,因此您的示例中的这段代码:

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;

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