是否可以表达"t"?三次贝塞尔曲线方程式的变量? [英] Is it possible to express "t" variable from Cubic Bezier Curve equation?

查看:96
本文介绍了是否可以表达"t"?三次贝塞尔曲线方程式的变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只想通过片段着色器绘制贝塞尔曲线,以连接编辑器中的节点.我知道定义贝塞尔曲线的所有4个点.并且为每个像素调用Fragment Shader,因此我可以检查一下:如果gl_Coord.x的"t"介于0和1之间,则将frag_color设置为Red.我想避免效率低下的着色器中的循环.我认为,最好的方法是检查曲线上的点.但是,对于贝塞尔曲线,该如何做呢?

I want draw bezier curve only by fragment shader to connect nodes in my editor. I know all 4 points that define Bezier Curve. And Fragment Shader is called for every pixel, so i can just check: if "t" for gl_Coord.x is between 0 and 1 then set frag_color to Red for example. I want to avoid loops in shader that's inefficient. Best way, i think, is check for points that lay on the curve. But how to do it for Bezier Curves?

是否可以从三次方贝塞尔方程表示"t"变量?

Is it possible to express "t" variable from cubic bezier equation?

x = ((1-t)^3 * p0.x) + (3 * (1-t)^2 * t * p1.x) + (3 * (1 - t) * t^2 * p2.x) + (t^3 * p3.x);

t = ?

网站Wolfram Aplha给我该公式(在GetBezierT函数中).但是公式给我错误的"t"值,我有一半的抛物线而不是曲线:

Website Wolfram Aplha give me that formula(in GetBezierT function). But formula give me wrong "t" values and i have half of parabola instead of curve:

#version 150
.....
layout (origin_upper_left, pixel_center_integer) in vec4 gl_FragCoord;
out vec4 frag_color;
.....
vec4 BackgroundColor = vec4(0.15, 0.15, 0.15, 1.0);
vec2 p0 = vec2(61.0f,87.0f);
vec2 p1 = vec2(181.0f, 39.0f);
vec2 p2 = vec2(283.0f, 178.0f);
vec2 p3 = vec2(416.0f, 132.0f);

float getBezierT(float x, float a, float b, float c, float d)
{
      return  float(sqrt(3) * 
          sqrt(-4 * b * d + 4 * b * x + 3 * c * c + 2 * c * d - 8 * c * x - d * d + 4 * d * x) 
            + 6 * b - 9 * c + 3 * d) 
            / (6 * (b - 2 * c + d));
}

void main() {  
    .....
    frag_color = BackgroundColor; 
    .....
    float tx = getBezierT(gl_FragCoord.x, p0.x, p1.x, p2.x, p3.x);
    float ty = getBezierT(gl_FragCoord.y, p0.y, p1.y, p2.y, p3.y);

    if (tx >= 0.0f && tx <= 1.0f && ty >= 0.0f && ty <= 1.0f)
    {
        if(abs(tx-ty) <  0.01f) // simple check is that one point with little bias
        frag_color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
    }
}

更新

犯了一个错误.我认为寻找t没有任何意义.我以为我会忍受的.但是在Salix albaStratubas给出答案之后,我意识到,如果tX等于tY,这意味着该点将位于曲线上,因为在公式中,对于每个点,一个t代替了xy.也许在某些情况下,不同的tXtY也可以在该曲线上给出一个点,但是我们可以忽略这一点.构造贝塞尔曲线的算法意味着我们线性增加t并将其代入公式中,曲线的扭曲程度无关紧要,该算法沿曲线顺序返回每个下一个点的坐标.

Made a mistake. I thought there was no point in looking for t. I thought I would put up with it. But after the answer given by Salix alba and Stratubas, I realized that if tX is equal to tY, this means that this point will lie on the curve, because in the formula for each point one value of t is substituted for both x and y. Maybe there are cases when different tX and tY can also give a point on this curve, but we can just ignore that. The algorithm for constructing a bezier curve implies that we linearly increase t and substitute it into the formula and it does not matter how much the curve is twisted, the algorithm returns the coordinates of each next point sequentially along the curve.

因此,首先,我再次提出一个问题:如何从三次贝塞尔方程表示变量t?

Therefore, first of all, I again open this question: how to express the variable t from a cubic bezier equation?

试图表达t,但这对我来说是非常困难的.为了科学目的",有必要评估这种方法的有效性.在这里提出问题之前,我进行了很多搜索,但从未发现有人会尝试使用此方法.我需要了解为什么.

