我在OpenGL程序中绘制了许多二次Bézier曲线.现在,曲线是一个像素的薄和软件生成的,因为我处于一个相当早的阶段,它足以看到什么有效.
很简单,给定3个控制点(P 0到P 2),我在软件中用t从0到1(步长为1/8)来评估下面的等式,并用GL_LINE_STRIP它们将它们链接在一起:
B(t)=(1- t)2 P 0 + 2(1- t)t P 1 + t 2 P 2
B显然,在哪里产生二维向量.
这种方法"足够好",因为即使是我最大的曲线,也不需要超过8步才能看起来弯曲.不过,一条像素细的曲线很难看.
我想编写一个GLSL着色器,它接受控制点和一个统一thickness变量,以使曲线更粗.起初我想过只制作一个像素着色器,它只会thickness / 2在曲线的距离内着色像素,但这样做需要求解三次多项式,并且在着色器内选择三个解决方案看起来不是最好的想法.
然后我试着查看其他人是否已经这样做了.我偶然发现了微软研究院的Loop和Blinn的一份白皮书,其中的人们展示了一种简单的方法来填充曲线下的区域.虽然它在这种程度上运作良好,但我很难适应两条连线曲线之间的绘制.
使用几何着色器查找与单条曲线匹配的边界曲线相当容易.片段着色器会出现问题,应该填满整个事物.他们的方法使用插值纹理坐标来确定片段是否落在曲线之上或之下; 但我想办法用两条曲线来做这件事(我对着色器而言并不是数学专家,所以事实上我并没有弄清楚如何做到这一点当然并不意味着它是不可能的).
我的下一个想法是将填充的曲线分成三角形,并仅在外部部分使用Bézier片段着色器.但是为此我需要将内部曲线和外部曲线分开到可变点,这意味着我必须再次解决方程式,这不是一个真正的选择.
是否有可行的算法用于使用着色器描边二次Bézier曲线?
这在一定程度上延续了我之前的答案,但实际上有很大不同,因为我在该答案中犯了一些核心问题。
为了允许片段着色器仅在两条曲线之间进行着色,提供了两组“纹理”坐标作为变化变量,并应用了 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);
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,很容易。困难的部分是在几何着色器中设置纹理坐标。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();
Run Code Online (Sandbox Code Playgroud)
这里 pos1 和 pos2 包含两个控制三角形的坐标。该部分渲染由 pos1 定义的三角形,但将 texCoord2 设置为 pos2 三角形的转换值。然后 pos2 三角形需要渲染,类似。然后需要填充这两个三角形两端之间的间隙,并适当平移两组坐标。
矩阵逆的计算需要 GLSL 1.50 或者需要手动编码。最好求解平移方程而不计算逆矩阵。不管怎样,我不希望这部分在几何着色器中特别快。