2画个三角形
ios
绘制三角形时,要用到顶点缓存(VBO)的知识。我的感受,这部分已是GL比较中级的知识了。因此这里不作详细解释,也不拿VBO和顶点数组(VA)和显示列表做对比。我以为,像GL这种实践性很强的技术,仍是先动手把效果实现了再说。《OpenGL超级宝典》之因此提供GLTools,就是想先把复杂的原理屏蔽掉,让初学者能快速实现效果,随着程序写的越多,有些原理天然而然就理解了。不然,一开始就灌输那么多原理的东西,但连个最基本的三角形都画不出来,我想大多数人都会很快失去兴趣,尤为是像DirectX和OpenGL这种偏底层的技术。编程
因此,这里我只简单说一下VBO的做用。VBO全名Vertex Buffer Object——顶点缓存对象。它的做用就是存储顶点的一系列属性,好比顶点的位置,颜色,法线,纹理坐标等等。GL在绘制的过程当中,就是根据VBO的数据把图形画出来。数组
既然,VBO是存储顶点的属性,那首先得有碗吧。缓存
因此,第一步就是建立VBO安全
voidglGenBuffers(GLsizein,GLuint*buffers); 服务器
n:要产生的VBO的数量ide
buffers:存储VBO的句柄。oop
PS:GLsizei=intGLuint= unsignedint学习
有人会问,干吗这么麻烦,直接用int不就行了。OpenGL是跨平台的API,因此要兼顾各个平台,因此本身从新定义了类型。这个知道就行,建议养成用GL专有类型的好习惯。ui
voidglBindBuffer(GLenum target,GLuint buffer);
绑定一个缓冲区对象。
target:可能取值是:GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, or GL_PIXEL_UNPACK_BUFFER.
咱们如今主要使用GL_ARRAY_BUFFER和 GL_ELEMENT_ARRAY_BUFFER。
GL_ARRAY_BUFFER用于保存以前说的,顶点的各类属性
GL_ELEMENT_ARRAY_BUFFER用于保存顶点索引,这个在之后用顶点索引模型绘制时会用到。
当进行绑定以后,之前的绑定就失效了。
在绑定的同时,也将该缓存设置成当前缓存。GL对缓存的一切操做,都是对当前缓存有效。
PS:这么作的缘由,是由于OpenGL采用状态机的模式。glBindBuffer就是讲GL的缓存模式切换到当前缓存。可能,习惯面向对象的人不习惯这种模式。GL是一种C语言API,因此GL中并无真正的对象改变。GL中的对象,实际上就是一个int类型的标识符,对于熟悉Windows编程的朋友来讲,也就是所谓的句柄。因此,GL要对使用或者改变某对象,采用的就是绑定该对象句柄的方式。而不是,C++的真正类,能够直接调用对象的成员方法。
选择好要操做的VBO以后,就要为VBO装数据,这里的数据,能够是顶点位置,顶点颜色等等
voidglBufferData(GLenum target,GLsizeiptr size,constGLvoid * data,GLenum usage);
target:和以前glBindBuffer中的target含义是同样的。
size:填充数据的字节数,注意是字节数,不是个数
data:具体的数据。一般用数组
usage:缓存区的用途。当不肯定用途时,使用GL_DYNAMIC_DRAW是一个比较安全的值。
准备工做都作好了,如今就能够开画了。
咱们画的三角形要到了顶点的两个属性:位置和颜色。
因此用到了两个VBO,咱们要建立两个VBO。
首先定义两个全局变量
在init()方法内,添加以下代码
GLuint vbo[2]; glGenBuffers(2, vbo); posBuf = vbo[0]; colorBuf = vbo[1];
而后,就是绘制三角形的代码了。
首先在display内,在glClear()和glSwapBuffer()之间,添加以下代码。
srand(time(0));
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat verts[] = {
200, 100, 0,
600, 100, 0,
400, 400, 0
};
GLfloat colors[] = {
(rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f,
(rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f,
(rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f
};
glBindBuffer(GL_ARRAY_BUFFER, posBuf);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glVertexPointer(3, GL_FLOAT, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, colorBuf);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glColorPointer(3, GL_FLOAT, 0, 0);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
#1:为随机数设置种子,种子为当前时间。关于C++随机数的问题,问Google~~,做为一名合格的码农,Google是必备技能。
#3 - #4:设置当前为模型视图模式,并重置模型试图矩阵。这里不详细解释,这些步骤都是必须的。等后面讲到GLSL时,天然而然就明白了。
#6 - #10:定义三角形的三个顶点。为了方面阅读,特地分红3行。3个顶点分别为(200, 100, 0), (600, 100, 0), (400, 400, 0)。注意哦,这个例子中,咱们使用的是二维坐标系,原点在左下角,宽是窗口的宽度(800),高是窗口的高度(600)。至于,怎么设置,稍后分析
#12 - #16:定义三角形的颜色。这下咱们srand()就派上用场了。所谓会变色,其实就是每秒随机生成一种颜色。GL设置颜色是使用浮点类型的,RGBA的范围都是0.0f~1.0f。
#18 - #20:这里就是关键代码了。
1.glBindBuffer(GL_ARRAY_BUFFER, posBuf);首先,先选择要操做的VBO。咱们先操做,存储顶点位置的混存。
2.glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
而后,为该缓冲区填充数据。注意第二个参数是字节大小,因此可使用sizeof获取。
注意,sizeof有个陷阱哦。由于sizeof()和verts是在同一方法体内,因此sizeof(verts)取得的是verts数组的字节大小。可是,若是verts是经过方法形参传递给sizeof,那sizeof(verts)取得的是float类型指针的大小,不是数组的大小。由于,数组在做为方法实参时,已经退化成指针了。因此,这时候咱们就要改为 N *sizeof(float), N是数组的个数
3.glVertexPointer(3, GL_FLOAT, 0, 0);设置顶点位置的指针。说明白点,就是让GL知道,若是想要顶点位置,就从当前缓存区里取就能够了。
参数1:顶点位置的大小。由于是3维坐标(x,y,z),天然取3
参数2:顶点位置的类型。GL_FlOAT,这个很明白了
参数3:每一个顶点属性的间隔。简单地说,咱们顶点位置和颜色都是分开设置的,利用不一样的数组。因此,每一个顶点位置都是连续的。好比(200, 100, 0),后面就是下个顶点(600, 100, 0),顶点位置之间的间隔为0。
参数4:这个参数可能有点迷糊人。虽然它的参数类型是指针类型,但在使用VBO时,实际上指的是偏移位置。咱们要用到VBO内的顶点位置,因此设为0
#22 - #24:设置颜色的方法,和设置顶点位置的方法是同样的。因此就不重复了。惟一的区别,就是告诉GL,这个缓存colorBuf保存的是顶点颜色。
glColorPointer(3, GL_FLOAT, 0, nullptr);
#26 - #27:开启对应的顶点属性。GL对于顶点的各个属性,默认都是关闭的。
GL_VERTEX_ARRAY:顶点位置
GL_COLOR_ARRAY:顶点颜色
注意到方法名有个Client吗?Client是客户端的意思(+_+没有人不知道吧~~)。这个例子,咱们用的仍是固定管线的作法,可是抛弃了glBegin/glEndglVertexglNormal。那玩意实在是太旧了。Client指的就是当前的GL程序,咱们在GL程序里设置好顶点属性后,再将属性传给管线,管线就是服务器了。由于是固定管线,咱们不能够干涉管线的工做。也由于这样,咱们才会调用glVertexPointer/glColorPointer来告诉GL,哪一个是顶点位置,哪一个是顶点颜色。等到后面,可编程管线时。咱们就能操做,管线的工做了。到那时,全部的顶点属性,统统调用glVertexAttribPointer来指定。固然,这是后话。
#28:这句就是真正把三角形画出来了。
glDrawArrays(GL_TRIANGLES, 0, 3);
GL有多重绘制模型,包括点,线,三角形,三角形扇等等。这里,咱们使用GL_TRIANGLES,来指定GL画三角形。
#29 - #30:恢复GL的默认设置。养成好习惯,绘制完成后,恢复GL的设置。
大功告成,来试试吧~~
说好的三角形呢?
对OpenGL了解的朋友,可能已经知道问题所在。到目前为止,都没有出现glViewport,gluOrtho,gluPerspective,致使没有设置GL的绘制区域和投影模式。如今咱们就来设置。
void reshape(int width, int height){
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 00, width, height);
gluOrtho2D(0, width, 0, height);
}
#2:选择当前模式为投影模式。
#3:重置当前的投影模式。
#5:glViewport:设置GL的绘制区域,这里咱们设置为整个窗口的大小。
#6:gluOrtho2D:设置GL的投影模式为正交投影。关于正交投影和透视投影,老方法,Google一大把。
实际上,以前咱们设定的二维坐标,就是这句代码起的做用。
打完收工。这下再来看看
三角形是有了,颜色也有了。but~~说好的变色呢。~~
其实新的颜色已经产生了,可是没有画出来而已,咱们只需在display()中的glSwapBuffers下面添加一句就能够了
glutPostRedisplay();
这句代码就是告诉glut,从新画一帧。
终于操蛋的出来了,不容易啊。
再来几张~~
附上所有源码
#define GLEW_STATIC #include <GL/glew.h> #include <GL/freeglut.h> #include <iostream> #include <ctime> GLuint posBuf, colorBuf; void init(){ glClearColor(0, 0, 0 ,1); glEnable(GL_DEPTH_TEST); GLuint vbo[2]; glGenBuffers(2, vbo); posBuf = vbo[0]; colorBuf = vbo[1]; } void display(){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); srand(time(0)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); GLfloat verts[] = { 200, 100, 0, 600, 100, 0, 400, 400, 0 }; GLfloat colors[] = { (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f }; glBindBuffer(GL_ARRAY_BUFFER, posBuf); glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); glVertexPointer(3, GL_FLOAT, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, colorBuf); glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW); glColorPointer(3, GL_FLOAT, 0, 0); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glDrawArrays(GL_TRIANGLES, 0, 3); glDisableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glutSwapBuffers(); glutPostRedisplay(); } void reshape(int width, int height){ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glViewport(0, 00, width, height); gluOrtho2D(0, width, 0, height); } void keyboard(unsigned char key, int x, int y){ } void keyboardUp(unsigned char key, int x, int y){ } void specialKey(int key, int x, int y){ } void specialKeyUp(int key, int x, int y){ } int main(int argc, char **argv){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(800, 600); glutCreateWindow("会变色的三角形"); glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutKeyboardUpFunc(keyboardUp); glutSpecialFunc(specialKey); glutSpecialUpFunc(specialKeyUp); glutDisplayFunc(display); glFlush(); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } init(); glutMainLoop(); return 0; }
本回到此为止,欲知后事如何,且听下回分解~~
PS:本篇做为一篇试用博文,最想看到就是效果如何。第一次写这么详细的文章,前先后后花了5个小时。其实,我也是接触OpenGL的一点皮毛,做为一个初学者,仍是能体会学习GL的难度。从刚开始接触NEHE教程,到红皮书,到国内出的杂七杂八的GL书籍,再到最后的蓝宝书。我也走了很多弯路,为了让之后的刚刚接触GL的初学者不要踏进以前我踩过的坑,因而萌生了把本身的学习心得记录下来的想法。
写的过程当中,本身确定有理解的误差,若是有任何不对的地方,敬请指正~~。若是对本文的写做风格有任何建议的话,尽管提~~
谢谢!