如何将 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
是您的 sampler
,uvR
、uv
、uvB
是纹理坐标 (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
实例化中的模板参数(可能是 half
或 float
)。它还需要一个采样器和纹理坐标,因此您的示例中的这段代码:
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 中您实际上需要将其作为参数传递)。
我正在开发自定义金属着色器,我正在尝试从着色器玩具中复制这种特殊效果: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
是您的 sampler
,uvR
、uv
、uvB
是纹理坐标 (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
实例化中的模板参数(可能是 half
或 float
)。它还需要一个采样器和纹理坐标,因此您的示例中的这段代码:
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 中您实际上需要将其作为参数传递)。