glgenvertexarrays如果没有你

OpenGL 红宝书 glGenVertexArrays(NumVAOs,VAO) 崩溃
[问题点数:40分,结帖人u]
OpenGL 红宝书 glGenVertexArrays(NumVAOs,VAO) 崩溃
[问题点数:40分,结帖人u]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。【OpenGL】第二篇 Hello OpenGL - alwaystiys - 博客园
-------------------------------------------------------------------------------------------------------------------------------
就像学习其他编程语言一样,为了顺利写下第一个OpenGL程序
我们必须不辞辛苦的先铺好砖块,搭建好环境&&
所以接下来让我先把所需要的库的环境安置好,再开始coding。
-------------------------------------------------------------------------------------------------------------------------------
【安装环境】
本来想简单点直接用glut和glew的库包含就好,但是为了配合该书的例子还是决定用OpenGL编程指南第八版的环境来配置。
关于该配置,我发现了一个前辈已经把详细过程写在他的csdn博客上,大家可以去看看,非常简单,里面有源码的下载地址。
http://blog.csdn.net/qq/article/details/
另外为了不用每次新建工程都要配置一次,我直接把include和lib目录下的文件拷贝到vs2012的安装目录
例如我的目录是C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC
拷贝到该目录下对应的include以及lib下,就可以了。以后每次新建工程就不必都重复那些步骤了。
接下来,就写下OpenGL程序验证下。关于代码我会尽量注释,希望大家能看得清楚。
&------------------------------------------------------------------------------------------------------------------------------
&其实我一直觉得的把代码先跑通然后再仔细研究代码会更加清楚,所以我先呈上该书第一个例子的运行结果。
--------------------------------------------------------------------------------------------------------------------------------
如果你的环境配置正确,代码也按照书上敲打的话,运行后可能会出现停止运行的情况
glutInitContextVersion(4, 3);将这一行代码注释 即可解决,暂时不知道为什么,看看后面能不能找到解释。
--------------------------------------------------------------------------------------------------------------------------------
正如书中所说的,创建窗口、接收鼠标和键盘输入等等这些功能并不属于OpenGL,只是利用了第三方软件库,
正如例子里面用到的glut、glew,这些工具简化了我们的开发,让我们可以更加专注于OpenGL的本质。
首先头文件以及一些变量定义
#include&iostream&
#include "vgl.h"
#include "LoadShaders.h"
enum VA0_IDs { Triangles, NumVAOs };
enum Buffer_IDs { ArrayBuffer, NumBuffers };
enum Attrib_IDs { vPosition = 0, vColor = 1 };
GLuint VAOs[NumVAOs];
GLuint Buffers[NumBuffers];
const GLuint NumVertices = 6;
接着先进入main函数,看看主要做了什么。
int main(int argc, char** argv){
该函数用来初始化GLUT库,之后会与当前窗口系统产生交互
  关于命令行参数,可以指定窗口大小、位置或颜色类型等
  因此glutInitWindowSize、glutInitDisplayMode或glutInitWindowPosition等可以在glutInit之前运行
  但是当命令行有参数时,glutInit会移除之前的操作,比如设置窗口大小等等。
glutInit(&argc, argv);
该函数指定了窗口颜色类型
除了GLUT_RGBA(指定 RGBA 颜色模式的窗口)外,还有其他类型,比如
GLUT_RGB(指定 RGB 颜色模式的窗口)、
GLUT_DEPTH(窗口使用深度缓存)、
GLUT_STENCIL(窗口使用模板缓存)等等
可查阅GLUT文档
https://www.opengl.org/documentation/specs/glut/spec3/node12.html#SECTION
glutInitDisplayMode(GLUT_RGBA);
接下来这两个函数看名字就已经知道其用途
分别是设置窗口大小以及窗口位置
不过我们还是根据实际设备的尺寸动态进行设置比较好
glutInitWindowSize(256, 256);
glutInitWindowPosition(300, 300);
从名字可以得知,使用某个指定版本的OpenGL
但是我使用4.3的时候运行会崩溃
换成3.0之后便可以,原因待查。
glutInitContextVersion(3, 0);
OpenGL3.2后提出两种模式
兼容模式compatibility profile,该模式保证1.0以后的所有特性都可以使用。
核心模式core profile,该模式可以确保使用的只是其最新特性
//glutInitContextProfile(GLUT_CORE_PROFILE);
//glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);
创建一个窗口,参数为窗口标题
创建完的窗口会关联OpenGL的上下文环境
该函数API官方说明:
The display state of a window is initially for the window to be shown.
But the window's display state is not actually acted upon until glutMainLoop is entered.
This means until glutMainLoop is called, rendering to a created window is ineffective
because the window can not yet be displayed.
简单的说,就是在执行了glutMainLoop函数之后,对窗口的渲染才起作用。
如果不调用glutMainLoop,窗口不会显示。
glutCreateWindow(argv[0]);
if(!GL_ARB_vertex_array_object)
std::cout && "GLEW_ARB_vertex_array_object not available." && std::
该函数属于另外一个辅助库GLEW(OpenGL Extension Wrangler),该库是C/C++的扩展库,方便我们获取OpenGL扩展的各种函数。
而这里说明下openGL在Windows下的情况。
万恶的微软为了推自己的D3D,所以默认对openGL的支持是很有限的。
从openGL1.1版本开始就再也没有升级了,差不多都十多年了。
所以现在Windows下对于openGL的支持,全靠显卡厂商。
正因为此,更新到最新的显卡驱动也是非常必须的。
对于不一样的显卡,支持openGL1的版本也是不一样的,具体需要上各家网站查看。
譬如我的GT750,就支持openGL4.3
虽然安装完驱动后就支持最新的openGL了,但是微软并没有提供直接的openGL API,导致使用起来比较繁琐。
于是,GLEW得用处就来了,他其实就是对这些繁琐的事情进行的封装,使得程序员可以很方便的调用glxxx的openGL函数。
所以,GLEW简化获取函数地址的过程,减少了我们的工作量!
因此此函数初始化后,我们就可以在之后的代码里面方便地使用相关的gl函数。
if (glewInit()){
cerr && "Unable to initialize GLEW ... exiting" &&
exit(EXIT_FAILURE);
这些函数是我自己加进去的,可以通过以下方法获取自己系统中的OpenGL信息:
const char* version = (const char*)glGetString(GL_VERSION);
printf("OpenGL 版本:%s\n", version);
const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
//printf("OpenGL 扩展:%s\n", extensions);
const char* renderer = (const char*)glGetString(GL_RENDERER);
printf("OpenGL 显卡:%s\n", renderer);
const char* vendor = (const char*)glGetString(GL_VENDOR);
printf("OpenGL 开发商:%s\n", vendor);
该函数初始化OpenGL的相关数据,为之后的渲染做准备。
接下来会详细解析
该函数为窗口设置了回调函数,即在窗口有更新时,该回调函数就会执行。
参数是指函数的地址,一个函数指针。
glutDisplayFunc(display);
该函数是个无限循环函数,即死循环。
它会一直处理已经被注册的回调函数,以及用户输入等操作。
glutMainLoop();
之后我们进去初始化函数init看看
void init(void){
该函数原型为void glGenVertexArrays(GLsizei n, GLuint *arrays);
作用是返回n个未使用的对象名保存到数组arrays中, 作为顶点数组对象(Vertex Array Object),常用简写VAO替代。
在OpenGL中,VAO负责管理和顶点(Vertices)集合相关联的各种数据。
但是这些数据我们是保存到顶点缓存对象中(Vertex Buffer Object),简称VBO。之后我们会详细介绍
现在我们只需知道VAO是一个对象,其中包含一个或者更多的VBOs。
glGenVertexArrays(NumVAOs, VAOs);
该函数原型为void glBindVertexArray(GLuint array);
作用简单来说,就是绑定对象(Bind An Object)
对于这个函数来说,VAOs[Triangles]里面保存着glGenVertexArrays执行完后返回的对象名,而它作为参数传入该函数。
第一次调用时OpenGL内部会分配这个对象所需的内存并且将它作为当前对象。
书上关于这个绑定对象的定义用设置铁路的道岔开关来描述,我觉得还是挺好理解的:
【一旦设置了开关,从这条线路通过的所有列车都会驶向对应的轨道,如果设置到另外一个状态,
那么之后经过的立车都会驶向另外一条轨道】
OpenGL也是如此,当前绑定了哪个对象,之后的操作都是针对于这个对象,除非重新绑定了其他对象。
一般来说,有两种情况需要我们绑定对象:
1、创建对象并初始化它所对应的数据时
2、下次我们准备使用这个对象而它并不是当前所绑定的对象时
glBindVertexArray(VAOs[Triangles]);
定义了两个三角形的坐标
此处需要了解,当前的坐标系是以屏幕为中心的,x轴正方向向右,y轴正方向向上。
范围分别为[-1,1]、[-1,1]
GLfloat vertices[NumVertices][2] = {
{ -0.90, -0.90 }, // Triangle 1
0.85, -0.90 },
0.90, -0.85 }, // Triangle 2
该函数原型为void glGenBuffers(GLsizei n, GLuint *buffers);
作用是返回n个当前未使用的对象名保存到buffers数组中,作为顶点缓存对象(Vertex Buffer Objects),简称VBO。
VBO是指OpenGL服务端(OpenGL server)分配和管理的一块内存区域。几乎所有传入OpenGL的数据都是存储在VBO当中。
glGenBuffers(NumBuffers, Buffers);
该函数原型为void glBindBuffer(GLenum target, GLuint buffer);
作用是为当前已分配的名称绑定不同类型的缓存对象。简单的说,就是初始化顶点缓存对象。
由于OpenGL里有很多种不同类型的缓存对象,因此绑定缓存对象时需要指定对应类型,
用参数target表示。在这个例子中,是将顶点数据保存到缓存当中,因此使用GL_ARRAY_BUFFER类型来表示。
目前缓存对象的类型共有8种,分别用于不同的OpenGL功能实现。以后介绍到VBO的时候再详细解释。
glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
上面的glBindBuffer中只是初始化了VBO,而下面这个函数则是把顶点数据(vertex data)传递到缓存对象中。
它主要做了两件事
1、分配顶点数据所需的存储空间
2、将数据从应用程序的数组中拷贝到OpenGL服务端的内存中
函数原型为 void glBufferData(GLenum target, GLsizeptr size, const GLvoid* data, GLenum usage);
target用于表示缓存的对象的类型
size用于表示要存储数据的大小,也即是OpenGL在分服务端内存分配的内存大小
data用于表示要存储的数据的指针
usage用于设置分配数据之后的OpenGL的读取和写入方式
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//---------------------------------------------------------------------------------
上面的代码
我们创建并绑定了VAO
以及定义了顶点数据,并将之传送到VBO中。
那么这个时候VAO还没有和VBO产生联系,在该函数末尾我们再详细说明。
接下来我们先看看着色器。
//---------------------------------------------------------------------------------
ShaderInfo该结构体的定义位于头文件LoadShaders.h中,如果有兴趣可以先看看,这里暂时不介绍
我们先只需知道
1、对于每一个OpenGL程序,当它所使用的OpenGL版本高于或等于3.1时,都需要指定至少两个着色器:
顶点着色器以及片段着色器,下面通过LoadShaders函数来完成这个任务。
2、我们需要额外编写两个文件,就下面例子来说,一个是triangles.vert顶点着色器
另外一个是triangles.frag片段着色器。两个文件在末尾会贴出来。
3、着色器所采用的语言是OpenGL着色语言GLSL,与C++非常类似。
关于着色器的详细信息,下一篇再详细介绍。
ShaderInfo shaders[] = {
{ GL_VERTEX_SHADER, "triangles.vert" },
{ GL_FRAGMENT_SHADER, "triangles.frag"},
{ GL_NONE, NULL}
GLuint program = LoadShaders(shaders);
glUseProgram(program);
函数原型:void glVertexAttribPointer(GLuint index, GLint size, GLenum type,
GLboolean normalized, GLsizei stride, const GLvoid* pointer);
之前我们调用了glBindData所传递给缓存的只是数据,之后我们要使用它,还必须指定数据类型。
所以该函数完成的主要任务是:
1、告诉OpenGL,该存储数据的格式
2、因为我们使用着色器来编程,因此在顶点着色器阶段,我们会使用该函数来给着色器中的in类型的属性变量传递数据。
那么它们是怎么联系起来的呢?
便是通过第一个参数index,指明了着色器程序中变量的下标的作用。
例如:layout( location=index ) in vec4
如果这个index和glVertexAttribPointer的第一个参数一样,那么相关缓存区的数据就会传递到这个position变量中去。
3、该函数执行之后,会影响改变VAO的状态,VBO会被复制保存到VAO中。之后如果改变了当前所绑定的缓存对象,也不会改变到VAO里的对象。
glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
因为默认情况下,顶点属性数组是不可访问的,所以我们需要调用以下函数激活它。
范围为0到GL_MAX_VERTEX_ATTRIBS-1
glEnableVertexAttribArray(vPosition);
再之后我们进入渲染函数display
void display(void){
清除指定的缓存数据并且重新设置为当前的清除值
参数可以通过逻辑或操作来指定多个数值参数。
GL_COLOR_BUFFER_BIT 颜色缓存
GL_DEPTH_BUFFER_BIT 深度缓存
GL_STENCIL_BUFFER_BIT 模板缓存
而OpenGL默认的清除颜色是黑色
如果要指定其他颜色,可以调用glClearColor
另外我们还需要知道,因为OpenGL是一种状态机,因此对它的所有设定OpenGL都会保留。
所以对于glClearColor,最好的调用方法是放在初始化方法中,因为这样它只会被调用一次
如果放在display中,OpenGL这样每一帧都会调用它重复设置清除颜色值,会降低运行效率。
glClear(GL_COLOR_BUFFER_BIT);
该函数我们在初始化函数见过,不过这次的功能不太一样
之前是初始化,现在是激活该顶点数组对象。
现在使用它作为顶点数据所使用的顶点数组。
glBindVertexArray(VAOs[Triangles]);
函数原型void glDrawArrays(GLenum mode, GLint first, GLsizei count);
设置了渲染模式为GL_TRIANGLES,起始位置位于缓存的0偏移位置,一共渲染NumVertices个元素。
以后我们会继续学习各种图元
glDrawArrays(GL_TRIANGLES, 0, NumVertices);
该函数强制吧所有进行中的OpenGL命令立即完成并传输到OpenGL服务端处理。
glFlush();
接下来是关于着色器部分的代码,由于内容过长,决定一分为二
地址在【第二篇 Hello OpenGL 续】
如果有发现我的内容有错误,恳请告诉我,我会改正,谢谢!该系列其他文章
从Cocos2d-x学习OpenGL -- 解析QUAD_COMMAND
文章介绍了Cocos2d-x的基本渲染结构,这篇顺着之前的渲染结构介绍渲染命令QUAD_COMMAND命令的部分,通过这部分的函数,学习OpenGL处理图片渲染的方法,首先介绍这节需要涉及到的基本概念VAO和VBO。VAO和VBO:顶点数组对象(Vertex Array Object &即VAO)是一个包含一个或数个顶点缓冲区对象(Vertex Buffer Object, 即 VBO)的对象,一般存储一个可渲染物体的所有信息。顶点缓冲区对象(VertexBuffer Object VBO)是你显卡内存中的一块高速内存缓冲区,用来存储顶点的所有信息。这些概念显得很晦涩,简而言之,一般我们绘制一些图形需要将所有顶点的信息存储在一个数组里,但是经常会出现一些点是被重复使用的,这样就会出现一个点的信息的存储空间被重复使用的问题,这样第一会造成存储控件的浪费,第二就是如果我们要修改这个点的信息,需要改多次。所以我们采用索引的方式来描述图形,这样可以用一个数组存储点的信息,另外一个数组存储点的索引,这样所有的点都是不同的,另外把顶点信息存储在显卡的内存中,减少了cpu向gpu传输数据的时间,提高了程序的渲染效率,这就是VBO,在OpenGL3.0中,出现了更进一步的VAO,VBO通过绘制上下文获得绘制状态,VAO可以拥有多个VBO,它记录所有绘制状态,它的代码更简洁,效率更高,在Cocos2d-x的绘制中,我们会判断底层是否支持VAO,如果支持VAO,那么优先采用VAO绘制。二者的区别可以从初始化就可以看出来:void&Renderer::setupBuffer()
&&&&if(Configuration::getInstance()-&supportsShareableVAO())
&&&&&&&&//初始化VBO和VAO
&&&&&&&&setupVBOAndVAO();
&&&&&&&&//不支持VAO,只初始化VBO
&&&&&&&&setupVBO();
void&Renderer::setupVBOAndVAO()
&&&&//一个VAO
&&&&glGenVertexArrays(1,&&_quadVAO);
&&&&//绑定VAO
&&&&GL::bindVAO(_quadVAO);
&&&&//创建生成两个VBO
&&&&glGenBuffers(2,&&_buffersVBO[0]);
&&&&//顶点Buffer
&&&&glBindBuffer(GL_ARRAY_BUFFER,&_buffersVBO[0]);
&&&&glBufferData(GL_ARRAY_BUFFER,&sizeof(_quads[0])&*&VBO_SIZE,&_quads,&GL_DYNAMIC_DRAW);
&&&&//这里就是VAO和VBO的区别,VAO把这些放到初始化中,无论后面绘制多少次,只要他不被改变,这段代码只会被调用一次,而VBO中,这个功能的代码会在每次被绘制时调用,这样就节约了效率
&&&&//位置
&&&&glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_POSITION);
&&&&glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,&3,&GL_FLOAT,&GL_FALSE,&sizeof(V3F_C4B_T2F),&(GLvoid*)&offsetof(&V3F_C4B_T2F,&vertices));
&&&&//颜色
&&&&glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_COLOR);
&&&&glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,&4,&GL_UNSIGNED_BYTE,&GL_TRUE,&sizeof(V3F_C4B_T2F),&(GLvoid*)&offsetof(&V3F_C4B_T2F,&colors));
&&&&//纹理坐标数据
&&&&glEnableVertexAttribArray(GLProgram::VERTEX_ATTRIB_TEX_COORDS);
&&&&glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS,&2,&GL_FLOAT,&GL_FALSE,&sizeof(V3F_C4B_T2F),&(GLvoid*)&offsetof(&V3F_C4B_T2F,&texCoords));
&&&&//索引Buffer
&&&&glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,&_buffersVBO[1]);
&&&&glBufferData(GL_ELEMENT_ARRAY_BUFFER,&sizeof(_indices[0])&*&VBO_SIZE&*&6,&_indices,&GL_STATIC_DRAW);
&&&&//取消VAO
&&&&GL::bindVAO(0);
&&&&glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,&0);
&&&&glBindBuffer(GL_ARRAY_BUFFER,&0);
&&&&CHECK_GL_ERROR_DEBUG();
void&Renderer::setupVBO()
&&&&//创建生成两个VBO
&&&&glGenBuffers(2,&&_buffersVBO[0]);
&&&&//调用函数绑定buffer
&&&&mapBuffers();
void&Renderer::mapBuffers()
&&&&//GL_ARRAY_BUFFER&表示顶点数据
&&&&//GL_ELEMENT_ARRAY_BUFFER&表示索引数据
&&&&//避免改变buffer元素
&&&&GL::bindVAO(0);
&&&&//绑定id&顶点数据
&&&&glBindBuffer(GL_ARRAY_BUFFER,&_buffersVBO[0]);
&&&&//为改id制定一段内存区域
&&&&glBufferData(GL_ARRAY_BUFFER,&sizeof(_quads[0])&*&VBO_SIZE,&_quads,&GL_DYNAMIC_DRAW);
&&&&glBindBuffer(GL_ARRAY_BUFFER,&0);
&&&&//第二个VBO&索引数据
&&&&glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,&_buffersVBO[1]);
&&&&glBufferData(GL_ELEMENT_ARRAY_BUFFER,&sizeof(_indices[0])&*&VBO_SIZE&*&6,&_indices,&GL_STATIC_DRAW);
&&&&glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,&0);
&&&&CHECK_GL_ERROR_DEBUG();
}需要介绍的两个关键的函数glBindBuffer:它绑定缓冲区对象表示选择未来的操作将影响哪个缓冲区对象。如果应用程序有多个缓冲区对象,就需要多次调用glBindBuffer()函数:一次用于初始化缓冲区对象以及它的数据,以后的调用要么选择用于渲染的缓冲区对象,要么对缓冲区对象的数据进行更新。当传入的第二个参数第一次使用一个非零无符号整数时,创建一个新的缓冲区对象;当第二个参数是之前使用过的,这个缓冲区对象成为活动缓冲区对象;如果第二个参数值为0时,停止使用缓冲区对象。glBufferData:保留空间存储数据,他分配一定大小的(第二个参数)的openGL服务端内存,用于存储顶点数据或索引。这个被绑定的对象之前相关联的数据都会被清除。glBufferData参数介绍参数1,目标GL_ARRAY_BUFFER或者GL_ELEMENT_ARRAY_BUFFER参数2,内存容量参数3,用于初始化缓冲区对象,可以使一个指针,也可以是空参数4,如何读写,可以选择如下几种:GL_DYNAMIC_DRAW:多次指定,多次作为绘图和图像指定函数的源数据,缓冲区对象的数据不仅常常需要进行更新,而且使用频率也非常高GL_STATIC_DRAW:数据只指定一次,多次作为绘图和图像指定函数的源数据,缓冲区对象的数据只指定1次,但是这些数据被使用的频率很高GL_STREAM_DRAW:数据只指定一次,最多只有几次作为绘图和图像指定函数的源数据,缓冲区对象中的数据常常需要更新,但是在绘图或其他操作中使用这些数据的次数较少从初始化的代码上,为什么VAO反倒复杂了呢?因为他只是把绘制时需要做的一些事情提前放到初始化函数中,来看一下绘制流程。&&&&//当前的openGL是否支持VAO
&&&&if&(Configuration::getInstance()-&supportsShareableVAO())
&&&&&&&&//绑定顶点数组
&&&&&&&&glBindBuffer(GL_ARRAY_BUFFER,&_buffersVBO[0]);
&&&&&&&&//向缓冲区申请空间并指定数据传输方式
&&&&&&&&glBufferData(GL_ARRAY_BUFFER,&sizeof(_quads[0])&*&(_numQuads),&nullptr,&GL_DYNAMIC_DRAW);
&&&&&&&&//提供缓冲区对象包含整个数据集合的更新
&&&&&&&&void&*buf&=&glMapBuffer(GL_ARRAY_BUFFER,&GL_WRITE_ONLY);
&&&&&&&&memcpy(buf,&_quads,&sizeof(_quads[0])*&(_numQuads));
&&&&&&&&//缓冲区对象的更新完成
&&&&&&&&glUnmapBuffer(GL_ARRAY_BUFFER);
&&&&&&&&//为了禁用缓冲区对象,可以用0作为缓冲区对象的标识符来调用glBindBuffer()函数。这将把OpenGL切换为默认的不使用缓冲区对象的模式。
&&&&&&&&glBindBuffer(GL_ARRAY_BUFFER,&0);
&&&&&&&&//Bind&VAO
&&&&&&&&GL::bindVAO(_quadVAO);
#define&kQuadSize&sizeof(_quads[0].bl)
&&&&&&&&glBindBuffer(GL_ARRAY_BUFFER,&_buffersVBO[0]);
&&&&&&&&glBufferData(GL_ARRAY_BUFFER,&sizeof(_quads[0])&*&_numQuads&,&_quads,&GL_DYNAMIC_DRAW);
&&&&&&&&//激活顶点颜色纹理坐标的属性
&&&&&&&&GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
&&&&&&&&//顶点
&&&&&&&&glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION,&3,&GL_FLOAT,&GL_FALSE,&kQuadSize,&(GLvoid*)&offsetof(V3F_C4B_T2F,&vertices));
&&&&&&&&//颜色
&&&&&&&&glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR,&4,&GL_UNSIGNED_BYTE,&GL_TRUE,&kQuadSize,&(GLvoid*)&offsetof(V3F_C4B_T2F,&colors));
&&&&&&&&//纹理坐标
&&&&&&&&glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS,&2,&GL_FLOAT,&GL_FALSE,&kQuadSize,&(GLvoid*)&offsetof(V3F_C4B_T2F,&texCoords));
&&&&&&&&glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,&_buffersVBO[1]);
&&&&}可以看到,这些设置属性的函数放在了绘制函数里,虽然看似是一样的,但是绘制函数会被调用的更频繁,所以把这些函数放到初始化函数中可以大幅提高程序的效率。这里介绍VAO的两个函数:glMapBuffer函数返回一个指针,指向与第一个参数相关联的当前绑定缓冲区对象的数据存储。第一个参数与glBufferData的第一个参数一致。第二个参数是GL_READ_ONLY、GL_WRITE_ONLY或GL_READ_WRITE之一,表示可以对数据进行的操作。glUnmapBuffer表示对当前绑定缓冲区对象的更新已经完成,并且这个缓冲区可以释放。最后需要调用绘制元素函数,绘制这些信息:glDrawElements(GL_TRIANGLES,&(GLsizei)&quadsToDraw*6,&GL_UNSIGNED_SHORT,&(GLvoid*)&(startQuad*6*sizeof(_indices[0]))&);它根据索引绘图(注意:顶点数据和索引各自使用不同的缓冲区)。需要注意的是在Renderer的析构函数中要调用glDeleteBuffers来释放它的资源,并使它的标识可以其他缓冲区对象使用。上一篇中介绍的几种渲染命令中的QUAD_COMMAND(这里把它称作四边形绘制)命令回调用drawBatchedQuads调用绘制函数,处理这个逻辑的命令是这样的:if(commandType&==&RenderCommand::Type::QUAD_COMMAND)
&&&&auto&cmd&=&static_cast&QuadCommand*&(command);
&&&&CCASSERT(nullptr!=&cmd,&&Illegal&command&for&RenderCommand&Taged&as&QUAD_COMMAND&);
&&&&&&&&&&&&&&&&&&&&
&&&&//如果Quad数据量超过VBO的大小,那么调用绘制,将缓存的命令全部绘制
&&&&if(_numQuads&+&cmd-&getQuadCount()&&&VBO_SIZE)
&&&&&&&&CCASSERT(cmd-&getQuadCount()&=&0&&&&cmd-&getQuadCount()&&&VBO_SIZE,&&VBO&is&not&big&enough&for&quad&data,&please&break&the&quad&data&down&or&use&customized&render&command&);
&&&&&&&&drawBatchedQuads();
&&&&//将命令缓存起来,先不调用绘制
&&&&_batchedQuadCommands.push_back(cmd);
&&&&memcpy(_quads&+&_numQuads,&cmd-&getQuads(),&sizeof(V3F_C4B_T2F_Quad)&*&cmd-&getQuadCount());
&&&&//转换成世界坐标
&&&&convertToWorldCoordinates(_quads&+&_numQuads,&cmd-&getQuadCount(),&cmd-&getModelView());
&&&&//记录下四边形数量
&&&&_numQuads&+=&cmd-&getQuadCount();
}void&Renderer::flush()
&&&&//绘制
&&&&drawBatchedQuads();
&&&&//清空
&&&&_lastMaterialID&=&0;
}这个处理主要是把命令存入_batchedQuadCommands中,如果如果Quad数据量超过VBO的大小,那么调用绘制,将缓存的命令全部绘制。如果一直没有超过VBO的大小,drawBatchedQuads绘制函数将在flush被调用时调用。如有错误,欢迎指出。下一篇介绍图形渲染和批处理。
来源网址:
评论内容不能为空。
技术咨询邮箱:
沪ICP备号-5

我要回帖

更多关于 如果没有你 的文章

 

随机推荐