美文网首页
01-OpenGL基本概念

01-OpenGL基本概念

作者: Alexander | 来源:发表于2021-10-20 09:29 被阅读0次

一、OpenGL简介

OpenGL本身不是API概念,而是一个规范,它是一个巨大的状态机,它的状态就是实际开发中的上下文context,一个OpenGL对象就代表一个OpenGL状态子集的集合。

struct objc_name {
     
     float option1,
     int option2,
     char[] name
}

二、OpenGL常见的工作流

  • 1, 创建对象
  • 2,绑定或指定对象到上下文
  • 3, 设置当前绑定的的对象
  • 4, 恢复上下文

三、OpenGL 中常见的名词解释

OpenGL图形管道可以分为两大部分
1,将3D坐标转换为2D坐标
2,将2D坐标转换为实际的颜色像素
OpenGL 渲染管道上的每一步在GPU上运行的程序对象,我们称之为着色器(shader),其使用的机械语言是glsl语言
1,输入: 一个顶点数据的集合
2,顶点着色器(vertex shader):转换3D坐标,允许我们对顶点属性进行基本处理。
3,基元组装(primitive assembly):组装顶点形成的基本图形。
4,几何着色器(geometry shader):可通过生成新的顶点来形成新的基元图形。
5,光栅化阶段(rasterization stage):将基元映射为最终显示在屏幕上的像素点,产生片元,在进入片元着色器前可能执行裁剪等操作,丢弃视口外的片元。
6, 片元着色器(fragment shader):计算像素点的最终颜色值,一般是在这里进行OpenGL的高级处理。
7, alpha测试和混合阶段: 检测片元深度,确定对象的前后位置或是否需要被丢弃,最后检测alpha值进行合适的混合操作。
8, 顶点(vertex):就是3D坐标数据的集合,顶点数据由顶点属性(vertex)表示最简单的顶点可以看做是一个3D位置和颜色值。
9, 片元(fragment):在OpenGL中片元是渲染一个像素点的全部数据。
10, 顶点缓冲区对象(vertex buffer objc / VBO):在GPU显存中管理和存储顶点数据,顶点缓冲区对象在OpenGL中是GL_ARRAY_BUFFER缓冲区类型。
注意: OpenGL中由于没有默认的顶点和片元着色器,所以至少需要定义一个顶点和片元着色器。
OpenGL只处理范围在[-1.0, 1.0]之间的3D坐标,这个坐标成为标准化的设备坐标,原因是只有在该范围内的坐标才能显示到屏幕上。

四、 OpenGL图形管道编程

eg1: 定义一个顶点输入
float vertices[] = {
     -0.5f, -0.5f, 0.0,
     0.5f, -0.5f, 0.0,
     0.0f, 0.5f, 0.0f
}
eg2: 创建顶点缓冲区对象
// 创建一个缓冲区对象
    unsigned int VBO;
    glGenBuffers(1, &VBO);
    
    // 绑定顶点缓冲区对象到GL_ARRAY_BUFFER类型的缓冲区
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    
    // 当我们将顶点缓冲区绑定到OpenGL GL_ARRAY_BUFFER类型的缓冲区上, 后续该类型目标缓冲区的调用都会作用于我们创建的顶点缓冲区VBO上,
    // 比如拷贝顶点数据到缓冲区的操作
    // glBufferData: 表示拷贝用户定义的顶点数据到当前绑定的缓冲区
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    // 图形卡管理数据的三种方式
    // GL_STREAM_DRAW:数据只会设置一次, GPU最多使用几次
    // GL_STATIC_DRAW:数据只设置一次,可以使用多次
    // GL_DYNAMIC_DARW:数据经常变动,并且会被使用多次

五,顶点着色器(vertex shader)

eg: 在glsl文件中定义一个只包含位置的顶点着色器
Layout (loction = 0) in vec3 apos; // in关键字:表示声明输入顶点属性

void main() {
     gl_Position(apos.x, apos.y, apos.z, 1.0f); // 顶点着色器通过位置数据设置给预定义变量gl_Position来设置着色器的输出
}

创建和编译顶点着色器的步骤:
     0.    将着色器的源码存放到字符串或者glsl文件里       

eg: 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";

     0.    创建顶点着色器对象
eg 
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);

     0.    将顶点数据添加到顶点着色器上,最后并编译
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);

     0.     检查错误
Int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {

     glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
}

六, 片元着色器

