学习视频链接
1、Open Graphics Library 是一个由 Khronos 组织制定并维护的规范 (Specification)
2、OpenGL 核心是一个 C 库,同时也支持多种语言的派生
3.2 版本和以后版本出现的核心模式也叫可编程管线,提供了更多的灵活性,更高的效率,更重要的是可以更深入的理解图形编程

上图中灰色是不可变成的,3.2 版本以前都是灰色的。上图中的几何着色器也可以不用我们自己来写
早期的 OpenGL 使用的模式 (也就是固定渲染管线),OpenGL 的大多数功能都被库隐藏起来,容易使用和理解,但是效率太低,开发者很少能控制 OpenGL 如何进行计算。因此从 OpenGL3.2 开始,推出核心模式
OpenGL 自身是一个巨大的状态机:描述该如何操作的所有变量的大集合
OpenGL 的状态通常补称为上下文 (Cotext)
状态设置函数 (State-changing Function)
状态应用的函数 (State-using Function)

我们通过改变一些上下文变量来改变 OpenGL 状态,从而告诉 OpenGL 如何去绘图
一个对象是指一些选项的信号,代表 OpenGL 状态每一个子集
- struct object_name {
- GLFloat option1;
- GLunit option2;
- GLchar[] name;
- };
通常把 OpenGL 上下文比作一个大的结构体,包含很多子集:
- // OpenGL的状态
- struct OpenGL_Context{
- ...
- object object_Window_Target; // 窗口大小、支持的颜色位数等信息
- ...
- };
当前状态只有一份,如果每次显示不同的效果,都要重新配置,会很麻烦。这时候我们就需要一些小助理 (对象),帮忙记录某些状态的信息,方便复用。
e.g. 如果我们有 10 种子集,每隔子集有 10 种不同的状态,那么我们将需要 100 个助理 (对象)
- // 创建对象
- GLuint objectId = 0;
- glGenObject(1, &objectId); // 给小助理(对象)一个编号
-
- // 绑定对象至上下文
- glBindObject(GL_WINDOW_TARGET, objectId);
-
- // 设置GL_WINDOW_TARGET对象的一些选项
- glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800);
- glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600);
-
-
- // 将上下文的 GL_WINDOW_TARGET 对象设回默认
- glBindObject(GL_WINDOW_TARGET, 0); // 小助理已经记录了上面的内容,可以休息了,需要的时候唤醒就可以了
-
- // 一旦我们重新绑定这个对象到 GL_WINDOW_TARGET 位置,这些选项就会重新生效




1、图示

2、标准化设备坐标 (Normalized Device Coordinates, NDC)
顶点着色器中处理过后,就应该是标准化设备坐标了,x、y 和 z 的值在 1.0 到 1.0 的小段空间 (立方体)。落在范围外的坐标都会被裁剪。
3、顶点着色器:
(1) 它会在 GPU 上创建内存,用于储存我们的顶点数据
通过顶点缓冲对象 (Vertex Buffer 0b jects, VBO) 管理,顶点缓冲对象的缓冲类型是 GL ARRAY_BUFFER
(2) 配置 OpenGL 如何解释这些内存
通过顶点数组对象(Vertex Array Objects, VAO) 管理,数组里的每一个项都对应一个属性的解析
OpenGL 允许我们同时绑定多个缓冲,只要它们是不同的缓冲类型。(每一个缓冲类型类似于前面说的子集,每个 VBO 是一个小助理)
VAO 并不保存实际数据,而是放顶点结构定义

