在OpenGL中利用GL_MAP1_VERTEX_3函数生成多条Bezier曲线

mo49yndu  于 2023-02-22  发布在  其他
关注(0)|答案(1)|浏览(109)

因此,我试图创建一个任意曲线形状使用OpenGL和目前我的代码只能产生一个曲线之间的指定控制点,下面是我的OpenGL代码:

#include <GL/glut.h>
#include <stdlib.h>

GLfloat controlPoints[18][3] =
{
    {0.0, 8.0, 0.0},
    { -1.5, 3.0, 0.0}, //2
    {-5.5, 4.0, 0.0},

    {-5.5, 4.0, 0.0},
    {-2.5, 0.0, 0.0}, //4
    {-6.0, -4.0, 0.0}, 

    {-6.0, -4.0, 0.0},
    {-1.5, -3.0, 0.0}, //6
    {0.0, -8.0, 0.0},
    
    {0.0, -8.0, 0.0},
    {1.0, -3.0, 0.0}, //8
    {6.0, -5.0, 0.0},

    {6.0, -5.0, 0.0},
    {3.0, 0.0, 0.0}, //10
    {6.5, 4.5, 0.0},
    
    {6.5, 4.5, 0.0},
    {1.5, 3.0, 0.0}, //12
    {0.0, 8.0, 0.0}
    
};

void init(void)
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);
    for (int i = 0; (i + 3) < 3; i += 3)
    {
        glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &controlPoints[i][0]);
    }
    //glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &controlPoints2[0][0]);

    glEnable(GL_MAP1_VERTEX_3);
    // The evaluator with a stride of 3 and an order of 4

}

void display(void)
{
    int i;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);

    //draw(controlPoints);
    //draw(controlPoints2);

    glBegin(GL_LINE_STRIP);
    {
        for (int i = 0; i <= 18; i++)
        {
            glEvalCoord1f((GLfloat)i / 18.0);
        }
    }
    glEnd();

    glBegin(GL_LINE_STRIP);
    {
        for (i = 0; i < 18; i++)
        {
            glVertex3fv(&controlPoints[i][0]);

        }
    }
    glEnd();

glPointSize(6.0);
    glColor3f(0.0, 0.0, 1.0);
    glBegin(GL_POINTS);
    {
        for (i = 0; i < 18; i++)
        {
            glVertex3fv(&controlPoints[i][0]);

        }
    }
    glEnd();
void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
    {
        glOrtho(-10.0, 10.0, -10.0 * (GLfloat)h / (GLfloat)w, 10.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0);
    }
    else
    {
        glOrtho(-10.0 * (GLfloat)h / (GLfloat)w, 10.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0, -10.0, 10.0);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 27:
        exit(0);
        break;
    }
}

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutMainLoop();
    return 0;
}

如何修改代码的初始化部分,以便能够在三个控制点之间生成6条曲线,总计18条?如果不可能,是否可以使用GL_LINE_STRIP完成?
下面是我当前的输出:

tv6aics1

tv6aics11#

我的建议-完全避免openGL求值器!
除了90年代的一些SGI机器外,还没有GPU供应商为它们增加硬件支持,因此它福尔斯了一个相当低效的软件实现。
不管怎样,你的代码有一些问题...

glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 
                4, ///< this says you want 4 control points per curve
                &controlPoints[i][0]);

但是,控制点中存在以下问题:

GLfloat controlPoints[18][3] =
{
    {0.0, 8.0, 0.0},
    { -1.5, 3.0, 0.0}, //2
    {-5.5, 4.0, 0.0}, ///< I'm assuming this is the last control point you want?

    {-5.5, 4.0, 0.0}, ///< however this is duplicated here?

看起来好像您想要一条二次曲线?(即每条曲线有3个控制点?)

// enable evaluators
    glEnable(GL_MAP1_VERTEX_3);

    // step through each triplet of CV's
    for(int cv = 0; cv < 18; cv += 3) {

        // specify the control point array
        glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 
                3, ///< each vertex has 3 floats. 
                3, ///< I assume you want 3? (as in 3x CV per curve)
                &controlPoints[cv][0]);

        // render this curve segment
        glBegin(GL_LINE_STRIP);
        {
            // choose how many divisions you want
            int NUM_DIVISIONS = 32;
            for (int i = 0; i <= NUM_DIVISIONS; i++)
            {
                glEvalCoord1f((GLfloat)i / (GLfloat) NUM_DIVISIONS);
            }
        }
        glEnd();
    }

    glDisable(GL_MAP1_VERTEX_3);

但是,正如我上面所说,GL评估器很糟糕。
实际上,自己编写代码要容易得多。
一个选项是简单地镶嵌每条曲线,然后渲染 (这将与您当前的控制点布局一起工作)