Tried to express t, but it is insanely difficult for me. It is necessary to evaluate the effectiveness of this approach for "scientific purposes" =). Before asking a question here, I searched a lot, but never found that someone would try to use this method. I need to understand why.

更新2

您做得很棒!我没想到会收到如此详细的答案.正是我需要的.给我时间检查所有内容=)

You have done an excellent job! I did not expect to receive such detailed answers. Exactly what i needed. Give me time to check everything=)

更新3

结论:根据三次贝塞尔方程精确表示t.这项工作很耗时,但是近似值没有实际用途.为了解决这个问题,有必要分析方程数据,找到模式并开发用于构造贝塞尔曲线的新公式.有了变量之间的新关系,将有可能以不同的方式表达t.如果我们用控制点的x坐标乘以由方程的四个部分中的函数生成的四个系数(v0-v3)的乘积之和的形式来表示三次贝塞尔公式t的值.这样得出公式x = a.x * v0 + b.x * v1 + c.x * v2 + d.x * v3.并且,如果您查看下表,您会发现变量t的表达式是一个具有四个未知数的方程式.因为它们之间的某些V系数的值和关系在每次迭代之间都以不可预测的方式变化.发现新的抽象公式超出了此问题和我的能力范围.

Conclusions: Accurate expression of t from the Cubic Bezier equation. Time-consuming task, but approximate values don't have practical use. To solve this problem, it is necessary to analyze the equation data, find patterns and develop new formula for constructing bezier curves. With a new relations of variables among themselves, then it will become possible to express t in a different way. If we represent the Cubic Bezier formula in the form of the sum of the products of the x coordinates of the control points by four coefficients (v0 -v3) generated by the functions in the four parts of the equation depending on the value of t. This gives the formula x = a.x * v0 + b.x * v1 + c.x * v2 + d.x * v3. And if you look at the table below, you can get the idea that the expression for the variable t is an equation with four unknowns. Because both the values and the relations of some of the V coefficients between themselves change in an unpredictable way from iteration to iteration. Finding that new abstract formula is beyond the scope of this question and my competence.

非常感谢大家的辛勤工作,尤其是Spektre所做的独特开发以及为优化渲染算法所做的努力.您的方法对我而言是最佳选择=)

Many thanks to all for your work, especially Spektre for the unique development and efforts made to optimize the rendering algorithm. Your approach is the best choice for me=)

推荐答案

您需要搜索立方路径并记住最近的点.可以递归地完成此操作,以提高精度,这里以 C ++ GL 为例:

What you need is to search your cubic path and remember closest point. This can be done recursively with increasing precisions here small C++ GL example:

//---------------------------------------------------------------------------
double pnt[]=                   // cubic curve control points
    {
    -0.9,-0.8,0.0,
    -0.6,+0.8,0.0,
    +0.6,+0.8,0.0,
    +0.9,-0.8,0.0,
    };
const int pnts3=sizeof(pnt)/sizeof(pnt[0]);
const int pnts=pnts3/3;
//---------------------------------------------------------------------------
double cubic_a[4][3];           // cubic coefficients
void cubic_init(double *pnt)    // compute cubic coefficients
    {
    int i;
    double *p0=pnt,*p1=p0+3,*p2=p1+3,*p3=p2+3;
    for (i=0;i<3;i++)           // cubic BEZIER coefficients
        {
        cubic_a[0][i]=                                    (    p0[i]);
        cubic_a[1][i]=                        (3.0*p1[i])-(3.0*p0[i]);
        cubic_a[2][i]=            (3.0*p2[i])-(6.0*p1[i])+(3.0*p0[i]);
        cubic_a[3][i]=(    p3[i])-(3.0*p2[i])+(3.0*p1[i])-(    p0[i]);
        }
    }
//---------------------------------------------------------------------------
double* cubic(double t)         // return point on cubic from parameter
    {
    int i;
    static double p[3];
    double tt=t*t,ttt=tt*t;
    for (i=0;i<3;i++)
     p[i]=cubic_a[0][i]
        +(cubic_a[1][i]*t)
        +(cubic_a[2][i]*tt)
        +(cubic_a[3][i]*ttt);
    return p;
    }
