opengl编程基础篇

本文深入介绍了OpenGL图形渲染过程,包括图元、顶点、顶点数组对象(VAO)、顶点缓冲对象(VBO)、索引缓冲对象(EBO)等关键概念。详细阐述了渲染管道的各个阶段,如顶点处理、光栅化、碎片处理和合并输出,并举例展示了如何使用VBO和EBO绘制几何图形。同时,提到了在Qt中使用OpenGL的类和接口,如QOpenGLWidget及其相关函数,以及如何配置和使用VBO和VAO进行图形绘制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本名词

图元

图形渲染管道的输入量是几何图元(比如,三角形,点,直线或者四边形),它们都可以通过一个或者若干个几何顶点来描述。OpenGL支持三种类型的几何图元:点,线段和封闭的多边形。它们都是通过顶点来描述。每个顶点都有几个特定的属性,比如位置,颜色,法线和纹理。OpenGL提供了10种图元(如下图)。球体,三维盒子,角锥体都不是图元,它们都是可以通过图元组合生成。

顶点

顶点可以说是用来描述图元的基本单位,三维中,三角形图元需要三个点空间点确认,而这个空间点就是顶点。四边形由四个顶点组成,点图元就使用一个顶点就行。
在这里插入图片描述
如上图正方形,由6个四边形图元组成,每个四边形图元又由四个顶点描述。

顶点数组对象 VAO(Vertex Arrary Object)

顶点缓冲对象 VBO(Vertex Buffer Objects)

索引缓冲对象 EBO(Element Buffer Object)

其他
  1. Rendering(渲染): 把数学和图形数据转换成3D空间图像的操作或指图像。
  2. Transformaton(转换): 把2D下的点连接,使得观察者产生3D的错觉。
  3. Projection(投影): 把3D坐标转换为2D屏幕坐标。
  4. Rasterization(光栅化): 绘制或填充每个定点之间的像素形成线段。在大多数渲染的情况下,我们并不使用线段,而是用实心三角形进行渲染。将每个图元转换为一系列的碎片。我们可以认为一个碎片就是三维空间中的一个像素,同样具有位置,颜色,法线和纹理属性。
  5. Shading(着色): 改变顶点间的表面的颜色值。
  6. Shader(着色器): 在图形硬件上执行的单独程序,用来处理顶点和执行光栅化。
  7. Texture Mapping(纹理贴图): 把图片贴到三角形或多边形上,使3D效果更加显著。
  8. Blending(混合): 把不同颜色混在一起。可以在一个图形中绘制另一个图形,使得效果真实。
  9. 顶点处理:对模型的各个顶点进行计算(着色)以及坐标变换

3D图像渲染管道

在这里插入图片描述
包括以下几个主要步骤:

  1. 顶点处理: 对模型的各个顶点进行计算(着色)以及坐标变换
  2. 光栅化: 将每个图元转换为一系列的碎片。我们可以认为一个碎片就是三维空间中的一个像素,同样具有位置,颜色,法线和纹理属性。
  3. 碎片处理: 光栅化之后,我们得到一些列的碎片,这个阶段则是对碎片进行处理(着色)
  4. 合并输出: 将所有的碎片(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提供支持的类主要有以下几个:

  1. OpenGLWindow
  2. QOpenGLWidget
  3. QOpenGLFunctions
  4. QOpenGLBuffer
  5. QGLWidget:用于渲染OpenGL场景的易于使用的Qt部件。
  6. QGLColormap:用于在QGLWidget中安装用户自定义的颜色图。
  7. QGLContext:封装了用于OpenGL渲染的场景。
  8. QGLFormat:指定OpenGL演染场景的显示模式。
  9. QGLFrameBufferObjectQGLPixelBuffer分别提供了对GL帧缓冲对象和GL像素缓冲的支持。
  10. QGLPaintEngine:QPaintEngine的派生类,为QPainter提供了OpenGL绘图引擎。

2、添加opengl模块及动态库

添加模块。

QT += opengl

有时候编译很多报错,动态库方向的,那就是没有添加对应的动态库,项目文件中添加对应库。

win32:LIBS += -lOpengl32 \
              -lglu32 \
              -lglut

4、虚函数接口

在QT中写OpenGL需要继承自QOpenGLWidget,QOpenGLWidget提供了三个虚函数用以重载来实现OpenGL的绘制。

  1. initializeGL() 注册函数:
    设置GL的渲染绘制属性、定义显示列表、载入固定纹理等初始化工作。在initializeGL()在调用paintGL()之前只被调用一次,之后不再调用。

  2. paintGL() 绘制函数:
    在此使用OpenGL中的接口进行场景绘制,QGLWidget的paintEvent( QPaintEvent* )将会自动调用 paintGL()进行部件的显示绘制。也可在需要重绘时通过updateGL()时调用paintGL(),和paintEvent使用基本相似。

  3. 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、两个例子

  1. 使用VBO,VAO绘制一个矩形图形。即直接依据VBO中的顶点数据绘制。
  2. 使用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;
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值