OpenGL(试用篇)——第一个OpenGL程序(2)

2画个三角形
ios

绘制三角形时,要用到顶点缓存(VBO)的知识。我的感受,这部分已是GL比较中级的知识了。因此这里不作详细解释,也不拿VBO和顶点数组(VA)和显示列表做对比。我以为,像GL这种实践性很强的技术,仍是先动手把效果实现了再说。《OpenGL超级宝典》之因此提供GLTools,就是想先把复杂的原理屏蔽掉,让初学者能快速实现效果,随着程序写的越多,有些原理天然而然就理解了。不然,一开始就灌输那么多原理的东西,但连个最基本的三角形都画不出来,我想大多数人都会很快失去兴趣,尤为是像DirectXOpenGL这种偏底层的技术。编程

因此,这里我只简单说一下VBO的做用。VBO全名Vertex Buffer Object——顶点缓存对象。它的做用就是存储顶点的一系列属性,好比顶点的位置,颜色,法线,纹理坐标等等。GL在绘制的过程当中,就是根据VBO的数据把图形画出来。数组

2.1顶点,快到碗里来~

2.1.1建立VBO


既然,VBO是存储顶点的属性,那首先得有碗吧。缓存

因此,第一步就是建立VBO安全

voidglGenBuffers(GLsizein,GLuint*buffers); 服务器

n:要产生的VBO的数量ide

buffers:存储VBO的句柄。oop

PSGLsizei=intGLuint= unsignedint学习

有人会问,干吗这么麻烦,直接用int不就行了。OpenGL是跨平台的API,因此要兼顾各个平台,因此本身从新定义了类型。这个知道就行,建议养成用GL专有类型的好习惯。ui


2.1.2绑定VBO


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++的真正类,能够直接调用对象的成员方法。


2.1.3VBO填充数据


选择好要操做的VBO以后,就要为VBO装数据,这里的数据,能够是顶点位置,顶点颜色等等

voidglBufferData(GLenum target,GLsizeiptr size,constGLvoid * data,GLenum usage);

target和以前glBindBuffer中的target含义是同样的。

size填充数据的字节数,注意是字节数,不是个数

data:具体的数据。一般用数组

usage:缓存区的用途。当不肯定用途时,使用GL_DYNAMIC_DRAW是一个比较安全的值。


2.1.4开始画了

准备工做都作好了,如今就能够开画了。

咱们画的三角形要到了顶点的两个属性:位置和颜色。

因此用到了两个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的设置。


大功告成,来试试吧~~

p_w_picpath

说好的三角形呢?

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:重置当前的投影模式。


#5glViewport:设置GL的绘制区域,这里咱们设置为整个窗口的大小。


#6gluOrtho2D:设置GL的投影模式为正交投影。关于正交投影和透视投影,老方法,Google大把。


实际上,以前咱们设定的二维坐标,就是这句代码起的做用。

打完收工。这下再来看看

p_w_picpath

三角形是有了,颜色也有了。but~~说好的变色呢。~~      

其实新的颜色已经产生了,可是没有画出来而已,咱们只需在display()中的glSwapBuffers下面添加一句就能够了


glutPostRedisplay();


这句代码就是告诉glut,从新画一帧。

终于操蛋的出来了,不容易啊。

再来几张~~

p_w_picpath

p_w_picpath

附上所有源码

#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的初学者不要踏进以前我踩过的坑,因而萌生了把本身的学习心得记录下来的想法。

写的过程当中,本身确定有理解的误差,若是有任何不对的地方,敬请指正~~。若是对本文的写做风格有任何建议的话,尽管提~~

谢谢!

相关文章
相关标签/搜索