//---------------------------------------------------------------------------
double cubic_d(double *p)       // return closest distance from point to cubic
    {
    int i,j;
    double t,tt,t0,t1,dt,
           l,ll,a,*q;
    tt=-1.0; ll=-1.0; t0=0.0; t1=1.001; dt=0.05;
    for (j=0;j<3;j++)
        {
        for (t=t0;t<=t1;t+=dt)
            {
            q=cubic(t);
            for (l=0.0,i=0;i<3;i++) l+=(p[i]-q[i])*(p[i]-q[i]);
            if ((ll<0.0)||(ll>l)){ ll=l; tt=t; }
            }
        t0=tt-dt; if (t0<0.0) t0=0.0;
        t1=tt+dt; if (t1>1.0) t1=1.0;
        dt*=0.2;
        }
    return sqrt(ll);
    }
//---------------------------------------------------------------------------
void gl_draw()
    {
    int i;
    double t,p[3],dp;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_CULL_FACE);

    // GL render
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDisable(GL_DEPTH_TEST);

                    glColor3f(0.2,0.2,0.2); glBegin(GL_LINE_STRIP); for (i=0;i<pnts3;i+=3) glVertex3dv(pnt+i); glEnd();
    glPointSize(5); glColor3f(0.0,0.0,0.7); glBegin(GL_POINTS); for (i=0;i<pnts3;i+=3) glVertex3dv(pnt+i); glEnd(); glPointSize(1);
    cubic_init(pnt);glColor3f(0.2,0.7,0.7); glBegin(GL_LINE_STRIP); for (t=0.0;t<1.001;t+=0.025) glVertex3dv(cubic(t)); glEnd();

    glColor3f(0.0,0.7,0.0); glBegin(GL_POINTS);
    p[2]=0.0; dp=0.01;
    for (p[0]=-1.0;p[0]<1.001;p[0]+=dp)
     for (p[1]=-1.0;p[1]<1.001;p[1]+=dp)
      if (cubic_d(p)<0.05)
       glVertex3dv(p);
    glEnd();

    glFlush();
    SwapBuffers(hdc);
    }
//---------------------------------------------------------------------------

因此,首先调用一次cubic_init来计算系数,然后使用参数获取曲线上的点:

so first you call cubic_init once to compute the coefficients and then to obtain point on curve as function of parameter use:

double pnt[3] = cubic(double t);

现在倒退(我返回最近的距离ll,但是您可以轻松地将其更改为返回tt)

Now the reverse (I return closest distance ll but you can easily change it to return the tt)

double dist = cubic_d(double pnt[3]);

现在,您只需将此端口移植到着色器,并确定片段是否足够接近以弯曲即可渲染(因此,距离(而不是t)也可以提高速度,您可以摆脱最后一个sqrt并使用后一个动力值)

Now you just port this to shader and determine if fragment is close enough to curve to render it (hence the distance instead of t also for speed you can get rid of the last sqrt and use powered values latter).

gl_draw函数使用GL渲染控制点(蓝色)/线条(灰色)的贝塞尔曲线(aqua),然后模拟片段着色器以在(绿色)中渲染厚度为2*0.05的曲线...

The gl_draw function renders control points(blue) / lines(gray) the bezier curve (aqua) with GL and then emulates fragment shader to render curve with thickness 2*0.05 in (green)...

预览:

现在只需要将其移植到GLSL中即可.为了使用GLSL传递顶点的本机方式,您需要将区域扩大一些,如下所示:

Now its just a matter of porting that into GLSL. In order to use GLSL native way of passing vertexes you need to enlarge the area a bit like in here:

但是您需要稍微改变几何形状以占据4个控制点,而不是3个.这应该在几何着色器中...

But you need to change the geometry a bit to account for 4 control points instead of just 3. That stuff should be in geometry shader ...

因此,在几何着色器中,如果距离cubic_d大于厚度,则应执行cubic_init,而在片段着色器discard中.

So in geometry shader you should do the cubic_init, and in fragment shader discard if distance cubic_d is bigger than thickness.

搜索基于:

.可以对搜索循环本身进行一些调整,以提高性能/精度...但是要注意,初始搜索应将曲线采样到至少4-5个块,否则对于某些形状可能无法正常工作.

which I develop for problems like this. The search loop itself can be tweaked a bit to improve performance/precision ... but beware the initial search should sample the curve to at least 4-5 chunks otherwise it might stop working properly for some shapes.

[Edit1]在考虑了GLSL版本后

顶点

