基本名词
图元
图形渲染管道的输入量是几何图元(比如,三角形,点,直线或者四边形),它们都可以通过一个或者若干个几何顶点来描述。OpenGL支持三种类型的几何图元:点,线段和封闭的多边形。它们都是通过顶点来描述。每个顶点都有几个特定的属性,比如位置,颜色,法线和纹理。OpenGL提供了10种图元(如下图)。球体,三维盒子,角锥体都不是图元,它们都是可以通过图元组合生成。
顶点
顶点可以说是用来描述图元的基本单位,三维中,三角形图元需要三个点空间点确认,而这个空间点就是顶点。四边形由四个顶点组成,点图元就使用一个顶点就行。
如上图正方形,由6个四边形图元组成,每个四边形图元又由四个顶点描述。
顶点数组对象 VAO(Vertex Arrary Object)
顶点缓冲对象 VBO(Vertex Buffer Objects)
索引缓冲对象 EBO(Element Buffer Object)
其他
- Rendering(渲染): 把数学和图形数据转换成3D空间图像的操作或指图像。
- Transformaton(转换): 把2D下的点连接,使得观察者产生3D的错觉。
- Projection(投影): 把3D坐标转换为2D屏幕坐标。
- Rasterization(光栅化): 绘制或填充每个定点之间的像素形成线段。在大多数渲染的情况下,我们并不使用线段,而是用实心三角形进行渲染。将每个图元转换为一系列的碎片。我们可以认为一个碎片就是三维空间中的一个像素,同样具有位置,颜色,法线和纹理属性。
- Shading(着色): 改变顶点间的表面的颜色值。
- Shader(着色器): 在图形硬件上执行的单独程序,用来处理顶点和执行光栅化。
- Texture Mapping(纹理贴图): 把图片贴到三角形或多边形上,使3D效果更加显著。
- Blending(混合): 把不同颜色混在一起。可以在一个图形中绘制另一个图形,使得效果真实。
- 顶点处理:对模型的各个顶点进行计算(着色)以及坐标变换
3D图像渲染管道
包括以下几个主要步骤:
- 顶点处理: 对模型的各个顶点进行计算(着色)以及坐标变换
- 光栅化: 将每个图元转换为一系列的碎片。我们可以认为一个碎片就是三维空间中的一个像素,同样具有位置,颜色,法线和纹理属性。
- 碎片处理: 光栅化之后,我们得到一些列的碎片,这个阶段则是对碎片进行处理(着色)
- 合并输出: 将所有的碎片(3D概念中)转化成二维的颜色像素,最后输出显示到屏幕上
上述步骤中,顶点处理和碎片处理是可编程的。你可以编写顶点着色程序和碎片着色程序,来实现顶点和碎片的自定义运算处理。
一般着色程序使用类似C语言的高级语言编写,比如GLSL(OpenGL Shading Language),HLSL(High-Level Shading Language for Miscrosoft Direct3D),或者Cg(C for Graphics by NVIDIA)。
此外,光栅化和合并输出两个步骤是不可编程的,但是可以配置GPU中处理这两个步骤的一些参数。
opengl接口
可参考文章:https://www.cnblogs.com/linkzijun/p/6196172.html
编程
QT中为我们把OpenGL进行了封装,使用较原生OpenGL简单,除了对OpenGL原生接口的支持,QGLWidget还为OpenGL在多个操作系统平台上的应用进行了浅层封装。
qt支持Opengl的类
1、在Qt中为OpenGL提供支持的类主要有以下几个:
- OpenGLWindow
- QOpenGLWidget
- QOpenGLFunctions
- QOpenGLBuffer
- QGLWidget:用于渲染OpenGL场景的易于使用的Qt部件。
- QGLColormap:用于在QGLWidget中安装用户自定义的颜色图。
- QGLContext:封装了用于OpenGL渲染的场景。
- QGLFormat:指定OpenGL演染场景的显示模式。
- QGLFrameBufferObject 和 QGLPixelBuffer分别提供了对GL帧缓冲对象和GL像素缓冲的支持。
- QGLPaintEngine:QPaintEngine的派生类,为QPainter提供了OpenGL绘图引擎。
2、添加opengl模块及动态库
添加模块。
QT += opengl
有时候编译很多报错,动态库方向的,那就是没有添加对应的动态库,项目文件中添加对应库。
win32:LIBS += -lOpengl32 \
-lglu32 \
-lglut
4、虚函数接口
在QT中写OpenGL需要继承自QOpenGLWidget,QOpenGLWidget提供了三个虚函数用以重载来实现OpenGL的绘制。
-
initializeGL() 注册函数:
设置GL的渲染绘制属性、定义显示列表、载入固定纹理等初始化工作。在initializeGL()在调用paintGL()之前只被调用一次,之后不再调用。 -
paintGL() 绘制函数:
在此使用OpenGL中的接口进行场景绘制,QGLWidget的paintEvent( QPaintEvent* )将会自动调用 paintGL()进行部件的显示绘制。也可在需要重绘时通过updateGL()时调用paintGL(),和paintEvent使用基本相似。 -
resizeGL() 该函数:
用于处理当部件大小发生改变时,对OpenGL绘图管线各矩阵需要进行的操作。该函数paintGL()第一次调用之前,initializeGL()调用之后被第一次被调用, 之后每当QGLWidget的不见大小发生改变时,都将调用该函数来对视图、投影矩阵等进行相应的设置。
5、VBO的创建以及配置
GLuint vbo_id;
glGenBuffers(1, &vbo_id); //开辟显存空间并分配VBO的ID
glBindBuffer(GL_ARRAY_BUFFER, vbo_id); //创建VBO并绑定ID
//把用户定义的数据传输到当前的vbo_id
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//告知OpenGL如何解释这些顶点数据
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
OpenGL中所有的图形都是通过分解成三角形的方式进行绘制,glDrawArrays函数负责把模型绘制出来。
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
函数原型:glDrawArrays (GLenum mode, GLint first, GLsizei count)
顶点属性glVertexAttribPointer
默认是关闭的,使用时要以顶点属性位置值为参数调用glEnableVertexAttribArray
开启。如:
glEnableVertexAttribArray(0);
......
glDisableVertexAttribArray(0);
6、VAO的创建和配置
GLuint vao_Id;
glGenVertexArrays(1, &vao_Id);
glBindVertexArray(vao_Id);
VAO是一个保存了所有顶点数据属性的状态结合,它存储了顶点数据的格式以及顶点数据所需的VBO对象的引用。VAO本身并没有存储顶点的相关属性数据,这些信息是存储在VBO中的,VAO相当于是对很多个VBO的引用,把一些VBO组合在一起作为一个对象统一管理。
7、EBO的创建和配置
和EBO差不多,只是VBO存储的顶点数据,EBO存储的是索引数据(索引顶点的)
GLuint EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
当用EBO绑定顶点索引的方式绘制模型时,需要使用glDrawElements
而不是glDrawArrays
。
8、两个例子
- 使用VBO,VAO绘制一个矩形图形。即直接依据VBO中的顶点数据绘制。
- 使用EBO绘制两个三角形,组成同样的矩形图形。使用EBO中的索引查找绘制,此方法节省共用的顶点数据,所以节省顶点数据内存,后期基本都是使用此方法。
例子1:
//使用VAO VBO绘制矩形
#include <GL/glew.h>
#include <GL/freeglut.h>
void userInit(); //自定义初始化
void reshape(int w, int h); //重绘
void display(void);
void keyboardAction(unsigned char key, int x, int y); //键盘退出事件
GLuint vboId;//vertex buffer object句柄
GLuint vaoId;//vertext array object句柄
GLuint programId;//shader program 句柄
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("Rectangle demo");
//使用glew,需要执行glewInit,不然运行过程会报错
//glewInit要放在glut完成了基本的初始化之后执行
glewInit();
//自定义初始化,生成VAO,VBO对象
userInit();
//重绘函数
glutReshapeFunc(reshape);
glutDisplayFunc(display);
//注册键盘按键退出事件
glutKeyboardFunc(keyboardAction);
glutMainLoop();
return 0;
}
//自定义初始化函数
void userInit()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
//创建顶点数据
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
};
//创建VAO对象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//创建VBO对象
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
//传入VBO数据
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//解除VBO绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
//调整窗口大小回调函数
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
}
//绘制回调函数
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glEnableVertexAttribArray(0);
//解释顶点数据方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
//绘制模型
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glutSwapBuffers();
}
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
{
switch (key)
{
case 033: // Escape key
exit(EXIT_SUCCESS);
break;
}
}
例子2:
//使用EBO绘制矩形(两个三角形)
#include <GL/glew.h>
#include <GL/freeglut.h>
void userInit(); //自定义初始化
void reshape(int w, int h); //重绘
void display(void);
void keyboardAction(unsigned char key, int x, int y); //键盘退出事件
GLuint eboId;//element buffer object句柄
GLuint vboId;//vertext buffer object句柄
GLuint vaoId;//vertext array object句柄
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(512, 512);
glutCreateWindow("Rectangle demo");
//使用glew,需要执行glewInit,不然运行过程会报错
//glewInit要放在glut完成了基本的初始化之后执行
glewInit();
//自定义初始化,生成VAO,VBO,EBO
userInit();
//重绘函数
glutReshapeFunc(reshape);
glutDisplayFunc(display);
//注册键盘按键退出事件
glutKeyboardFunc(keyboardAction);
glutMainLoop();
return 0;
}
//自定义初始化函数
void userInit()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
//创建顶点数据
const GLfloat vertices[] = {
-0.5f,-0.5f,0.0f,1.0f,
0.5f,-0.5f,0.0f,1.0f,
0.5f,0.5f,0.0f,1.0f,
-0.5f,0.5f,0.0f,1.0f,
};
// 索引数据
GLshort indices[] = {
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};
//创建VAO对象
glGenVertexArrays(1, &vaoId);
glBindVertexArray(vaoId);
//创建VBO对象,把顶点数组复制到一个顶点缓冲中,供OpenGL使用
glGenBuffers(1, &vboId);
glBindBuffer(GL_ARRAY_BUFFER, vboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//创建EBO对象
glGenBuffers(1, &eboId);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboId);
//传入EBO数据
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
//解释顶点数据方式
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
//解绑VAO
glBindVertexArray(0);
//解绑EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//解绑VBO
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
//调整窗口大小回调函数
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
}
//绘制回调函数
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//绑定VAO
glBindVertexArray(vaoId);
//绘制模型
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL);
glutSwapBuffers();
}
//键盘按键回调函数
void keyboardAction(unsigned char key, int x, int y)
{
switch (key)
{
case 033: // Escape key
exit(EXIT_SUCCESS);
break;
}
}