void render_quadratic_curves(
    GLfloat controlPoints[][3], 
    int num_curves, 
    int num_divisions) {

    int out_size_of_each_curve = (num_divisions + 1) * 3;

    // allocate enough memory to store a curves
    GLfloat* temp = new GLfloat[out_size_of_each_curve];

    // re-render from the same vertex array. 
    glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, temp);
    glEnableClientState(GL_VERTEX_ARRAY);

    for(int curve = 0; curve < num_curves; ++curve) {

       // pointers to the control points for this curve
       const GLfloat* P0 = controlPoints[3 * curve + 0];
       const GLfloat* P1 = controlPoints[3 * curve + 1];
       const GLfloat* P2 = controlPoints[3 * curve + 2];

       for(int division = 0; division <= num_divisions; ++division) {

           GLfloat t = (GLfloat) division / (GLfloat) NUM_DIVISIONS;
           GLfloat inv_t = (1.0f - t);

           // compute bezier coefficients for quadratic curve
           GLfloat B0 = inv_t * inv_t;
           GLfloat B1 = 2.0f * inv_t * t;
           GLfloat B2 = t * t;

           // compute XYZ coordinates
           GLfloat x = P0[0] * B0 + 
                       P1[0] * B1 + 
                       P2[0] * B2;
           GLfloat y = P0[1] * B0 + 
                       P1[1] * B1 + 
                       P2[1] * B2;
           GLfloat z = P0[2] * B0 + 
                       P1[2] * B1 + 
                       P2[2] * B2;

           // insert into the buffer for rendering
           temp[3 * division + 0] = x;
           temp[3 * division + 1] = y;
           temp[3 * division + 2] = z;
       }

       // render this curve in one go as a strip
       glDrawArrays(GL_LINE_STRIP, 0, num_divisions + 1);
    }

    // cleanup
    glDisableClientState(GL_VERTEX_ARRAY);
    delete [] temp;
}

但是,在上面的示例中,实际上有一个循环,因此可以使用GL_LINE_LOOP一次性完成 (这种方法非常适合VBO)

void render_quadratic_curves_as_loop(
    GLfloat controlPoints[][3], 
    int num_curves, 
    int num_divisions) {

    // curves are 1 vertex smaller in size than previously,
    // since the start vertex of one curve, is shared with the 
    // last vertex of the previous curve
    int out_size_of_each_curve = num_divisions * 3;

    // allocate enough memory to store all of the curves
    GLfloat* temp = new GLfloat[out_size_of_each_curve * num_curves];

    for(int curve = 0; curve < num_curves; ++curve) {

       GLfloat* this_curve = temp + curve * out_size_of_each_curve;

       // pointers to the control points for this curve
       const GLfloat* P0 = controlPoints[3 * curve + 0];
       const GLfloat* P1 = controlPoints[3 * curve + 1];
       const GLfloat* P2 = controlPoints[3 * curve + 2];

       // note! I am using less than here! 
       // the last vertex of each curve is simply the first 
       // vertex of the next one... 
       for(int division = 0; division < num_divisions; ++division) {

           GLfloat t = (GLfloat) division / (GLfloat) NUM_DIVISIONS;
           GLfloat inv_t = (1.0f - t);

           // compute bezier coefficients for quadratic curve
           GLfloat B0 = inv_t * inv_t;
           GLfloat B1 = 2.0f * inv_t * t;
           GLfloat B2 = t * t;

           // compute XYZ coordinates
           GLfloat x = P0[0] * B0 + 
                       P1[0] * B1 + 
                       P2[0] * B2;
           GLfloat y = P0[1] * B0 + 
                       P1[1] * B1 + 
                       P2[1] * B2;
           GLfloat z = P0[2] * B0 + 
                       P1[2] * B1 + 
                       P2[2] * B2;

           // insert into the buffer for rendering
           this_curve[3 * division + 0] = x;
           this_curve[3 * division + 1] = y;
           this_curve[3 * division + 2] = z;
       }
    }

    // re-render from the same vertex array. 
    // This *could* be replaced with a VBO. 
    glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, temp);
    glEnableClientState(GL_VERTEX_ARRAY);
    
    // render all of the curves in one go. 
    glDrawArrays(GL_LINE_LOOP, 0, out_size_of_each_curve * num_curves);

    // cleanup
    glDisableClientState(GL_VERTEX_ARRAY);
    delete [] temp;
}

// You'll now need to remove the duplicate CV's from your array
GLfloat controlPoints[12][3] =
{
    {0.0, 8.0, 0.0},
    { -1.5, 3.0, 0.0}, //2

    {-5.5, 4.0, 0.0},
    {-2.5, 0.0, 0.0}, //4

    {-6.0, -4.0, 0.0},
    {-1.5, -3.0, 0.0}, //6
    
    {0.0, -8.0, 0.0},
    {1.0, -3.0, 0.0}, //8

    {6.0, -5.0, 0.0},
    {3.0, 0.0, 0.0}, //10
    
    {6.5, 4.5, 0.0},
    {1.5, 3.0, 0.0}, //12
};
render_quadratic_curves_as_loop(controlPoints, 6, 32);

如果您实际上希望每条曲线有4个CV,那么您可以轻松地将其扩展为三次贝塞尔曲线。

