OpenGL学习(一)绘制一个图形

2023-12-05

本文使用Qt进行操作演示。

注意:

坐标原点位于屏幕中心

坐标参数:0.0f - 1.0f

颜色参数:0.0f - 1.0f

OpenGL提供了几种用于绘制几何图形的绘制模式。下面是一些常用的绘制模式:

  1. 点(GL_POINTS) :绘制单个点。

  2. 线(GL_LINES) :绘制一系列相互独立的线段。

  3. 线带(GL_LINE_STRIP) :绘制一系列连接在一起的线段,其中每个线段的起点都是前一个线段的终点。

  4. 线环(GL_LINE_LOOP) :与线带类似,但会在最后一个点和第一个点之间绘制一条线段,形成闭合的图形。

  5. 三角形(GL_TRIANGLES) :绘制一系列相互独立的三角形。

  6. 三角形带(GL_TRIANGLE_STRIP) :绘制一系列连接在一起的三角形,其中每个三角形的前两个顶点是前一个三角形的后两个顶点。

  7. 三角形扇形(GL_TRIANGLE_FAN) :绘制一系列共享一个公共顶点的三角形。

除了上述绘制模式,还有其他一些高级的绘制模式,如四边形、多边形等。此外,还可以使用索引缓冲对象(Index Buffer Objects,简称IBO)来指定绘制的顺序,从而实现更复杂的绘制。

绘制模式可以通过以下方式设置:

glDrawArrays(GL_TRIANGLES, 0, numVertices);

其中,第一个参数指定绘制的模式,第二个参数指定要绘制的顶点的起始位置,第三个参数指定要绘制的顶点数量。

1.绘制三角形

代码如下:

myopenglwidget.h
// An highlighted block
#ifndef MYOPENGLWIDGET_H
#define MYOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>

class MyOpenGLWidget : public QOpenGLWidget,protected QOpenGLFunctions  // 可直接使用QOpenGLFunctions中的OpenGL函数
{
    Q_OBJECT
public:
    explicit MyOpenGLWidget (QWidget * parent = 0);

protected:
    void initializeGL();
    void paintGL();
    void resizeGL(int width,int height);

private:
    QOpenGLShaderProgram * program;     // 着色器程序
};

#endif // MYOPENGLWIDGET_H

myopenglwidget.cpp
#include "myopenglwidget.h"

MyOpenGLWidget::MyOpenGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{
}

void MyOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();    //调用该函数来初始化OpenGL函数,以便在后续使用OpenGL的函数。

    QSurfaceFormat format;
    format.setSamples(4); // 设置采样数为4(可根据需要进行调整)
    setFormat(format);

    glEnable(GL_MULTISAMPLE);   // 启用多重采样

    //创建一个顶点着色器对象,并将其指针赋值给vShader变量。第一个参数QOpenGLShader::Vertex表示创建的是一个顶点着色器对象,第二个参数this表示将当前窗口或部件作为父对象。
    QOpenGLShader *vShader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    //定义了一个字符串变量vsrc,用于存储顶点着色器代码。
    const char* vsrc =
        "attribute vec4 vPosition;  \n"     //定义了一个属性变量vPosition,用于接收传入的顶点坐标。
        "void main(){\n"                    //定义了一个主函数,其功能是将传入的顶点坐标赋值给内置变量gl_Position,从而确定此顶点的屏幕位置。
        "   gl_Position = vPosition;\n"
        "}\n";
    vShader->compileSourceCode(vsrc);       //将顶点着色器代码编译成可执行代码,并将结果存储在vShader对象中。如果编译失败,则会抛出异常。

    //创建一个片段着色器对象,并将其指针赋值给fShader变量。第一个参数QOpenGLShader::Fragment表示创建的是一个片段着色器对象,第二个参数this表示将当前窗口或部件作为父对象。
    QOpenGLShader *fShader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char* fsrc =
        "void main(){\n"
        "   gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n" // 将输出的颜色设置为黄色
        "}\n";
    fShader->compileSourceCode(fsrc);           //将片段着色器代码编译成可执行代码,并将结果存储在fShader对象中。如果编译失败,则会抛出异常。

    program = new QOpenGLShaderProgram;     //创建一个着色器程序对象,并将其指针赋值给program变量。
    program->addShader(vShader);            //将之前创建并编译好的顶点着色器对象添加到着色器程序中。
    program->addShader(fShader);            //将之前创建并编译好的片段着色器对象添加到着色器程序中。
    program->link();                        //链接着色器程序,将顶点着色器和片段着色器关联起来,并生成最终的可执行程序。
    program->bind();                        //绑定着色器程序,使其成为当前OpenGL上下文中的活动程序。
}
void MyOpenGLWidget::paintGL()
{

    glEnable(GL_MULTISAMPLE);

    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);       //设置清除颜色缓冲区时使用的颜色,这里设置为白色。
    glClear(GL_COLOR_BUFFER_BIT);               //清除颜色缓冲区,将之前设定的清除颜色填充整个窗口。

    GLfloat vertices[] = {                      //定义了一个包含矩形顶点坐标的数组。
        -0.8f, -0.8f,
        0.8f, -0.8f,
        0.0f, 0.8f
    };

    GLuint vPosition = program->attributeLocation("vPosition");             //获取顶点着色器中属性变量vPosition的位置。
    //将顶点数据与属性变量关联起来。vPosition表示属性变量的位置,2表示每个顶点有两个分量,GL_FLOAT表示每个分量的数据类型为浮点型,GL_FALSE表示不需要进行归一化处理,0表示相邻顶点间的偏移量,vertices表示顶点数据数组。
    glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(vPosition);   //启用顶点属性数组。

    glDrawArrays(GL_TRIANGLE_FAN, 0, 3);    //使用顶点数组绘制图元。GL_TRIANGLE_FAN表示绘制三角形扇,0表示从数组中的第一个顶点开始绘制,4表示绘制的顶点数量。
}

