学习OpenGL(一):绘制三角形

目录

  • 引言
  • 代码示例
  • 代码解析
    • 初始化
    • 绘制

本系列文章主要是记录学习OpenGL的过程,旨在驱动学习理解OpenGL,最终达到能够使用相关接口解决实际项目问题,学习流程参考《LearnOpenGL》。主要展现形式是"代码示例+接口分析"的形式,编码主要是基于Qt封装的OpenGL模块展开,这样对于我来说更加熟悉。

引言

OpenGL自身是一个巨大的状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。OpenGL的状态通常被称为OpenGL上下文(Context)。我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。

第一个项目绘制三角形,如下图所示:

学习OpenGL(一):绘制三角形_第1张图片

代码示例

#include 
#include 

class CustomGLWidget : public QOpenGLWidget, QOpenGLFunctions_3_3_Core
{
    Q_OBJECT
public:
    explicit CustomGLWidget(QWidget *parent = nullptr);

signals:

public slots:

protected:
    void initializeGL() override;
    void resizeGL(int w, int h) override;
    void paintGL() override;
};
static unsigned int VBO, VAO;
static unsigned int shaderProgram;

static float vertices[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f, 0.5f, 0.0f,
};

static const char* vertexShaderSource = "#version 330 core \n"
                                        "layout (location = 0) in vec3 aPos;\n"
                                        "void main()\n"
                                        "{\n"
                                        "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
                                        "}\0";

static const char* fragmentShaderSource = "#version 330 core\n"
                                          "out vec4 FragColor;\n"
                                          "void main()\n"
                                          "{\n"
                                          "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
                                          "}\0";

CustomGLWidget::CustomGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
{

}

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

    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    // 绑定顶点数组对象
    glBindVertexArray(VAO);

    // 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 设定顶点属性指针
    glVertexAttribPointer(0 ,3, GL_FLOAT, GL_FALSE, 3* sizeof(float), nullptr);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    // 编译顶点着色器
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    // 编译片段着色器
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    // 链接
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
}

void CustomGLWidget::resizeGL(int w, int h)
{
    QOpenGLWidget::resizeGL(w, h);
}

void CustomGLWidget::paintGL()
{
    // 背景颜色
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // 绘制三角形
    glUseProgram(shaderProgram);
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

代码解析

借助Qt使用OpenGL,主要要使用了两个类QOpenGLWidget以及QOpenGLFunctions_3_3_Core。QOpenGLWidget为窗体部件,需要重写initializeGL、resizeGL以及paintGL函数。QOpenGLFunctions_3_3_Core则提供对应版本的OpenGL函数,这里使用的是3.3版本与《LearnOpenGL》相同。

OpenGL规范严格规定了每个函数该如何执行,以及它们的输出值。至于内部具体每个函数是如何实现(Implement)的,将由OpenGL库的开发者自行决定(译注:这里开发者是指编写OpenGL库的人)。因为OpenGL规范并没有规定实现的细节,具体的OpenGL库允许使用不同的实现,只要其功能和结果与规范相匹配(亦即,作为用户不会感受到功能上的差异)。

相关的封装使得Qt能够更加方便的调用OpenGL函数,也就是更容易的调用显卡中的函数。

实际的OpenGL库的开发者通常是显卡的生产商。你购买的显卡所支持的OpenGL版本都为这个系列的显卡专门开发的。当你使用Apple系统的时候,OpenGL库是由Apple自身维护的。在Linux下,有显卡生产商提供的OpenGL库,也有一些爱好者改编的版本。这也意味着任何时候OpenGL库表现的行为与规范规定的不一致时,基本都是库的开发者留下的bug。

初始化

initializeOpenGLFunctions将Qt相关函数与显卡提供的OpenGL库建立联系,也就是找到显卡中所提供的OpenGL库的对应的函数地址。

通过顶点缓冲对象(Vertex Buffer Objects, VBO)管理顶点数据,在GPU内存(通常被称为显存)中储存大量顶点,借助这些缓冲对象可以大批量的把数据从CPU传递至GPU中,而不是逐个发送。

顶点数组对象(Vertex Array Object, VAO)可以像顶点缓冲对象那样被绑定,任何随后的顶点属性调用都会储存在这个VAO中,用于管理VBO中的数据,方便代码中的调用。

顶点数组对象会储存以下这些内容:
glEnableVertexAttribArray和glDisableVertexAttribArray的调用。
通过glVertexAttribPointer设置的顶点属性配置。
通过glVertexAttribPointer调用与顶点属性关联的顶点缓冲对象。

学习OpenGL(一):绘制三角形_第2张图片

glCompileShader编译着色器,glLinkProgram链接着色器程序,也就是通过着色器语言GLSL(OpenGL Shading Language)编写的vertexShaderSource、fragmentShaderSource 编译生成可以在GPU上跑的程序,用于相关元素的渲染,渲染流程如下图所示:

学习OpenGL(一):绘制三角形_第3张图片
这里在《LearnOpenGL》中有详细解释,实际编码只需关系顶点着色器和片段着色器,大概可以理解为顶点着色器控制形状、片段着色器控制颜色。

绘制

glClearColor绘制背景颜色,清除颜色缓冲之后,整个颜色缓冲都会被填充为glClearColor里所设置的颜色。

glUseProgram(shaderProgram)使用已经编译好的着色器程序,glBindVertexArray(VAO)绑定顶点数组对象,glDrawArrays(GL_TRIANGLES, 0, 3)绘制图元。

你可能感兴趣的:(学习OpenGL,学习,qt,c++)