OpengGL第八版的第一个例子

不得不说,opengl第八版和第七版改动太大了。

第八版的第一个例子是按可编程渲染管线来讲的。网上很多pengl入门教程用的还是固定管线的知识。关于固定管线和可编程渲染管线的区别可以参考知乎:

可编程渲染和固定渲


关于图形绘制方式的比较以及为什么使用VAO VBO可以参考这篇文章:

基本图形绘制方式比较

为什么要使用VBO:

  •      使用立即模式的缺点很明显,数据量大一点的话,代码量增加,而且数据发送到服务端需要开销;
  • 使用显示列表,显示列表是一个服务端函数,因此它免除了传送数据的额外开销。但是,显示列表一旦编译后,其中的数据无法修改。
  •      使用顶点数组,可以减少函数调用和共享顶点数据的冗余。但是,使用顶点数组时,顶点数组相关函数是在客户端,因此数组中数据在每次被解引用时必须重新发送到服务端,额外开销不可忽视。
  •      使用VBO在服务端创建缓存对象,并且提供了访问函数来解引用数组;例如在顶点数组中使用的函数如 glVertexPointer(), glNormalPointer(), glTexCoordPointer()。同时,VBO内存管理会根据用户提示,"target"  和"usage"模式,将缓存对象放在最佳地方。因此内存管理会通过在系统内存、AGP内存和视频卡内存(system, AGP and video memory)这3中内存见平衡来优化缓存。另外,不像显示列表,VBO中数据可以通过映射到客户端内存空间而被用户读取和更新。VBO的另外一个优势是它像显示列表和纹理一样,能和多个客户端共享缓存对象。可见使用VBO优势很明显。
  • 为什么要配合VAO使用VBO:
  •    VBO存储了实际的数据,真正重要的不是它存储了数据,而是他将数据存储在GPU中。这意味着VBO它会很快,因为存在RAM中的数据需要被传送到GPU中,因此这个传送是有代价的。

    VAO代表的是一些描述存储在VBO中对象的属性。VAO可以被视为指向对象的高级内存指针,有点类似于C语言指针,但比地址多了跟多的跟踪作用。他们很复杂。

    VAO就像一个容器,可以把VBO中的各项属性组合在一起。

    VAO并不与VBO直接相关,进过初看起来如此。VAOs节省了设置程序所需的状态的时间。如果没有VAO,你需要调用一堆类似gl*之类的命令。

  • 下面有个例子说明为什么VAO和VBO要配合使用:

  1. // draw with VAO  
    glBindVertexArray(vaoId); // bind vao  
    glDrawElements(...);  
    glBindVertexArray(0);     // unbind vao  
      //不使用VAO的话,要不停的开关client states
    // draw without VAO  
    // need to set many states before drawing  
    glEnableClientState(GL_VERTEX_ARRAY); // enable client states  
    glEnableClientState(GL_NORMAL_ARRAY);  
    glBindBuffer(GL_ARRAY_BUFFER, vboId); // bind vbo  
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId);  
    glVertexPointer(3, GL_FLOAT, 0, 0); // vertex attributes  
    glNormalPointer(GL_FLOAT, 0, offset); // normal attributes  
    glDrawElements(...);  
    glBindBuffer(GL_ARRAY_BUFFER, 0); // unbind vbo  
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);  
    glDisableClientState(GL_VERTEX_ARRAY);  
    glDisableClientState(GL_NORMAL_ARRAY);

服务端和客户端:在opengl编程中,服务端通常指各种应用程序,比如我有一幅图,这幅图想通过显卡绘制到屏幕上,那么这幅图的数据就是客户端数据;它需要传递到显存当中去显示,那么我的服务端就是opengl操控的部分。(个人理解,欢迎指正)

VBO顶点数据传输过程:

顶点数据传输过程

VBO实际上存储的是顶点的属性数据。比如顶点坐标,顶点颜色等通过顶点着色器可以设置的各种属性数据。它是一个内存数组,

比如我有一个数组,这个数组可能是客户端产生的坐标,也有可能是像素等

const GLfloat vertices[] = {  
        -0.5f,-0.5f,0.0f,1.0f,  
        0.5f,0.0f,0.0f,1.0f,  
        0.0f,0.5f,0.0f,1.0f  
     }; 
我需要把这些数据传输到opengl缓存当中。首先创建VBO,即vboId;然后把这个对象绑定到GL_ARRAY_BUFFER上,我们通过GL_ARRAY_BUFFER这个中介来把vertices数组中的数据传给vboId(同样,我们在后面组织数据的时候也是要先将vbo绑定到GL_ARRAY_BUFFER中)。至于为什么用GL_ARRAY_BUFFER,我暂时不清楚。传给vboId后,这些数据全都放在了一块内存区域。

这样完成了发送顶点数据到GPU的任务。但是BO中的数据时未格式化的,但这是OpenGL关心的。我们只是分配了BO,并填充了些随机二进制数据。现在我们需要告诉OpenGL,BO中有顶点数据,并告诉他顶点数据的格式。我们通过下面这样的代码来完成这一任务:

glBindBuffer(GL_ARRAY_BUFFER, vboId);//将GL_ARRAY_BUFFER与vboId绑定一起