void MyOpenGLWidget::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h);
}

对代码进行修改,绘制点(将paintGL函数中代码替换如下):

     // 定义顶点数据
    GLfloat points[] = {
        0.0f, 0.0f, 0.0f,  // 第一个点的位置
        0.5f, 0.5f, 0.0f,  // 第二个点的位置
        -0.5f, -0.5f, 0.0f  // 第三个点的位置
    };

    // 创建顶点缓冲对象(VBO)并将顶点数据传入
    GLuint vbo;     //定义一个无符号整数变量vbo,用于存储VBO的标识符。
    glGenBuffers(1, &vbo);      //生成一个新的VBO,并将其标识符存储在vbo变量中。
    glBindBuffer(GL_ARRAY_BUFFER, vbo);     //绑定(激活)VBO,告诉OpenGL后续的操作都是针对这个VBO的。这里使用GL_ARRAY_BUFFER作为目标,表示这个VBO中存储的是顶点数据。
    //将数据复制到VBO中。sizeof(points)表示点数组占用的字节数,points是指向点数组的指针。GL_STATIC_DRAW表示这个VBO中的数据将被修改很少,并且会被频繁地用于渲染操作。
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    // 启用顶点属性数组
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, 0);

    // 绘制独立的点
    glDrawArrays(GL_POINTS, 0, 3);

    // 禁用顶点属性数组
    glDisableClientState(GL_VERTEX_ARRAY);

绘制线段:(只需修改绘制模式)

    // 绘制一条直线
    glDrawArrays(GL_LINES, 0, 2);

    //绘制多条直线
    //首先顶点数要够
    GLfloat lines[] = {
        0.0f, 0.0f, 0.0f,  // 第一条线起点
        0.5f, 0.5f, 0.0f,  // 第一条线终点
        -0.5f, -0.5f, 0.0f,  // 第二条线起点
        -1.0f,1.0f,0.0f // 第二条线终点 
    };

    //其余不动
    glDrawArrays(GL_LINES, 0, sizeof(points)/sizeof(GLfloat)*3); //*3因为每个点有三个坐标x,y,z
main.cpp
#include "myopenglwidget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyOpenGLWidget w;
    w.resize(400,300);
    w.show();
    return a.exec();
}
控制绘制图形颜色代码:
QOpenGLShader *fShader = new QOpenGLShader(QOpenGLShader::Fragment, this);
const char* fsrc =
    "void main(){\n"
    "   gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n" // 将输出的颜色设置为黄色
    "}\n";
