如何确保屏幕空间导数出现在三角形边缘上?

dm7nw8vv  于 2022-10-18  发布在  其他
关注(0)|答案(1)|浏览(127)

我使用以下顶点着色器绘制了一个抗锯齿圆形正方形作为模型坐在顶部的地平面:


# version 330

out vec2 tex;
uniform mat4 mvp;

const vec2 pos[4] = vec2[](
    vec2(-1.0, 1.0),
    vec2(1.0, 1.0),
    vec2(1.0, -1.0),
    vec2(-1.0, -1.0));

void main()
{
    tex = pos[gl_VertexID];
    /* setup camera */
    gl_Position = mvp * vec4(pos[gl_VertexID], 0.0, 1.0);
}

和以下片段着色器:


# version 330

in vec2 tex;
out vec4 Out_Color;

float roundedBoxSDF(vec2 uv, float Size, float Radius)
{
    return length(max(abs(uv) - Size + Radius, 0.0)) - Radius;
}

void main()
{
    /* 95% to make sure there is a screen space derivative present to
    calculate against. */
    float dist = roundedBoxSDF(tex, 0.95, 0.5);
    float smoothedAlpha = dist / length(vec2(dFdx(dist), dFdy(dist)));

    Out_Color = vec4(vec3(1, 1, 1), 1.0 - smoothedAlpha);
}

如果我用纹理坐标绘制正方形,直到四边形的边缘,沿着四边形的边的非圆形边总是有锯齿,因为有符号的距离场没有屏幕空间导数来计算距离原点1的边。

这似乎是一个简单的修复方法:就像在上面的碎片着色器中一样,我只需将纹理坐标缩小到95%roundedBoxSDF(tex, 0.95, 0.5);,这样就有了一个没有边的四边形来收集屏幕空间导数。很好的抗锯齿效果。

...直到我放大很远或以倾斜的Angular 观看。然后5%的边距不再足够了,我又得到了混叠,因为在屏幕空间方面,没有像素存在来获得所述派生。

我怎么能解释这样的事情呢?继续缩小纹理坐标和增长顶点作为与相机的距离的函数?还有比这更聪明的吗?

os8fio9y

os8fio9y1#

问题不在于衍生品--与你所认为的相反,它们在边缘有明确的定义。也不是缺少各向异性过滤--因为它也发生在非斜角上。
正如你自己所说,问题是,从某些Angular 来看,收缩0.95%是不够的。这是因为形状的分析边与四边形边的距离不到一个像素。在这种情况下,在非透明像素需要光栅化的地方没有任何像素:

如果你走缩小的路线,你缩小的量本身取决于衍生品-你需要在屏幕空间缩小一个像素。一种等价但更简单的方法是计算SDF on the interior of your non-shrunk box,然后设置out_Color.a = -smoothedAlpha

在任何一种情况下,缩小都是一种相当老套的解决问题的方法,因为它会改变你的形状的有效尺寸。
更正确的方法是将光栅化的三角形扩大一个像素。有一个NVIDIA扩展就可以做到这一点:NV_conservative_raster_dilate。在未扩展的OpenGL中,您可以使用几何体着色器获得类似的结果。在任何一种情况下,膨胀的三角形都会重叠,如果您的形状要部分透明,则需要额外的措施。

相关问题