Webgl 如何使用对金属着色器的fwidth()调用移植此Shadertoy着色器?

Webgl 如何使用对金属着色器的fwidth()调用移植此Shadertoy着色器?,webgl,shader,metal,Webgl,Shader,Metal,我一直在将Shadertoy着色器移植到Metal,以便学习如何编写Metal着色器。我认为我做得不对,因为我一直在将我的每个着色器作为计算着色器而不是顶点/片段着色器编写。这对我移植的很多着色器都有效,将近20个。但是,有些端口速度非常慢,而另一些端口包含不可用的功能 下面是一个让我大吃一惊的着色器: render()和mainImage()中的fwidth()调用不允许在metal compute着色器中使用。但是,金属着色器语言确实有fwidth(),但它只能在片段着色器中调用 下面是我

我一直在将Shadertoy着色器移植到Metal,以便学习如何编写Metal着色器。我认为我做得不对,因为我一直在将我的每个着色器作为计算着色器而不是顶点/片段着色器编写。这对我移植的很多着色器都有效,将近20个。但是,有些端口速度非常慢,而另一些端口包含不可用的功能

下面是一个让我大吃一惊的着色器:

render()和mainImage()中的fwidth()调用不允许在metal compute着色器中使用。但是,金属着色器语言确实有fwidth(),但它只能在片段着色器中调用

下面是我移植到计算着色器的尝试:

#include <metal_stdlib>

using namespace metal;

float float_mod(float f1, float f2) {
    return f1-f2 * floor(f1/f2);
}


float sdfCircle(float2 center, float radius, float2 coord )
{
    float2 offset = coord - center;

    return sqrt((offset.x * offset.x) + (offset.y * offset.y)) - radius;
}

float sdfEllipse(float2 center, float a, float b, float2 coord)
{
    float a2 = a * a;
    float b2 = b * b;
    return (b2 * (coord.x - center.x) * (coord.x - center.x) +
            a2 * (coord.y - center.y) * (coord.y - center.y) - a2 * b2)/(a2 * b2);
}

float sdfLine(float2 p0, float2 p1, float width, float2 coord)
{
    float2 dir0 = p1 - p0;
    float2 dir1 = coord - p0;
    float h = clamp(dot(dir0, dir1)/dot(dir0, dir0), 0.0, 1.0);
    return (length(dir1 - dir0 * h) - width * 0.5);
}

float sdfUnion( const float a, const float b )
{
    return min(a, b);
}

float sdfDifference( const float a, const float b)
{
    return max(a, -b);
}

float sdfIntersection( const float a, const float b )
{
    return max(a, b);
}

float anti(float d) {
    return fwidth(d) * 1.0;
}

float4 render(float d, float3 color, float stroke)
{
    //stroke = fwidth(d) * 2.0;
    float anti = fwidth(d) * 1.0;
                float4 strokeLayer = float4(float3(0.05), 1.0-smoothstep(-anti, anti, d - stroke));
    float4 colorLayer = float4(color, 1.0-smoothstep(-anti, anti, d));

    if (stroke < 0.000001) {
        return colorLayer;
    }
    return float4(mix(strokeLayer.rgb, colorLayer.rgb, colorLayer.a), strokeLayer.a);
}