fShader->compileSourceCode(fsrc); //将片段着色器代码编译成可执行代码,并将结果存储在fShader对象中。如果编译失败,则会抛出异常。
顶点数组:
GLfloat vertices[] = {                      //定义了一个包含矩形顶点坐标的数组。
      -0.8f, -0.8f,
      0.8f, -0.8f,
      0.0f, 0.8f
};
使用顶点数组绘制图元:
    glDrawArrays(GL_TRIANGLE_FAN, 0, 3);    //使用顶点数组绘制图元。GL_TRIANGLE_FAN表示绘制三角形扇,0表示从数组中的第一个顶点开始绘制,4表示绘制的顶点数量。

2.绘制两个三角形

其余不变:

void MyOpenGLWidget::paintGL()
{

    glEnable(GL_MULTISAMPLE);

    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);       //设置清除颜色缓冲区时使用的颜色,这里设置为白色。
    glClear(GL_COLOR_BUFFER_BIT);               //清除颜色缓冲区,将之前设定的清除颜色填充整个窗口。

    GLfloat vertices[] = {                      //定义了一个包含矩形顶点坐标的数组。
        -0.9f, 0.9f,
        0.1f, 0.9f,
        -0.9f, 0.5f,

        0.0f,0.0f,
        -0.9f,0.0f,
        -0.9f,-0.5f,

    };

    GLuint vPosition = program->attributeLocation("vPosition");             //获取顶点着色器中属性变量vPosition的位置。
    //将顶点数据与属性变量关联起来。vPosition表示属性变量的位置,2表示每个顶点有两个分量,GL_FLOAT表示每个分量的数据类型为浮点型,GL_FALSE表示不需要进行归一化处理,0表示相邻顶点间的偏移量,vertices表示顶点数据数组。
    glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(vPosition);   //启用顶点属性数组。
    glDrawArrays(GL_TRIANGLE_FAN, 0, 3);    //使用顶点数组绘制图元。GL_TRIANGLE_FAN表示绘制三角形扇,0表示从数组中的第一个顶点开始绘制,4表示绘制的顶点数量。
    glDrawArrays(GL_TRIANGLE_FAN, 3, 3);
    qDebug()<<sizeof(vertices)/(sizeof(GLfloat)*2);
//    glDrawArrays(GL_TRIANGLE_FAN, 3, sizeof(vertices)/sizeof(GLfloat)*3);
}

在 glDrawArrays函数中,第一个参数是绘制模式,第二个参数是顶点数组中起始下标,第三个参数是绘制使用的顶点个数.

绘制独立的三角形还是需要用特定模式:

  • 一步到位
 glDrawArrays(GL_TRIANGLES, 0, sizeof(vertices)/(sizeof(GLfloat)*2));

3.绘制两个不同颜色的三角形

其余不变:

void MyOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    // 创建顶点着色器对象和片段着色器对象
    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    const char *vsrc =
        "attribute vec4 vPosition;\n"
        "void main() {\n"
        "  gl_Position = vPosition;\n"
        "}\n";
    vshader->compileSourceCode(vsrc);

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char *fsrc =
        "uniform vec4 uColor;\n"
        "void main() {\n"
        "  gl_FragColor = uColor;\n"
        "}\n";
    fshader->compileSourceCode(fsrc);

    // 创建着色器程序,并链接顶点着色器和片段着色器
    program = new QOpenGLShaderProgram(this);
    program->addShader(vshader);
    program->addShader(fshader);
    program->link();
}
void MyOpenGLWidget::paintGL()
{
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    GLfloat vertices[] = {
        -0.9f, 0.9f,
         0.1f, 0.9f,
        -0.9f, 0.5f,
         0.0f, 0.0f,
        -0.9f, 0.0f,
        -0.9f,-0.5f
    };

    GLuint vPosition = program->attributeLocation("vPosition");//获取 attribute 变量 vPosition 的位置
    program->bind();//绑定着色器程序

    glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(vPosition);

    program->setUniformValue("uColor", QVector4D(1.0f, 0.0f, 0.0f, 1.0f)); // 设置第一个三角形的颜色为红色
    glDrawArrays(GL_TRIANGLES, 0, 3);

    program->setUniformValue("uColor", QVector4D(0.0f, 0.0f, 1.0f, 1.0f)); // 设置第二个三角形的颜色为蓝色
    glDrawArrays(GL_TRIANGLES, 3, 3);

变化之处:

1.创建片段着色器时需要把颜色替换成一个变量

2.绘制图像之前对赵色器的颜色进行设置修改

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenGL学习(一)绘制一个图形 的相关文章

随机推荐