// Vertex
#version 400 core
layout(location = 0) in vec2 pos;   // control points (QUADS)
layout(location = 3) in vec3 col;   // color

out vec2 vpos;
out vec3 vcol;

void main()
    {
    vpos=pos;
    vcol=col;
    gl_Position=vec4(pos,0.0,1.0);
    }

几何形状:

//------------------------------------------------------------------------------
// Geometry
//------------------------------------------------------------------------------
#version 400 core
layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 4) out;

uniform float d=0.05;   // half thickness

in vec2 vpos[];
in vec3 vcol[];

out vec2 a0,a1,a2,a3;   // cubic coefficients
out vec3 fcol;          // color
out vec2 fpos;          // position
//------------------------------------------------------------------------------
void main()
    {
    vec4 p0,p1,p2,p3,a,b;
    p0=gl_in[0].gl_Position;
    p1=gl_in[1].gl_Position;
    p2=gl_in[2].gl_Position;
    p3=gl_in[3].gl_Position;
    // compute BEZIER coefficients
    a0.x=                             (    p0.x);
    a1.x=                  (3.0*p1.x)-(3.0*p0.x);
    a2.x=       (3.0*p2.x)-(6.0*p1.x)+(3.0*p0.x);
    a3.x=(p3.x)-(3.0*p2.x)+(3.0*p1.x)-(    p0.x);
    a0.y=                             (    p0.y);
    a1.y=                  (3.0*p1.y)-(3.0*p0.y);
    a2.y=       (3.0*p2.y)-(6.0*p1.y)+(3.0*p0.y);
    a3.y=(p3.y)-(3.0*p2.y)+(3.0*p1.y)-(    p0.y);
    // compute BBOX
    a=p0;                     b=p0;
    if (a.x > p1.x) a.x=p1.x; if (b.x < p1.x) b.x=p1.x;
    if (a.x > p2.x) a.x=p2.x; if (b.x < p2.x) b.x=p2.x;
    if (a.x > p3.x) a.x=p3.x; if (b.x < p3.x) b.x=p3.x;
    if (a.y > p1.y) a.y=p1.y; if (b.y < p1.y) b.y=p1.y;
    if (a.y > p2.y) a.y=p2.y; if (b.y < p2.y) b.y=p2.y;
    if (a.y > p3.y) a.y=p3.y; if (b.y < p3.y) b.y=p3.y;
    // enlarge by d
    a.x-=d; a.y-=d;
    b.x+=d; b.y+=d;
    // pass it as QUAD
    fcol=vcol[0];
    fpos=vec2(a.x,a.y); gl_Position=vec4(a.x,a.y,0.0,1.0); EmitVertex();
    fpos=vec2(a.x,b.y); gl_Position=vec4(a.x,b.y,0.0,1.0); EmitVertex();
    fpos=vec2(b.x,a.y); gl_Position=vec4(b.x,a.y,0.0,1.0); EmitVertex();
    fpos=vec2(b.x,b.y); gl_Position=vec4(b.x,b.y,0.0,1.0); EmitVertex();
    EndPrimitive();
    }

//------------------------------------------------------------------------------

片段:

// Fragment
#version 400 core
uniform float d=0.05;   // half thickness

in vec2 fpos;           // fragment position
in vec3 fcol;           // fragment color
in vec2 a0,a1,a2,a3;    // cubic coefficients

out vec4 col;

vec2 cubic(float t)     // return point on cubic from parameter
    {
    float tt=t*t,ttt=tt*t;
    return a0+(a1*t)+(a2*tt)+(a3*ttt);
    }

void main()
    {
    vec2 p;
    int i;
    float t,tt,t0,t1,dt,l,ll;
    tt=-1.0; ll=-1.0; dt=0.05; t0=0.0; t1=1.0; l=0.0;
    for (i=0;i<3;i++)
        {
        for (t=t0;t<=t1;t+=dt)
            {
            p=cubic(t)-fpos;
            l=length(p);
            if ((ll<0.0)||(ll>l)){ ll=l; tt=t; }
            }
        t0=tt-dt; if (t0<0.0) t0=0.0;
        t1=tt+dt; if (t1>1.0) t1=1.0;
        dt*=0.2;
        }
    if (ll>d) discard;
    col=vec4(fcol,1.0); // ll,tt can be used for coloring or texturing
    }

