glsl - 是否可以仅使用 GPU 来加厚二次贝塞尔曲线?

标签 glsl bezier

我在我的 OpenGL 程序中绘制了很多二次贝塞尔曲线。现在,曲线只有一个像素细并且是由软件生成的,因为我处于相当早期的阶段,足以看看什么是有效的。

很简单,给定 3 个控制点(P0 到 P2),我在软件中评估以下方程,其中 t 从 0 到 1(步长为 1/8),并使用 GL_LINE_STRIP将它们链接在一起:

B(t) = (1 - t)2P0 + 2(1 - t)tP1 + t2P2



哪里B ,很明显,结果是一个二维向量。

这种方法“足够好”,因为即使是我最大的曲线也不需要超过 8 个步骤来看起来弯曲。尽管如此,一个像素的细曲线是丑陋的。

我想编写一个 GLSL 着色器,它可以接受控制点和统一的 thickness可以使曲线变粗。起初我想只制作一个像素着色器,它只会给 thickness / 2 内的像素着色。曲线的距离,但这样做需要求解三次多项式,并且在着色器内的三个解决方案之间进行选择看起来并不是最好的主意。

然后我尝试查找其他人是否已经这样做了。我偶然发现 a white paper by Loop and Blinn from Microsoft Research伙计们展示了一种简单的方法来填充曲线下的区域。虽然它在这个程度上运作良好,但我无法适应在两条弯曲曲线之间绘制的想法。

使用几何着色器可以很容易地找到与单条曲线匹配的边界曲线。问题来自应该填充整个事物的片段着色器。他们的方法使用内插纹理坐标来确定片段是落在曲线之上还是之下;但我想不出用两条曲线来做的方法(我对着色器很陌生,而不是数学专家,所以我不知道如何做到这一点的事实当然并不意味着这是不可能的)。

我的下一个想法是将填充曲线分成三角形,并且只在外部使用贝塞尔片段着色器。但为此,我需要在可变点处分割内曲线和外曲线,这再次意味着我必须求解方程,这不是一个真正的选择。

是否有使用着色器抚摸二次贝塞尔曲线的可行算法?

最佳答案

这部分延续了我之前的答案,但实际上完全不同,因为我在该答案中弄错了几个核心问题。

为了让片段着色器仅在两条曲线之间进行着色,提供了两组“纹理”坐标作为可变变量,并应用了 Loop-Blinn 技术。

varying vec2 texCoord1,texCoord2;
varying float insideOutside;

varying vec4 col;

void main()
{   
    float f1 = texCoord1[0] * texCoord1[0] - texCoord1[1];
    float f2 = texCoord2[0] * texCoord2[0] - texCoord2[1];

    float alpha = (sign(insideOutside*f1) + 1) * (sign(-insideOutside*f2) + 1) * 0.25;
    gl_FragColor = vec4(col.rgb, col.a * alpha);
}

到目前为止,很容易。困难的部分是在几何着色器中设置纹理坐标。 Loop-Blinn 为控制三角形的三个顶点指定了它们,并且它们在三角形上被适本地插值。但是,在这里我们需要在实际渲染不同的三角形时使用相同的插值。

对此的解决方案是找到从 (x,y) 坐标到内插/外插值的线性函数映射。然后,可以在渲染三角形时为每个顶点设置这些值。这是我这部分代码的关键部分。
    vec2[3] tex = vec2[3]( vec2(0,0), vec2(0.5,0), vec2(1,1) );

    mat3 uvmat;
    uvmat[0] = vec3(pos2[0].x, pos2[1].x, pos2[2].x);
    uvmat[1] = vec3(pos2[0].y, pos2[1].y, pos2[2].y);
    uvmat[2] = vec3(1, 1, 1);

    mat3 uvInv = inverse(transpose(uvmat));

    vec3 uCoeffs = vec3(tex[0][0],tex[1][0],tex[2][0]) * uvInv;
    vec3 vCoeffs = vec3(tex[0][1],tex[1][1],tex[2][1]) * uvInv;

    float[3] uOther, vOther;
    for(i=0; i<3; i++) {
        uOther[i] = dot(uCoeffs,vec3(pos1[i].xy,1));
        vOther[i] = dot(vCoeffs,vec3(pos1[i].xy,1));
    }   

    insideOutside = 1;
    for(i=0; i< gl_VerticesIn; i++){
        gl_Position = gl_ModelViewProjectionMatrix * pos1[i];
        texCoord1 = tex[i];
        texCoord2 = vec2(uOther[i], vOther[i]);
        EmitVertex();
    }
    EndPrimitive();

这里 pos1 和 pos2 包含两个控制三角形的坐标。这部分渲染由 pos1 定义的三角形,但将 texCoord2 设置为来自 pos2 三角形的转换值。然后同样需要渲染 pos2 三角形。然后需要填充两端这两个三角形之间的间隙,并适本地平移两组坐标。

矩阵逆的计算需要 GLSL 1.50 或需要手动编码。最好在不计算逆的情况下求解平移方程。无论哪种方式,我都不希望这部分在几何着色器中特别快。

关于glsl - 是否可以仅使用 GPU 来加厚二次贝塞尔曲线?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5533945/

相关文章:

opengl - GLSL - 从世界坐标计算屏幕坐标无法正常工作

c++ - 无法在 OpenGL 中获取 GLSL 着色器

ios - OpenGLES 无法在 iOS 的顶点着色器中使用输入参数

c# - 分割贝塞尔曲线

javascript - 在javascript中有效地分割贝塞尔曲线

svg - 用控制点关闭SVG路径(Z)?

opengl - gl_PointSize 对应世界空间大小

opengl - GLSL 在程序之间共享制服 #130

objective-c - objective-c中的贝塞尔曲线算法

python - 扩展的贝塞尔曲线库或贝塞尔曲线运算算法