openGL入门(一)
- OpenGL(Open Graphics Library)
- GLAD & GLFW
- 通过代码片段滤清流程
- 1. 画面绘制基础
- 2.VAO,VBO,EBO
- (1) VAO : Vertex Array Object
- (2) VBO: Vertex Buffer Object
- EBO:Element Buffer Object
- 总结
OpenGL(Open Graphics Library)
OpenGL,图形绘制接口通常用于3D渲染需求中,多数平台如移动终端,PC端,web端都支持(Webgl是Opengl的子集)。有初学者想要整体了解如何使用C++调用Opengl接口绘制图形,以及渲染管线流程等可以参考入口.本文结合基本的绘制逻辑以及常见问题,用于记录和分享开发时基本逻辑和常见问题。
GLAD & GLFW
GLFW:它提供了创建和管理窗口的功能、一系列处理用户输入的函数,跨平台且与Opengl完美集成,为开发者提供一个绘制空间和窗口(既开发者绘制的图形都会在GLFW初始化的窗口中展示出来)且使用方便。
GLAD:GLAD 简单来说可以使开发者轻松初始化Opengl,加载函数指针,并快速进入到VAO,VBO,shaderlab等的开发流程中。
通过代码片段滤清流程
opengl不论对于2D绘制还是3D绘制,都是通过对模型物体的描述,最终通过计算,将坐标空间转换到屏幕空间,并在计算好的像素点上展示不同的颜色。
1. 画面绘制基础
首先我们需要创建一个窗口,这个窗口作为图形绘制的载体,最终将开发者使用Opengl绘制的图像绘制在窗口中。
//初始化glfw库glfwInit();// 下面着三行代码表示制定了opengl的版本,其中GLFW_OPENGL_CORE_PROFILE 更是确定了使用Opengl的核心模式// 这种模式指定主要是为了明确版本和剔除不必要的引入。glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//创建代码如下,设置了windows的宽高,窗口名称GLFWwindow* window = glfwCreateWindow(800, 600, "title", NULL, NULL);
上述代码指定opengl版本,确定使用核心模式,并创建了展示绘制结果的窗口,那么后续按照逻辑,开发者需要:
(1) 能够使用Opengl指定版本的接口
(2) 能够让结果绘制到窗口中。
首先我们来解决(2):
glfwMakeContextCurrent(window);
此函数使得当前操作的上下文结果都关于window,也就是可以将数据正确地绘制到window上。
再解决问题(1):
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}
此方法执行成功将加载Opengl所有的函数指针,既接下来开发者需要使用到的,绘制图形的各种接口,如果不初始化,您将遇到类似如下的问题:
归根结底是因为开发者没有加载函数指针,再后续又操作了函数指针,所以会出错。
以上便是常规的初始化行为,如果是在项目中,会更加的结构化,但是流程基本是一致的,初始化后,开发者便可以进入真正的Opengl图形绘制流程。
2.VAO,VBO,EBO
(1) VAO : Vertex Array Object
顶点数组对象,是对顶点属性配置的一种封装,VAO本身是一个Handler,其代表着对开发者提供的数组对象的内部数据,为Gpu解释如何拆分Buffer中的数据,简单直接地说,就是您拥有如下一组数据:
float vertices[] = {// positions // colors // texture coords0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right0.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 };
那么通过对多维数组地注释标记,我们将数据从意识上分成了三大组类别数据,如何告知Gpu呢,既通过VAO告知:
(1)生成VAO
(2) 使用 glVertexAttribPointer 对多维数组进行竖向分割,举例如上:八个float数据分成三组,(3,3,2)分别为position,colors and texture.
具体的代码如下:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// color attributeglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// texture coord attributeglVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(2);
需要注意的是,在使用过程中glVertexAttribPointer & glEnableVertexAttribArray 往往绑定在一起使用,真是项目中我们往往会做二次封装。这里不在解释参数含义。
(2) VBO: Vertex Buffer Object
VBO是在GPU内存中存储顶点数据的对象,也就是GPU真正操作的Gpu内存块真正相关的缓存块,它负责将顶点数据Copy给GPU,VBO与VAO:一个是负责数据供给,一个负责告诉Gpu如何解释块内数据。
glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
可以观察到,不同于VAO,VBO使用glBufferData 直接关联到具体的数组数据,buffer<-> vertices,GL_STATIC_DRAW 这个宏是告知Gpu这组数据在后续不会变化。
EBO:Element Buffer Object
元素缓冲对象,也叫索引缓冲对象,有个前提,在Gpu绘制数据时,不论是平面模型还是3D模型,三角形是最小单位,而且如果接触过各类游戏引擎,也可以发现,在模型上,经常提到的减面,就是减少构成模型的三角形数量,从而提高绘制效率,但也降低了模型精细度。那么EBO可以指定重复利用的index,如果您像绘制一个矩形,不如使用两个三角形拼接:
unsigned int indices[] = {0, 1, 3, // first triangle1, 2, 3 // second triangle};glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
indices的数据指代最开始vertices数组中的index,通过使用GL_ELEMENT_ARRAY_BUFFER 绑定buffer,也就是告知Gpu如何利用VBO中的数据,使用两个三角形便可绘制出一个矩形。(这样的设计更具有通用性)。
总结
朴素的讲,VAO EBO 都服务于VBO,VBO是真正的cpu 与gpu之间的数据中介,有了顶点数据,也告知了gpu如何解析数据,后续该通过渲染管线实际操作,将想要绘制的数据绘制到窗口中。