着色器片段 - 绘制曲线线条

4

如何使用片元着色器画一条直线或曲线?

我有一个程序,它会根据用户指定的一组顶点计算贝塞尔曲线。早期,处理每个顶点以生成基于此三次贝塞尔曲线方程的一组插值点非常简单:

Cubic Bezier curve

...然后将所有处理过的顶点存储到GL_VERTEX_ARRAY中,以便使用glDrawArrays(GL_LINE_STRIP, 0, myArraySize)进行绘制。虽然解决方案很简单,但这种实现的时间复杂度为O(n^2)。问题出现在我开始增加步骤计数时,比如每次迭代我的循环都将t增加0.01,再加上用户生成尽可能多的顶点。

所以那时我开始研究着色器,特别是片段着色器。据我所知,我们的片段着色器程序为GPU正在处理的当前片段分配一种颜色。我还没有认真研究过着色器编程,但我目前的线条着色器实现如下:

#version 120

uniform vec2 resolution;
uniform vec2 ptA;
uniform vec2 ptB;
uniform vec2 ptC;
uniform vec2 ptD;
uniform float stepTotal;

void main()
{
    vec2 uv = gl_FragCoord.xy / resolution.xy;
    vec2 curvePts[int(stepTotal)];

    float t = stepTotal;

    for(float i = 0.; i < 1.0; i += 1./t)
    {
        # Courtesy to: https://yalantis.com/blog/how-we-created-visualization-for-horizon-our-open-source-library-for-sound-visualization/
        vec2 q0 = mix(ptA, ptB, i);
        vec2 q1 = mix(ptB, ptC, i);
        vec2 q2 = mix(ptC, ptD, i);

        vec2 r0 = mix(q0, q1, i);
        vec2 r1 = mix(q1, q2, i);

        vec2 p_int = mix(r0, r1, i);
        curvePts[int(i) * int(stepTotal)] = p_int;
    }

    // TO DO:
    // Check if current fragment is within the curve. Not sure how
    // to proceed from here...
}

如您所见,我目前卡在了如何检查当前片段是否在曲线内以及如何为该特定片段分配颜色的问题,最终在显示时成为一条曲线。


1
两个不错的学习网站:着色器之书镶嵌 - Ripi2
这不是一个好的解决方案,因为你将为每个像素(片段)计算它。当然,你可以添加逻辑来判断像素是否“在曲线内”,但逻辑本身会相当复杂。 - Martin Perry
@Ripi2 - 谢谢。那是一个好网站,我们正在阅读它。 - JDBones
@Martin Perry - 确实。对我来说,这是一种编程方式的转变,我习惯于在即时模式下工作,只需指定一些顶点,然后通过GL_LINES、GL_LINE_STRIP等进行绘制。 - JDBones
1
可能是在GPU上绘制二次曲线的重复问题。 - Spektre
请查看GLSL渲染2D立方贝塞尔曲线,它是基于先前评论中的链接部分实现的。 - Spektre
1个回答

3

您可以使用距离函数来绘制线段:

float DistanceToLineSegment(vec3 p, vec3 a, vec3 b)
{
    vec3 pa = p - a, ba = b - a;
    float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
    return length( pa - ba*h );
}

如果距离函数的结果小于某个阈值,则该片段将“内嵌”于一个段中。

您还可以将阈值测试替换为平滑步进函数以绘制抗锯齿线。


是的。就这样!谢谢! - JDBones
你介意详细说明一下 pab 的语义吗?至少,还有可选的 pabah 吗?我认为这个回答很有用,但是使用所选择的变量名很难理解方法(尽管我想对于一个包含多个向量和三行步骤的程序来说,也不可能做得更好)。 - Armen Michaeli
“p”是任意点的坐标;“a”是线段起点的坐标;“b”是线段终点的坐标;“pa”是从点“p”到点“a”的向量;“pb”是从点“p”到点“b”的向量。 - game development germ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接