由于GL_QUADS不再存在,因此每个CUBIC期望以GL_LINES_ADJACENCY的形式出现4个BEZIER控制点:(当我像这样在gl_draw内使用它时:

It expect 4 BEZIER control points per CUBIC in form of GL_LINES_ADJACENCY since GL_QUADS are no more :( When I use it like this (inside gl_draw):

glUseProgram(prog_id);               // use our shaders
i=glGetUniformLocation(prog_id,"d"); // set line half thickness
glUniform1f(i,0.02);
glColor3f(0.2,0.7,0.2);              // color
glBegin(GL_LINES_ADJACENCY); 
for (i=0;i<pnts3;i+=3)
 glVertex3dv(pnt+i);
glEnd();
glUseProgram(0);

结果如下:

粗略比旧的api点状着色器仿真快很多 :).我知道不应该将旧的api和新样式的GLSL着色器混合使用,因此您应该创建 VAO/VBO ,而不要使用glBegin/glEnd ...我很懒惰,仅出于此答案的目的...

and of coarse is a lot of faster than the old api dotted shader emulation :). I know old api and new style GLSL shaders should not be mixed so you should create VAO/VBO instead of using glBegin/glEnd... I am too lazy to do that just for the purpose of this answer ...

以下是非功能示例(每x y个y)(与CPU边点相比):

double pnt[]=                   // cubic curve control points
    {
    +0.9,-0.8,0.0,
    -2.5,+0.8,0.0,
    +2.5,+0.8,0.0,
    -0.9,-0.8,0.0,
    };

如您所见,两种方法都与形状匹配(点使用更大的厚度).为了使它起作用,必须正确设置搜索系数(dt),以免错过解决方案...

As you can see both approaches matches the shape (dots used bigger thickness). In order this to work the search coefficients (dt) must be set properly to not miss a solution...

以您的方式解决三次方问题的PS导致了其中的两组:

PS solving the cubic your way leads to 2 set of these:

我强烈怀疑它的计算速度比简单搜索快得多.

Which I strongly doubt can be much computed faster than simple search.

[Edit2]进一步的改进

我只是简单地更改了几何着色器,以使其将曲线采样为10个片段,并为每个片段分别发出BBOX,从而消除了之前需要处理的大量空白空间.我稍微改变了颜色布局和渲染顺序.

I simply changed the geometry shader so that it sample the curve into 10 segments and emit BBOX for each separatelly eliminating a lot of empty space that needed to be processed before. I changed the color layout and rendering order a bit.

这是新结果(与上一个结果相同,但由于较低的空置率而加快了几倍):

This is new result (identical to previous one but several times faster due lower empty space ratio):

这是现在的报道范围:

在覆盖范围之前是控制点的BBOX + d的放大,在这种情况下,该范围要大得多,然后弯曲(两个控制点在外部).

Before the coverage was BBOX of control points + enlargement by d which in this case was much bigger then curve itself (2 control points are outside view).

此处更新了几何着色器:

//------------------------------------------------------------------------------
// Geometry
//------------------------------------------------------------------------------
#version 400 core
layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 40) out;  // 4*n <= 60

uniform float d=0.05;   // half thickness

in vec2 vpos[];
in vec3 vcol[];

out vec2 a0,a1,a2,a3;   // cubic coefficients
out vec3 fcol;          // color
out vec2 fpos;          // position
//------------------------------------------------------------------------------
vec2 cubic(float t)     // return point on cubic from parameter
    {
    float tt=t*t,ttt=tt*t;
    return a0+(a1*t)+(a2*tt)+(a3*ttt);
    }
