OpenGL - 绘制三角形

OpenGL - 绘制三角形

前言

  • 本节目标
    • 掌握VAO,VBO,VEO的概念和用法
    • 在窗口内绘制三角形
  • 重难点都在本节

渲染管线

渲染管线这个名字听起来高端,其实很简单。管线就意味着串行执行,一步步来的代码。说白了就是一个流程图,没有什么并行,架构等复杂的逻辑。借用一张流程图来说:

  • 渲染基础理论:所有的2d,3d物体可以拆分成若干个三角形。
  • 最重要的输入:顶点数据
  • 着重看蓝色部分,因为蓝色部分是开发者可以控制的
  • 顶点着色器,这里着色二字不恰当。其实作用就是点定位代码。
  • 片元着色器,在光栅化后 会转换成像素点,这里的功能就是计算像素最终颜色。这里的计算就非常的精细了

通过上述流程图可知如果我们要实现绘制一个三角形,写顶点着色器就可以了,如果带颜色再加一个片段着色器。

所以首先我们来画一个不带颜色的三角形

绘制三角形

下面是示例是绘制两个三角形组成的正方形,整体思路如下图

  • 上图展示了一个2d/3d对象的生成步骤
  • 数据源生成是一个难点,不可能人工输入,这个可以交给某些数据源生成器,比如Unity直观地拖拉拽。在本次示例比较简单,直接给出顶点数据
  • 数据源生成VAO比较复杂,需要经过VBO,VAO的辅助
  • 着色器代码编写也是难点,这个后续章节再聊。本节给个简单顶点着色器代码如下
kotlin 复制代码
#version 330 core
layout (location = 0) in vec3 position;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
        gl_Position = vec4(position, 1.0f);
}

给出顶点数据

kotlin 复制代码
GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f, // 1
        0.5f, -0.5f, -0.5f, // 2
        0.5f,  0.5f, -0.5f, // 3
        0.5f,  0.5f, -0.5f, // 4
        -0.5f,  0.5f, -0.5f, // 5
        -0.5f, -0.5f, -0.5f, // 6
        
};

创建VAO(顶点数组对象)

  • 这个步骤肯定是需要VBO辅助的,可以理解为VBO创建顶点的内存,VAO用于指向。
  • 可能需要VEO辅助,VEO的作用是描述顶点数据的意义

下面介绍下这个三个对象即VAO,VBO,VEO

创建VAO对象

kotlin 复制代码
// 创建VAO对象
GLuint VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
  • 绘制对象的时候就使用它,VBO, EBO主要是用来辅助内存分配的。内存分配完成后,VAO会指向内存地址。
  • 一个对象对应一个VAO

创建VBO

顶点缓冲对象,用于OpenGL在GPU管理顶点数据的对象,从而提升GPU访问顶点数据的性能。

kotlin 复制代码
GLuint VBO;
// 1. 分配起始地址
glGenBuffers(1, &VBO);
// 2. 绑定其实是指定到GPU
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 3. 分配内存大小
glBufferData(GL_ARRAY_BUFFER, sizeof(DRAW_LIGHT::vertices), DRAW_LIGHT::vertices, GL_STATIC_DRAW);
// 4. 配置内存定义,这里即一次取3个float, 从这个三个中取3个,作为postion = 0 的属性供shader使用
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *) 0);
// 5. 这一步例行公事,意思是启用顶点属性
glEnableVertexAttribArray(0);
  • 创建VBO的过程就是对内存分配的描述。即用C++/OPENgl 去描述顶点数组每个内存块的意义。
  • 步骤是分配地址 -> 绑定地址 -> 分配数据内存 -> 描述内存如何分配
  • 作用是辅助内存分配。可以思考下为啥不能直接用VAO进行内存分配?
  • VBO是可以复用的,即可以重复利用VBO指针
  • VAO和VBO简单的关系可以如下图

这个比较重要,我们在举一个复杂的例子, 如下

一个复杂的vbo例子

kotlin 复制代码
GLfloat vertices[] = {
        // Positions          // Colors           // Texture Coords
         0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // Top Right
         0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // Bottom Right
        -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // Bottom Left
        -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // Top Left 
    };
    
    
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // Color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);
  • 我们纵向看glVertexAttribPointer的前两个参数组成了两个数组[0,1,2],[3,3,2]。这两个数组定义了VBO的内存模型。
  • 首先一个顶点的内存容量为8个GLfloat。且内存分配为3,3,2, 如下图
  • 在渲染管线的顶点着色器这一步中,读取GLSL。如下。
kotlin 复制代码
#version 330 core
# 读取 location 0, 即上图蓝色部分,交给position变量
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
        gl_Position = vec4(position, 1.0f);
        ourColor = color;
        // We swap the y-axis by substracing our coordinates from 1. This is done because most images have the top y-axis inversed with OpenGL's top y-axis.
        // TexCoord = texCoord;
        TexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
}

💡 注意上述代码,不是本节使用的着色器。只是为了说明原理用。本章所使用的着色器已在本节开头列出

创建EBO

用于描述顶点数据的意义

kotlin 复制代码
GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f, // 1
        0.5f, -0.5f, -0.5f, // 2
        0.5f,  0.5f, -0.5f, // 3
        0.5f,  0.5f, -0.5f, // 4
        -0.5f,  0.5f, -0.5f, // 5
        -0.5f, -0.5f, -0.5f, // 6
        
};
GLuint indices[] = { // 注意索引从0开始!
        0, 1, 3, // 第一个三角形
        1, 2, 3  // 第二个三角形
};

glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
  • 上述代码描述了第一个三角形用顶点的 0,1,3作为数据源绘制,第二个三角形用1,2,3作为数据源绘制
  • 如何不用EBO描述,默认就是0,1,2为第一个,3,4,5为第二个
  • 从上面两条容易想到,EBO可以优化顶点的数量,比如一个矩形用默认的方式要写6个点(2 (三角形个数)* 3(三角形顶点数)), 但其实矩形只有四个顶点。这种情况就可以用EBO实现

核心绘制代码

kotlin 复制代码
shader.Use();
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
  • shader是顶点着色器和片元着色器的封装。先忽略
  • glBindVertexArray这里是使用VAO
  • glDrawArrays这里出发渲染管线,先经过顶点着色器绘制顶点,再通过片元着色器给片元上色

Github

相关推荐
hackeroink17 分钟前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者2 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-2 小时前
验证码机制
前端·后端
燃先生._.3 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖4 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235244 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240255 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar5 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人6 小时前
前端知识补充—CSS
前端·css