kernel void compute(texture2d<float, access::write> output [[texture(0)]],
                    texture2d<float, access::sample> input [[texture(1)]],
                    constant float &timer [[buffer(0)]],
                    uint2 gid [[thread_position_in_grid]])
{
    float4 fragColor;
    int width = output.get_width();
    int height = output.get_height();
    float2 resolution = float2(width,height);
    float2 uv = float2(gid) / resolution;

    float size = min(resolution.x, resolution.y);
    float pixSize = 1.0 / size;
    float stroke = pixSize * 1.5;
    float2 center = float2(0.5, 0.5 * resolution.y/resolution.x);

    float a = sdfEllipse(float2(0.5, center.y*2.0-0.34), 0.25, 0.25, uv);
    float b = sdfEllipse(float2(0.5, center.y*2.0+0.03), 0.8, 0.35, uv);
    b = sdfIntersection(a, b);

    float4 layer1 = render(b, float3(0.32, 0.56, 0.53), fwidth(b) * 2.0);


// Draw strips
    float4 layer2 = layer1;
    float t, r0, r1, r2, e, f;
    float2 sinuv = float2(uv.x, (sin(uv.x*40.0)*0.02 + 1.0)*uv.y);
    for (float i = 0.0; i < 10.0; i++) {
        t = float_mod(timer + 0.3 * i, 3.0) * 0.2;
        r0 = (t - 0.15) / 0.2 * 0.9 + 0.1;
        r1 = (t - 0.15) / 0.2 * 0.1 + 0.9;
        r2 = (t - 0.15) / 0.2 * 0.15 + 0.85;
        e = sdfEllipse(float2(0.5, center.y*2.0+0.37-t*r2), 0.7*r0, 0.35*r1, sinuv);
        f = sdfEllipse(float2(0.5, center.y*2.0+0.41-t), 0.7*r0, 0.35*r1, sinuv);
        f = sdfDifference(e, f);
        f = sdfIntersection(f, b);
        float4 layer = render(f, float3(1.0, 0.81, 0.27), 0.0);
        layer2 = mix(layer2, layer, layer.a);
    }


// Draw the handle
    float bottom = 0.08;
    float handleWidth = 0.01;
    float handleRadius = 0.04;
    float d = sdfCircle(float2(0.5-handleRadius+0.5*handleWidth, bottom), handleRadius, uv);
    float c = sdfCircle(float2(0.5-handleRadius+0.5*handleWidth, bottom), handleRadius-handleWidth, uv);
    d = sdfDifference(d, c);
    c = uv.y - bottom;
    d = sdfIntersection(d, c);
    c = sdfLine(float2(0.5, center.y*2.0-0.05), float2(0.5, bottom), handleWidth, uv);
    d = sdfUnion(d, c);
    c = sdfCircle(float2(0.5, center.y*2.0-0.05), 0.01, uv);
    d = sdfUnion(c, d);
    c = sdfCircle(float2(0.5-handleRadius*2.0+handleWidth, bottom), handleWidth*0.5, uv);
    d = sdfUnion(c, d);
    float4 layer0 = render(d, float3(0.404, 0.298, 0.278), stroke);

    float2 p = (2.0*float2(gid).xy-resolution.xy)/min(resolution.y,resolution.x);
    float3 bcol = float3(1.0,0.8,0.7-0.07*p.y)*(1.0-0.25*length(p));
    fragColor = float4(bcol, 1.0);
    fragColor.rgb = mix(fragColor.rgb, layer0.rgb, layer0.a);
    fragColor.rgb = mix(fragColor.rgb, layer1.rgb, layer1.a);
    fragColor.rgb = mix(fragColor.rgb, layer2.rgb, layer2.a);

    fragColor.rgb = pow(fragColor.rgb, float3(1.0/2.2));
    output.write(fragColor,gid);

}
#包括
使用金属;
浮点数浮点数(浮点数f1、浮点数f2){
返回f1-f2*楼层(f1/f2);
}
浮动sdfCircle(浮动2中心、浮动半径、浮动2坐标)
{
浮动2偏移=坐标-中心;
返回sqrt((offset.x*offset.x)+(offset.y*offset.y))-半径;
}
浮子sdfEllipse(浮子2中心、浮子a、浮子b、浮子2坐标)
{
浮点数a2=a*a;
浮动b2=b*b;
返回(b2*(coord.x-center.x)*(coord.x-center.x)+
a2*(坐标y-中心y)*(坐标y-中心y)-a2*b2)/(a2*b2);
}
浮子sdfLine(浮子2 p0、浮子2 p1、浮子宽度、浮子2坐标)
{
float2dir0=p1-p0;
float2-dir1=coord-p0;
浮动h=夹具(点(dir0,dir1)/点(dir0,dir0),0.0,1.0);
返回(长度(dir1-dir0*h)-宽度*0.5);
}
浮点函数(常量浮点a、常量浮点b)
{
返回最小值(a,b);
}
浮点SDF差异(常量浮点a、常量浮点b)
{
返回最大值(a,-b);
}
浮点sdfIntersection(常量浮点a、常量浮点b)
{
返回最大值(a,b);
}
防浮(浮子d){
返回宽度(d)*1.0;
}
浮动4渲染(浮动d、浮动3颜色、浮动笔划)
{
//行程=fwidth(d)*2.0;
抗浮性=fwidth(d)*1.0;
float4-strokeLayer=float4(float3(0.05),1.0-smoothstep(-anti,anti,d-stroke));
float4 colorLayer=float4(颜色,1.0-smoothstep(-anti,anti,d));
如果(冲程<0.000001){
返回彩色层;
}
返回float4(混合(strokeLayer.rgb、colorLayer.rgb、colorLayer.a)、strokeLayer.a);
}
内核无效计算(texture2d输出[[texture(0)]],
纹理2D输入[[纹理(1)],
恒定浮点和计时器[[缓冲区(0)],
uint2 gid[[螺纹位置在网格中]]
{
花色;
int width=output.get_width();
int height=output.get_height();
float2分辨率=float2(宽度、高度);
float2紫外=float2(gid)/分辨率;
浮动大小=最小值(分辨率x,分辨率y);
浮动像素大小=1.0/大小;
浮动行程=像素大小*1.5;
float2中心=float2(0.5,0.5*分辨率.y/分辨率.x);
浮点数a=Sdfillipse(浮点数2(0.5,中心y*2.0-0.34),0.25,0.25,uv);
浮点数b=Sdfillipse(浮点数2(0.5,中心y*2.0+0.03),0.8,0.35,uv);
b=sdfIntersection(a,b);
float4 layer1=渲染(b,float3(0.32,0.56,0.53),fwidth(b)*2.0);
//拉条
浮动4层2=层1;
浮点数t,r0,r1,r2,e,f;
float2 sinuv=float2(uv.x,(sin(uv.x*40.0)*0.02+1.0)*uv.y);
对于(浮点i=0.0;i<10.0;i++){
t=浮点型(定时器+0.3*i,3.0)*0.2;
r0=(t-0.15)/0.2*0.9+0.1;
r1=(t-0.15)/0.2*0.1+0.9;
r2=(t-0.15)/0.2*0.15+0.85;
e=Sdfillipse(浮动2(0.5,中心y*2.0+0.37-t*r2),0.7*r0,0.35*r1,sinuv);
f=Sdfillipse(浮动2(0.5,中心y*2.0+0.41-t),0.7*r0,0.35*r1,sinuv);
f=SDF差值(e,f);
f=sdfIntersection(f,b);
float4层=渲染(f,float3(1.0,0.81,0.27),0.0);
第二层=混合(第二层,第二层,第三层a);
}
//拉手
浮底=0.08;
浮动手柄宽度=0.01;
浮子公差=0.04;
浮点数d=sdfCircle(浮点数2(0.5-handleRadius+0.5*handleWidth,底部),handleRadius,uv);
浮点数c=sdfCircle(浮点数2(0.5-handleRadius+0.5*handleWidth,底部),handleRadius-handleWidth,uv);
d=SDF差异(d,c);
c=uv.y-底部;
d=sdfIntersection(d,c);
c=sdfLine(浮点数2(0.5,中心y*2.0-0.05),浮点数2(0.5,底部),把手宽度,紫外线);
d=sdfUnion(d,c);
c=sdfCircle(浮动2(0.5,中心y*2.0-0.05),0.01,uv);
d=sdfUnion(c,d);
c=sdfCircle(浮动2(0.5-handleRadius*2.0+handleWidth,底部),handleWidth*0.5,uv);
d=sdfUnion(c,d);
浮动4层0=渲染(d,浮动3(0.404,0.298,0.278),笔划);
float2p=(2.0*float2(gid).xy分辨率.xy)/min(分辨率.y,分辨率.x);
float3 bcol=float3(1.0,0.8,0.7-0.07*p.y)*(1.0-0.25*长度(p));
fragColor=float4(bcol,1.0);
fragColor.rgb=混合(fragColor.rgb,layer0.rgb,layer0.a);
fragColor.rgb=混合(fragColor.rgb,layer1.rgb,layer1.a);
fragColor.rgb=混合(fragColor.rgb,layer2.rgb,layer2.a);
fragColor.rgb=pow(fragColor.rgb,float3(1.0/2.2));
输出.写入(fragColor,gid);
}
这不会编译,因为fwidth()不可用。但是,如果我确实去掉fwidth(),它将编译。。。但当然画的不是正确的东西


我想知道是否有更好的方法将其移植到片段/顶点着色器,以便使用MSL的fwidth()?或者将其作为计算着色器编写很好,我应该找到一种不同的方法来使用fwidth()

正如您可能知道的那样,真正的答案是使用片段函数,在这里自动计算导数。如果没有这一点,你将不得不自己计算它们
fwidth
dfdx
dfdy
的绝对值之和,分别表示x轴和y轴上参数的窗口空间变化率。您可以通过使用有限差分来确定这些,并将结果传递到每个
render
调用中。这样做很乏味,但是