//------------------------------------------------------------------------------
void main()
    {
    float t,dt=1.0/10.0;    // 1/n
    vec2 p0,p1,p2,p3,a,b;
    p0=gl_in[0].gl_Position.xy;
    p1=gl_in[1].gl_Position.xy;
    p2=gl_in[2].gl_Position.xy;
    p3=gl_in[3].gl_Position.xy;
    // compute BEZIER coefficients
    a0.x=                             (    p0.x);
    a1.x=                  (3.0*p1.x)-(3.0*p0.x);
    a2.x=       (3.0*p2.x)-(6.0*p1.x)+(3.0*p0.x);
    a3.x=(p3.x)-(3.0*p2.x)+(3.0*p1.x)-(    p0.x);
    a0.y=                             (    p0.y);
    a1.y=                  (3.0*p1.y)-(3.0*p0.y);
    a2.y=       (3.0*p2.y)-(6.0*p1.y)+(3.0*p0.y);
    a3.y=(p3.y)-(3.0*p2.y)+(3.0*p1.y)-(    p0.y);
    p1=cubic(0.0);
    for (t=dt;t < 1.001;t+=dt)
        {
        p0=p1; p1=cubic(t);
        // compute BBOX
        a=p0;                     b=p0;
        if (a.x > p1.x) a.x=p1.x; if (b.x < p1.x) b.x=p1.x;
        if (a.y > p1.y) a.y=p1.y; if (b.y < p1.y) b.y=p1.y;
        // enlarge by d
        a.x-=d; a.y-=d;
        b.x+=d; b.y+=d;
        // pass it as QUAD
        fcol=vcol[0];
        fpos=vec2(a.x,a.y); gl_Position=vec4(a.x,a.y,0.0,1.0); EmitVertex();
        fpos=vec2(a.x,b.y); gl_Position=vec4(a.x,b.y,0.0,1.0); EmitVertex();
        fpos=vec2(b.x,a.y); gl_Position=vec4(b.x,a.y,0.0,1.0); EmitVertex();
        fpos=vec2(b.x,b.y); gl_Position=vec4(b.x,b.y,0.0,1.0); EmitVertex();
        EndPrimitive();
        }
    }
//------------------------------------------------------------------------------

我的gfx卡有60个顶点限制,因此当我输出模拟QUAD的三角带时,​​段的限制为60/4 = 15我使用了n=10只是为了确保它在较低的硬件上运行.为了更改段数,请参阅带有n

My gfx card has 60 vertex limit so as I output triangle strips emulating QUADs the limit on segments is 60/4 = 15 I used n=10 just to be sure it runs on lower HW. In order to change the number of segments see the 2 lines with comment containing n

[Edit3]更好地覆盖有用/空空间比率

我将AABB BBOX的覆盖范围更改为〜OOB BBOX,没有重叠.这也允许将t的实际范围传递到片段中,从而将搜索速度提高了约10倍.更新的着色器:

I changed the AABB BBOX coverage to ~OOB BBOX without overlaps. This also allows to pass actual range of t into fragment speeding up the search ~10 times. Updated shaders:

顶点:

// Vertex
#version 400 core
layout(location = 0) in vec2 pos;   // control points (QUADS)
layout(location = 3) in vec3 col;   // color

out vec2 vpos;
out vec3 vcol;

void main()
    {
    vpos=pos;
    vcol=col;
    gl_Position=vec4(pos,0.0,1.0);
    }

几何形状:

//------------------------------------------------------------------------------
// Geometry
//------------------------------------------------------------------------------
#version 400 core
layout(lines_adjacency) in;
layout(triangle_strip, max_vertices = 40) out;  // 4*n <= 60

uniform float d=0.05;   // half thickness

in vec2 vpos[];
in vec3 vcol[];

out vec2 a0,a1,a2,a3;   // cubic coefficients
out vec3 fcol;          // color
out vec2 fpos;          // position
out vec2 trange;        // t range of chunk
//------------------------------------------------------------------------------
vec2 cubic(float t)     // return point on cubic from parameter
    {
    float tt=t*t,ttt=tt*t;
    return a0+(a1*t)+(a2*tt)+(a3*ttt);
    }