// obviously each curve will now need an additional CV
void render_cubic_curves_as_loop(
    GLfloat controlPoints[][3], 
    int num_curves, 
    int num_divisions) {

    // curves are 1 vertex smaller in size than previously,
    // since the start vertex of one curve, is shared with the 
    // last vertex of the previous curve
    int out_size_of_each_curve = num_divisions * 3;

    // allocate enough memory to store all of the curves
    GLfloat* temp = new GLfloat[out_size_of_each_curve * num_curves];

    for(int curve = 0; curve < num_curves; ++curve) {

       GLfloat* this_curve = temp + curve * out_size_of_each_curve;

       // pointers to the control points for this curve
       const GLfloat* P0 = controlPoints[4 * curve + 0];
       const GLfloat* P1 = controlPoints[4 * curve + 1];
       const GLfloat* P2 = controlPoints[4 * curve + 2];
       const GLfloat* P3 = controlPoints[4 * curve + 2];

       // note! I am using less than here! 
       // the last vertex of each curve is simply the first 
       // vertex of the next one... 
       for(int division = 0; division < num_divisions; ++division) {

           GLfloat t = (GLfloat) division / (GLfloat) NUM_DIVISIONS;
           GLfloat inv_t = (1.0f - t);

           // compute bezier coefficients for cubic curve
           GLfloat B0 = inv_t * inv_t * inv_t;
           GLfloat B1 = 3.0f * inv_t * inv_t * t;
           GLfloat B2 = 3.0f * inv_t * t * t;
           GLfloat B2 = t * t;

           // compute XYZ coordinates
           GLfloat x = P0[0] * B0 + 
                       P1[0] * B1 + 
                       P2[0] * B2 + 
                       P3[0] * B3;
           GLfloat y = P0[1] * B0 + 
                       P1[1] * B1 + 
                       P2[1] * B2 + 
                       P3[1] * B3;
           GLfloat z = P0[2] * B0 + 
                       P1[2] * B1 + 
                       P2[2] * B2 + 
                       P3[2] * B3;

           // insert into the buffer for rendering
           this_curve[3 * division + 0] = x;
           this_curve[3 * division + 1] = y;
           this_curve[3 * division + 2] = z;
       }
    }

    // re-render from the same vertex array. 
    // This *could* be replaced with a VBO. 
    glVertexPointer(3, GL_FLOAT, sizeof(float) * 3, temp);
    glEnableClientState(GL_VERTEX_ARRAY);
    
    // render all of the curves in one go. 
    glDrawArrays(GL_LINE_LOOP, 0, out_size_of_each_curve * num_curves);

    // cleanup
    glDisableClientState(GL_VERTEX_ARRAY);
    delete [] temp;
}

注意:在现代硬件上,如果你有镶嵌着色器,那通常是最好的选择。如果你有硬件示例,你可以指定基本系数作为共享顶点缓冲区,并且控制点可以指定每个示例。
1.生成VBO以存储混合系数,并且将VBO设置为具有顶点除数0。

void populate_shared_vertex_data_for_VBO(float* out, int NUM_DIVISIONS) {

    for(int i = 0; i <= NUM_DIVISIONS; ++i) {

         GLfloat t = (GLfloat) division / (GLfloat) (NUM_DIVISIONS + 1);
         GLfloat inv_t = (1.0f - t);

         // compute bezier coefficients for cubic curve
         GLfloat B0 = inv_t * inv_t * inv_t;
         GLfloat B1 = 3.0f * inv_t * inv_t * t;
         GLfloat B2 = 3.0f * inv_t * t * t;
         GLfloat B2 = t * t;

         out[0] = B0;
         out[1] = B1;
         out[2] = B2;
         out[3] = B3;
         out += 4;
    }
}

1.将所有曲线的控制点加载到单个BIG VBO中,设置4个每示例属性(即指定4个不同的着色器输入,每个CV一个,将每个步幅设置为sizeof(Cubic_Curve_CVS),并将除数设置为1)。

struct Cubic_Curve_CVS {
   float P0[3];
   float P1[3];
   float P2[3];
   float P3[3];
};

Cubic_Curve_CVS VBO_DATA[NUM_CURVES]; ///< load this

顶点着色器最终实现起来非常简单:

#version 450

uniform mat4 vs_mvp;

// share this buffer between all indices, 
// i.e. glVertexAttribDivisor(0, 0);
layout(location = 0) in vec4 vs_coeffs;

// make these per-instance attributes
// i.e. :
// glVertexAttribDivisor(1, 1);
// glVertexAttribDivisor(2, 1);
// glVertexAttribDivisor(3, 1);
// glVertexAttribDivisor(4, 1);
layout(location = 1) in vec4 vs_CV0;
layout(location = 2) in vec4 vs_CV1;
layout(location = 3) in vec4 vs_CV2;
layout(location = 4) in vec4 vs_CV3;

void main()
{
  float B0 = vs_coeffs.x;
  float B1 = vs_coeffs.y;
  float B2 = vs_coeffs.z;
  float B3 = vs_coeffs.w;

  vec4 V = vs_CV0 * B0 +
           vs_CV1 * B1 + 
           vs_CV2 * B2 +
           vs_CV3 * B3;
  gl_Position = vs_mvp * V;
}

然后使用glDrawArraysInstanced一次性渲染整个场景。

相关问题