是否可以仅使用GPU来加粗二次贝塞尔曲线? [英] Is it possible to thicken a quadratic Bézier curve using the GPU only?

查看:113
本文介绍了是否可以仅使用GPU来加粗二次贝塞尔曲线?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在OpenGL程序中绘制了许多二次贝塞尔曲线.现在,这些曲线是一个像素的细线,并且是由软件生成的,因为我还处于初期阶段,足以看到有效的方法.

I draw lots of quadratic Bézier curves in my OpenGL program. Right now, the curves are one-pixel thin and software-generated, because I'm at a rather early stage, and it is enough to see what works.

很简单,给定3个控制点( P 0 P 2 ),我评估了以下内容在软件中 t 从0到1(步长为1/8)变化的等式,并使用GL_LINE_STRIP将它们链接在一起:

Simply enough, given 3 control points (P0 to P2), I evaluate the following equation with t varying from 0 to 1 (with steps of 1/8) in software and use GL_LINE_STRIP to link them together:

B( t )=(1- t ) 2 P 0 + 2(1- t ) t P 1 + t 2 P 2

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

B显然可以产生二维矢量.

Where B, obviously enough, results in a 2-dimensional vector.

这种方法足够好"地工作了,因为即使我最大的曲线也不需要超过8个步骤就能看起来很弯曲.不过,一个像素的细曲线很难看.

This approach worked 'well enough', since even my largest curves don't need much more than 8 steps to look curved. Still, one pixel thin curves are ugly.

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

I wanted to write a GLSL shader that would accept control points and a uniform thickness variable to, well, make the curves thicker. At first I thought about making a pixel shader only, that would color only pixels within a thickness / 2 distance of the curve, but doing so requires solving a third degree polynomial, and choosing between three solutions inside a shader doesn't look like the best idea ever.

然后我尝试查找其他人是否已经这样做了.我偶然发现了 Loop和Blinn来自Microsoft Research的白皮书在这里,伙计们展示了一种简单的方法来填充曲线下的区域.尽管在某种程度上可以正常工作,但我很难将其适应于在两条装订曲线之间绘制.

I then tried to look up if other people already did it. I stumbled upon a white paper by Loop and Blinn from Microsoft Research where the guys show an easy way of filling the area under a curve. While it works well to that extent, I'm having trouble adapting the idea to drawing between two bouding curves.

使用几何着色器查找与单个曲线匹配的边界曲线非常容易.问题在于片段着色器应该填满整个东西.他们的方法是使用插值的纹理坐标来确定片段是落在曲线上方还是下方.但是我无法找到一种方法来处理两条曲线(对于着色器我不是一个新手,也不是数学专家,因此我不知道该怎么做的事实当然并不意味着那是不可能的).

Finding bounding curves that match a single curve is rather easy with a geometry shader. The problems come with the fragment shader that should fill the whole thing. Their approach uses the interpolated texture coordinates to determine if a fragment falls over or under the curve; but I couldn't figure a way to do it with two curves (I'm pretty new to shaders and not a maths expert, so the fact I didn't figure out how to do it certainly doesn't mean it's impossible).

我的下一个想法是将填充曲线分成三角形,并且仅在外部使用Bézier片段着色器.但是为此,我需要在可变点处拆分内部和外部曲线,这再次意味着我必须求解方程,这实际上不是一个选择.

My next idea was to separate the filled curve into triangles and only use the Bézier fragment shader on the outer parts. But for that I need to split the inner and outer curves at variable spots, and that means again that I have to solve the equation, which isn't really an option.

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

Are there viable algorithms for stroking quadratic Bézier curves with a shader?

推荐答案

这部分延续了我以前的答案,但实际上却大不相同,因为我在该答案中遇到了一些中心问题.

This partly continues my previous answer, but is actually quite different since I got a couple of central things wrong in that answer.

为使片段着色器仅在两条曲线之间着色,提供了两组纹理"坐标作为可变变量,并应用了Loop-Blinn技术.

To allow the fragment shader to only shade between two curves, two sets of "texture" coordinates are supplied as varying variables, to which the technique of Loop-Blinn is applied.

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为控制三角形的三个顶点指定了它们,并且在三角形上对其进行了适当的插值.但是,在这里,我们需要具有相同的插值值,同时实际渲染不同的三角形.

So far, easy. The hard part is setting up the texture coordinates in the geometry shader. Loop-Blinn specifies them for the three vertices of the control triangle, and they are interpolated appropriately across the triangle. But, here we need to have the same interpolated values available while actually rendering a different triangle.

解决方案是找到从(x,y)坐标到内插/外推值的线性函数映射.然后,可以在渲染三角形时为每个顶点设置这些值.这是我这部分代码的关键部分.

The solution to this is to find the linear function mapping from (x,y) coordinates to the interpolated/extrapolated values. Then, these values can be set for each vertex while rendering a triangle. Here's the key part of my code for this part.

    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三角形.然后,需要在两端正确填充两组坐标的同时填充这两个三角形之间的间隙.

Here pos1 and pos2 contain the coordinates of the two control triangles. This part renders the triangle defined by pos1, but with texCoord2 set to the translated values from the pos2 triangle. Then the pos2 triangle needs to be rendered, similarly. Then the gap between these two triangles at each end needs to filled, with both sets of coordinates translated appropriately.

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

The calculation of the matrix inverse requires either GLSL 1.50 or it needs to be coded manually. It would be better to solve the equation for the translation without calculating the inverse. Either way, I don't expect this part to be particularly fast in the geometry shader.

这篇关于是否可以仅使用GPU来加粗二次贝塞尔曲线?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