glEnableVertexAttribArray(0);//打开0号属性(这个0号属性通过shader绑定在一起)

glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_SET(0));//将0号属性与vboId结合到一起。

第一个函数声明使用BO。第二个函数启动顶点属性数组,这个稍后解释。

第三个函数是关键的。glVertexAttribPointer,尽管,包含”Pointer”一词,但是实际上它处理的并不是指针,而是BO。

在渲染时,OpenGL从BO中提取顶点数据。我们要做的就是通告OpenGL存储在BO中的顶点数组中数据格式。也就是要告诉OpenGL如何解释BO中的数组。

在我们的案例中,数据格式如下

  •      表示位置的单个数据值以32位浮点数据存储,使用C/C++ float类型。
  •      每个位置由4个这样的值组成。
  •      每4个值之间没有间隙,数据值在数组中紧密相连。
  •      数组中第一个值在BO的开始处

glVertexAttribPointer 函数告诉了OpenGL所有这些情况。第三个参数制定了值得基本类型,即GL_FLOAT,对应32位浮点数据。第二个参数,指定多少个这样的值组成一个位置,即一个点。在这里,即4个值组成一个点。第5个参数指定数据间间隙,第6个参数指定BO中数据偏移量,0代表从BO的开始处算起。

第四个参数以后再做解释。

下面通过一个实例来说明,由于opengL第一个例子含有枚举类型,不方便看,我就自己改了一个类似的。

#include<iostream>
#include"vgl.h"
#include"LoadShaders.h"
using namespace std;
GLfloat vertices[][2] = {
	{ -0.5f, -0.0f },
	{ -0.5f, 0.5f },
	{ 0.5f, 0.5f },
};
GLfloat color[][3] = {
	{ 1.0f, 0.5f, 0.0f },
	{ 0.0f, 1.0f, 0.0f },
	{ 1.0f, 1.0f, 0.0f },
};
GLuint vao;
GLuint vbo[2];
void init()
{
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);
	
	glGenBuffers(2, vbo);//生成两个VBO,第一个存储顶点坐标数据,第二个存储定点颜色数据

	//将顶点坐标数据复制到vbo0中
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//将vertices指针指向的所有数据复制到vbo[0]中;
	glBindBuffer(GL_ARRAY_BUFFER, 0);//解绑定

	//将顶点颜色数据复制到vbo1中
	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(color), color, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	ShaderInfo shaders[] = {
		{GL_VERTEX_SHADER,"triangles.vert"},
		{GL_FRAGMENT_SHADER,"triangles.frag"},
		{GL_NONE,NULL}
	};
	GLuint program = LoadShaders(shaders);
	glUseProgram(program);

	//将vbo0中的数据每两个一组放一块(因为vertic中是两个数据一组,如果vertic用(x,y,z)代表坐标,则应该是3个数据绑一组)
	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);//当前操作的是vbo0;
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));//0代表着色器中的0号属性(这个属性可以绑定到vbo0和vbo1中,由于我们没有写shader,所以暂且将0号属性绑到vbo0中),GL_FALSE说明不进行归一化,BUFFER_OFFSET(0)由于当前操作的就是vbo0,并且我们想从第一个字节开始,所以偏移量就是0;如果我们想跳过第一组数据来画一条直线,那么BUFFER_OFFSET(sizeof(*vertices));
	glBindBuffer(GL_ARRAY_BUFFER, 0);//解绑定

	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);//当前操作的是vbo1
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));//1代表vbo1
	glBindBuffer(GL_ARRAY_BUFFER, 0);//解绑定

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);
}
void display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	glBindVertexArray(vao);
	glDrawArrays(GL_TRIANGLES, 0, 3);//管理glDrawArray会从顶点着色器中获取位置属性,然后画图;所以我们如果想根据vbo2来画图的话,就必须在shader中将location == 1
	glFlush();
}
int main(int argc, char **argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
	glutInitWindowSize(400, 400);
	glutInitContextVersion(3, 1);
	glutInitContextProfile(GLUT_CORE_PROFILE);
	glutCreateWindow(argv[0]);
	//如果要使用glew相关的函数,那么一定要先对glew初始化。
	if (glewInit())
	{
		cout << "glew 初始化失败" << endl;
		exit(EXIT_FAILURE);
	}
	init();
	glutDisplayFunc(display);
	glutMainLoop();
	return 0;
}

triangle.vert代码如下:

#version 330 core
void main()
{
    gl_Position = vPosition;
}

location = 0 意思就是,0号属性;in就是说0号属性变量的值是外部传进来的。在glVertexAttribPointer中,第一个参数0代表的就是0号属性,所以vPosition的值就是vbo0传过来的。如果我们让location = 1,那么在

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0,BUFFER_OFFSET(0));//1代表vbo1

这句话中,就会把vbo1的值传给vposition。

之后在display函数中,

glBindVertexArray(vao);

glDrawArrays(GL_TRIANGLES, 0, 3);

glDrawArray会从顶点着色器中获取位置属性,然后画图;所以我们如果想根据vbo2来画图的话,就必须在shader中将location == 1



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值