4、编写代码
- #include "openglwidget.h"
-
- float vertices[] = {
- -0.5f, -0.5f, 0.0f,
- 0.5f, -0.5f, 0.0f,
- 0.0f, 0.5f, 0.0f
- };
-
- // 创建VBO和VAO对象,并赋予ID
- unsigned int VAO, VBO;
-
- OpenGLWidget::OpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
- {
-
- }
-
- // 初始化的槽函数
- // 在第一次调用paintGL()或resizeGL()之前调用一次,然后在小部件被分配新的QGLContext时调用一次
- void OpenGLWidget::initializeGL()
- {
- initializeOpenGLFunctions();
-
-
- glGenVertexArrays(1, &VAO);
- glGenBuffers(1, &VBO);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
-
- // 当前绑定到target的缓冲区对象创建一个新的数据存储
- // 如果data不是NULL,则使用来自此指针的数据初始化数据存储
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
-
- // 告知显卡如何解析缓冲里的属性值
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
- // 开启VAO管理的第一个属性值
- glEnableVertexAttribArray(0);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
- }
-
- void OpenGLWidget::resizeGL(int w, int h)
- {
-
- }
-
- void OpenGLWidget::paintGL()
- {
- glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glBindVertexArray(VAO);
- glDrawArrays(GL_TRIANGLES, 0, 3);
- }
目前程序还不能绘制出图像
- #include "openglwidget.h"
-
- float vertices[] = {
- -0.5f, -0.5f, 0.0f,
- 0.5f, -0.5f, 0.0f,
- 0.0f, 0.5f, 0.0f
- };
-
- // 创建VBO和VAO对象,并赋予ID
- unsigned int VAO, VBO;
-
- unsigned int shaderProgram;
-
- const char* vertexShaderScource =
- "#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"
- "}\n\0";
-
- const char* fragmentShaderScource =
- "#version 330 core\n"
- "out vec4 FragColor;\n"
- "void main()\n"
- "{\n"
- "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
- "}\n\0";
-
- OpenGLWidget::OpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
- {
-
- }
-
- // 初始化的槽函数
- // 在第一次调用paintGL()或resizeGL()之前调用一次,然后在小部件被分配新的QGLContext时调用一次
- void OpenGLWidget::initializeGL()
- {
- initializeOpenGLFunctions();
-
-
- glGenVertexArrays(1, &VAO);
- glGenBuffers(1, &VBO);
-
- // 绑定VBO和VAO对象
- glBindVertexArray(VAO);
- glBindBuffer(GL_ARRAY_BUFFER, VBO);
-
- // 当前绑定到target的缓冲区对象创建一个新的数据存储
- // 如果data不是NULL,则使用来自此指针的数据初始化数据存储
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
-
- // 告知显卡如何解析缓冲里的属性值
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
- // 开启VAO管理的第一个属性值
- glEnableVertexAttribArray(0);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
-
- unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
- glShaderSource(vertexShader, 1, &vertexShaderScource, NULL);
- glCompileShader(vertexShader);
-
- unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
- glShaderSource(fragmentShader, 1, &fragmentShaderScource, NULL);
- glCompileShader(fragmentShader);
-
- shaderProgram = glCreateProgram();
- glAttachShader(shaderProgram, vertexShader);
- glAttachShader(shaderProgram, fragmentShader);
- glLinkProgram(shaderProgram);
-
- glDeleteShader(vertexShader);
- glDeleteShader(fragmentShader);
- }
-
- void OpenGLWidget::resizeGL(int w, int h)
- {
-
- }
-
- void OpenGLWidget::paintGL()
- {
- glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glUseProgram(shaderProgram);
- glBindVertexArray(VAO);
- glDrawArrays(GL_TRIANGLES, 0, 3);
- }

1、索引缓冲对象 (Element Buffer Object, EBO, 也叫 Index Buffer Object, IBO) 可以绘制两个三角形来组成一个矩形 (OpenGL 主要处理三角形)。这会生成下面的顶点的集合:

2、使用以前的方法画


添加语句画出线条

3、现在的方法画

再 VAO 解除绑定前绑定 EBO:


4、图示
目标是 GL_ELEMENT_ARRAY_BUEFER 的时候,VAO 会储存 glBindBuffer 的函数调用。这也意味着它也会储存解绑调用。
VAO 不会存储 GL_ARRAY_BUFFER (VBO) 的 glBindBuffer 的函数调用

3 中如果提前提前解绑 VAO 上图的黄色线就没有了