计算机图形学中一般是由四个值的数组代表R,G,B,A。片元着色器中只需要定义一个最终颜色的输出变量,该变量是一个四维数组的矢量, 这是需要我们自行计算。

eg 片元着色器和顶点着色器的创建步骤是一样的
1,创建片元着色器
unsigned int fragmentShader;
fragmentShader = glCreatedShader(GL_FRAGMENT_SHADER);

2, 添加顶点数据到该片元着色器上, 并编译
2.1, 顶点数据
const char* fragmentShaderSource = "#version 330 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "{"
    "    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
    "}\0";
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);

3, 检查错误
int success;
char infoLog[512];
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {

     glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
}

7, 着色器对象(shader program object)

不同的着色器编译成对象后,必须要连接到一个程序对象上才能被渲染绘制图形

1, 创建程序对象
unsigned int shaderProgram;
shaderProgram = glCreateProgram();

2, 将编译完成的着色器对象添加到创建的程序对象中link起来
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);

3, 将指定的着色器对象link起来
glLinkProgram(shaderProgram);

4, 程序对象的错误检查
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {

     glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
}

5, 完成链接后可以删除着色器对象
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);

6, 使用程序对象
glUserProgram(shaderProgram);

8,链接顶点属性

顶点着色器允许我们以顶点属性的形式指定输入数据,这就意味着我们在顶点着色器中需要手动指定输入数据与顶点属性之间的对应关系,换句话说就是告诉OpenGL该如何解释我们的输入数据,通过 glVertexAttribPointer 来实现

参数解释

glVertexAttribPointer(<#GLuint indx#>, <#GLint size#>, <#GLenum type#>, <#GLboolean normalized#>, <#GLsizei stride#>, <#const GLvoid *ptr#>)

参数1:指定我们要配置的顶点属性
参数2:指定顶点属性的大小
参数3:指定数据的类型
参数4:指定数据是否标准化
参数5:表示步长,指定连续顶点属性之间的间距
参数6:缓冲区对象中数据的偏移量

注意点:每个顶点属性都是从VBO(缓冲区对象)中获取数据,具体是从哪个VBO中获取数据,取决于在调用glVertexAttribPointer 方法时绑定的VBO。

9,顶点数组对象(VAO / vertex array object)

可以像顶点缓冲区对象一样进行绑定,当我们绑定顶点数组对象后,后续所有顶点属性相关的调用都会存储在VAO中,这就可以让我们只进行一次顶点属性配置,然后在绘制对象时绑定我们配置好的VAO即可。

注意点: OpenGL 核心渲染模式要求至少使用一个顶点数组对象(VAO)

    // VAO
    unsigned int VAO;
    glGenVertexArraysOES(1, &VAO);
    
    // 图形绘制(在渲染循环中)
    glUseProgram(shaderProgram);
    glBindVertexArrayOES(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);

10, 元素缓冲区对象

元素缓冲区(element buffer object / EBO):是一个类似顶点缓冲区对象的缓冲区, 存储OpenGL用于决定绘制哪个顶点的索引,也可以叫索引绘制。

元素缓冲区对象的创建与绑定
 // 顶点数据
    float vertics[] = {
        0.5f, 0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        -0.5f, 0.5f, 0.0f
    };
    
    // 索引数据
    unsigned int indices[] = {
        0, 1, 3, // 第一个三角形
        1, 2, 3
    };
    
    // 创建一个元素缓冲区对象
    unsigned int EBO;
    glGenBuffers(1, &EBO);
    // 绑定元素缓冲区对象
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    // 添加索引数组到元素缓冲区给OpenGL使用
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    // 使用元素缓冲区对象绘制图形 (也是在渲染循环中)
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

总结:

OpenGL绘制图形的基本流程图


image.png

OpenGL对象


image.png

OpenGL中VAO、VBO、以及EBO之间的关系


image.png

顶点缓冲区对象(VBO):管理GPU上的一块用于存放顶点数据的显存,用于存放进入OpenGL渲染管道的顶点数据;
顶点数组对象(VAO):存放顶点属性信息,需要与对应的VBO关联,目的是告诉OpenGL,VBO上的顶点数据应该如何解释(比如顶点数据的长度、间距等)。内部同时保存一个EBO对象,因此绑定VAO就会自动绑定对应的EBO。
元素缓冲区对象(EBO):存放的是索引数据,作用和VBO一样;

相关文章

网友评论

      本文标题:01-OpenGL基本概念

      本文链接:https://www.haomeiwen.com/subject/ooxtaltx.html