//------------------------------------------------------------------------------
void main()
    {
    int i,j,n=10,m=10;              // n,m
    float t,dd,d0,d1,dt=1.0/10.0;   // 1/n
    float tt,dtt=1.0/100.0;         // 1/(n*m)
    vec2 p0,p1,p2,p3,u,v;
    vec2 q0,q1,q2,q3;
    p0=gl_in[0].gl_Position.xy;
    p1=gl_in[1].gl_Position.xy;
    p2=gl_in[2].gl_Position.xy;
    p3=gl_in[3].gl_Position.xy;
    // compute BEZIER coefficients
    a0.x=                             (    p0.x);
    a1.x=                  (3.0*p1.x)-(3.0*p0.x);
    a2.x=       (3.0*p2.x)-(6.0*p1.x)+(3.0*p0.x);
    a3.x=(p3.x)-(3.0*p2.x)+(3.0*p1.x)-(    p0.x);
    a0.y=                             (    p0.y);
    a1.y=                  (3.0*p1.y)-(3.0*p0.y);
    a2.y=       (3.0*p2.y)-(6.0*p1.y)+(3.0*p0.y);
    a3.y=(p3.y)-(3.0*p2.y)+(3.0*p1.y)-(    p0.y);
    q2=vec2(0.0,0.0);
    q3=vec2(0.0,0.0);
    // sample curve by chunks
    for (p1=cubic(0.0),i=0,t=dt;i<n;i++,t+=dt)
        {
        // sample point
        p0=p1; p1=cubic(t); q0=q2; q1=q3;
        // compute ~OBB enlarged by D
        u=normalize(p1-p0);
        v=vec2(u.y,-u.x);
        // resample chunk to compute enlargement
        for (d0=0.0,d1=0.0,tt=t-dtt,j=2;j<m;j++,tt-=dtt)
            {
            dd=dot(cubic(tt)-p0,v);
            d0=max(-dd,d0);
            d1=max(+dd,d1);
            }
        d0+=d; d1+=d; u*=d;
        d0*=1.25; d1*=1.25; // just to be sure
        // enlarge radial
        q2=p1+(v*d1);
        q3=p1-(v*d0);
        // enlarge axial
        if (i==0)
            {
            q0=p0+(v*d1)-u;
            q1=p0-(v*d0)-u;
            }
        if (i==n-1)
            {
            q2+=u;
            q3+=u;
            }
        // pass it as QUAD
        fcol=vcol[0]; trange=vec2(t-dt,t);
        fpos=q0; gl_Position=vec4(q0,0.0,1.0); EmitVertex();
        fpos=q1; gl_Position=vec4(q1,0.0,1.0); EmitVertex();
        fpos=q2; gl_Position=vec4(q2,0.0,1.0); EmitVertex();
        fpos=q3; gl_Position=vec4(q3,0.0,1.0); EmitVertex();
        EndPrimitive();
        }
    }
//------------------------------------------------------------------------------*

片段:

// Fragment
#version 400 core

//#define show_coverage

uniform float d=0.05;   // half thickness

in vec2 fpos;           // fragment position
in vec3 fcol;           // fragment color
in vec2 a0,a1,a2,a3;    // cubic coefficients
in vec2 trange;         // t range of chunk

out vec4 col;

vec2 cubic(float t)     // return point on cubic from parameter
    {
    float tt=t*t,ttt=tt*t;
    return a0+(a1*t)+(a2*tt)+(a3*ttt);
    }

void main()
    {
    vec2 p;
    int i,n;
    float t,tt,t0,t1,dt,l,ll;
    tt=-1.0; ll=-1.0; l=0.0;
    #ifdef show_coverage
    t0=0.0; t1=1.0; dt=0.05; n=3;
    #else
    t0=trange.x; n=2;
    t1=trange.y;
    dt=(t1-t0)*0.1;
    #endif
    for (i=0;i<n;i++)
        {
        for (t=t0;t<=t1;t+=dt)
            {
            p=cubic(t)-fpos;
            l=length(p);
            if ((ll<0.0)||(ll>l)){ ll=l; tt=t; }
            }
        t0=tt-dt; if (t0<0.0) t0=0.0;
        t1=tt+dt; if (t1>1.0) t1=1.0;
        dt*=0.2;
        }
    #ifdef show_coverage
    if (ll>d) col=vec4(0.1,0.1,0.1,1.0); else
    #else
    if (ll>d) discard;
    #endif
    col=vec4(fcol,1.0);
    }

并预览(曲线+覆盖率):

And preview (curve + coverage):

然后弯曲:

因为您可以看到交叉覆盖处的接缝是由于覆盖渲染而没有融合的缘故.曲线本身就可以.

as You can see the seam at the crossing wit hcoverage is due to coverage rendering without blending. The curve itself is OK.

d0,d1参数是到实际块OBB轴向(u)的最大垂直距离,该距离被d放大并确定放大25%.看起来非常合适.我怀疑进一步的优化会带来很多好处,因为此结果与覆盖范围的完美契合非常接近...

The d0,d1 parameters are the max perpendicular distances to th actual chunk OBB axial axis (u) enlarged by d and scaled up by 25% just to be sure. Looks like it fits very good. I doubt there is much to be gained by further optimizations as this result is pretty close to perfect fit of the coverage...

#define show_coverage仅允许查看将什么几何传递给片段着色器...

the #define show_coverage just enables to view what geometry is passed to fragment shader ...

这篇关于是否可以表达"t"?三次贝塞尔曲线方程式的变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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