本文主要整理自http://www.aiuxian.com/article/p-1704414.html,感谢作者wangdingqiaoit
//包含头文件 # include <GL/glew.h> # include <GL/freeglut.h> #pragma comment(lib,"glew32d.lib") //函数原型声明 void userInit();//自定义初始化函数 void display(void);//绘制回调函数 void keyboardAction(unsigned char key, int x, int y);//键盘按键回调函数 //程序入口函数 int main(int argc, char **argv) { glutInit(&argc, argv);//初始化GLUT glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE);//设置显示模式 glutInitWindowPosition(100, 100);//设置窗口起始位置 glutInitWindowSize(512, 512);//设置绘图窗口的初始高度和宽 glutCreateWindow("Triangle demo");//在屏幕上创建一个窗口 glewInit();//使用GLEW时,使用该函数初始化GLEW userInit();//自定义的初始化函数 glutDisplayFunc(display);//注册绘制窗口回调函数 glutKeyboardFunc(keyboardAction);//注册键盘事件回调函数 glutMainLoop();//开始事件循环 return 0; } //自定义初始化函数 void userInit() { glClearColor(0.0, 0.0, 0.0, 0.0);//设置清屏颜色 glColor4f(1.0, 1.0, 0.0, 0.0);//设置绘制颜色 } //绘制回调函数 void display(void) { glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓存 glBegin(GL_TRIANGLES); glVertex3f(-0.5, -0.5, 0.0); glVertex3f(0.5, 0.0, 0.0); glVertex3f(0.0, 0.5, 0.0); glEnd(); glFlush();//强制刷新 } //键盘按键回调函数 void keyboardAction(unsigned char key, int x, int y) { switch (key) { case 033: // Escape key case 'q': case 'Q': exit(EXIT_SUCCESS);//退出程序 break; } }
//依赖库glew32.lib freeglut.lib //使用顶点列表绘制三角形(已过时,仅为学习目的) # include <GL/glew.h> # include <GL/freeglut.h> #pragma comment(lib,"glew32d.lib") void userInit(); void reshape(int w, int h); void display(void); void keyboardAction(unsigned char key, int x, int y); //显示列表句柄 GLuint displayListId; int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); glutInitWindowPosition(100, 100); glutInitWindowSize(512, 512); glutCreateWindow("Triangle demo"); glewInit(); userInit(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutKeyboardFunc(keyboardAction); glutMainLoop(); return 0; } //自定义初始化函数 void userInit() { glClearColor(0.0, 0.0, 0.0, 0.0); glColor4f(1.0, 1.0, 0.0, 0.0); //创建显示列表,和向显存中写入顶点数据 displayListId = glGenLists(1); glNewList(displayListId, GL_COMPILE); glBegin(GL_TRIANGLES); glVertex3f(-0.5, -0.5, 0.0); glVertex3f(0.5, 0.0, 0.0); glVertex3f(0.0, 0.5, 0.0); glEnd(); glEndList(); } //调整窗口大小回调函数 void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); } //绘制回调函数 void display(void) { glClear(GL_COLOR_BUFFER_BIT); //利用显示列表,绘制三角形 glPushMatrix(); glTranslatef(-0.5, 0, 0); glCallList(displayListId); // 绘制一次 glPopMatrix(); glPushMatrix(); glTranslatef(0.5, 0, 0); glCallList(displayListId); // 绘制二次 glPopMatrix(); glFlush(); } //键盘按键回调函数 void keyboardAction(unsigned char key, int x, int y) { switch (key) { case 033: // Escape key exit(EXIT_SUCCESS); break; } }
这里的VAO还是Vertex Array Object, 客户端的VAO(CPU RAM中的)。
使用顶点数组方式,需要利用glEnableClientState
开启一些特性,这里开启顶点数组特性使用glEnableClientState(GL_VERTEX_ARRAY)
。
使用顶点数组时,用户定义好存储顶点的数据,在调用glDrawArrays、glDrawElements之类的函数时,通过glVertexPointer
设定的指针,传送数据到GPU。当调用完glDrawArrays后,GPU中已经有了绘图所需数据,用户可以释放数据空间(其实是一帧结束后,每帧还是要向GPU传递数据的)。
一次draw call传递数据到显存中,绘制到后台缓存中,只是封装了glBegin/glEnd。
//依赖库glew32.lib freeglut.lib //使用Vertex Arrays顶点数组绘制三角形(不推荐使用) # include <GL/glew.h> # include <GL/freeglut.h> #pragma comment(lib, "glew32d.lib") void userInit(); void reshape(int w, int h); void display(void); void keyboardAction(unsigned char key, int x, int y); //定义一个包含3个float的结构体 //为了保持简单,暂时未引入c++类概念 struct vec3f { GLfloat x, y, z; }; int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); glutInitWindowPosition(100, 100); glutInitWindowSize(512, 512); glutCreateWindow("Triangle demo"); glewInit(); userInit(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutKeyboardFunc(keyboardAction); glutMainLoop(); return 0; } //自定义初始化函数 void userInit() { glClearColor(0.0, 0.0, 0.0, 0.0); glColor4f(1.0, 1.0, 0.0, 0.0); } //调整窗口大小回调函数 void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); } //绘制回调函数 void display(void) { glClear(GL_COLOR_BUFFER_BIT); //利用顶点数组,绘制三角形 const int num_indices = 3; //创建保存顶点的结构体数组 vec3f *vertices = new vec3f[num_indices]; // 顶点1 vertices[0].x = -0.5f; vertices[0].y = -0.5f; vertices[0].z = 0.0f; // 顶点2 vertices[1].x = 0.5f; vertices[1].y = 0.0f; vertices[1].z = 0.0f; //顶点3 vertices[2].x = 0.0f; vertices[2].y = 0.5f; vertices[2].z = 0.0f; // 启用vertex arrays glEnableClientState(GL_VERTEX_ARRAY); //定义顶点数组 glVertexPointer( 3, // 每个顶点的维度 GL_FLOAT, // 顶点数据类型 0, // 连续顶点之间的间隙,这里为0 vertices //指向第一个顶点的第一个坐标的指针 ); // 一次draw call传递数据到显存中,绘制到后台缓存中,只是封装了glBegin/glEnd glDrawArrays(GL_TRIANGLES, 0, num_indices); glDisableClientState(GL_VERTEX_ARRAY); //释放内存空间 delete[] vertices; // 将后台缓存提交到显示器,gluSwapBuffer翻转交换链提交到显示器 glFlush(); } //键盘按键回调函数 void keyboardAction(unsigned char key, int x, int y) { switch (key) { case 033: // Escape key exit(EXIT_SUCCESS); break; } }
VBO即Vertex Buffer Object,是一个在高速视频卡中的内存缓冲,用来保存顶点数据,也可用于包含诸如归一化向量、纹理和索引等数据。
VBO存储了实际的数据,真正重要的不是它存储了数据,而是他将数据存储在GPU中。这意味着VBO它会很快,因为存在RAM中的数据需要被传送到GPU中,因此这个传送是有代价的。
但是VBO的相关状态,在没有启用Shader情况下,VBO相关状态还是在CPU中设置的。
初始化时,用glBufferData, glBufferDataX拷贝数据到GPU以后,就可以释放掉数据了, Shader中也一样。
//依赖库glew32.lib freeglut.lib //使用VBO绘制三角形(现代OpenGL方式) # include <GL/glew.h> # include <GL/freeglut.h> #pragma comment(lib, "glew32d.lib") void userInit(); void reshape(int w, int h); void display(void); void keyboardAction(unsigned char key, int x, int y); //VBO句柄 GLuint vboId; int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); glutInitWindowPosition(100, 100); glutInitWindowSize(512, 512); glutCreateWindow("Triangle demo"); glewInit(); userInit(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutKeyboardFunc(keyboardAction); glutMainLoop(); return 0; } //自定义初始化函数 void userInit() { glClearColor(0.0, 0.0, 0.0, 0.0); glColor4f(1.0, 1.0, 0.0, 0.0); //创建顶点数据 GLfloat *vertices = (GLfloat*)malloc(sizeof(GLfloat) * 9); //memset(vertices, 0, sizeof(GLfloat)* 9); GLfloat vertices2[]= { -0.5, -0.5, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0 }; memcpy(vertices, vertices2, sizeof(GLfloat)* 9); //分配vbo句柄 glGenBuffersARB(1, &vboId); //GL_ARRAY_BUFFER_ARB表示作为顶点数组解析 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId); //拷贝数据 glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)*9, vertices, GL_STATIC_DRAW_ARB); // 对CPU禁用VBO glBindBufferARB(GL_VERTEX_ARRAY, 0); free(vertices); // glBufferData,glBufferDataARB 函数会一次拷贝完数据到GPU中,后面清除即可。 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);//绑定vbo glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性 glVertexPointer(3, GL_FLOAT, 0, 0); } //调整窗口大小回调函数 void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); } //绘制回调函数 void display(void) { glClear(GL_COLOR_BUFFER_BIT); glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);//绑定vbo //glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性 // 如何解析vbo中数据, 用VBO传递NULL数据进入,GPU会取到当前激活的VBO中的顶点数据; // 没有VAO则每帧需要glEnableClientState和glVertexPointer指定,当然绘制一个数据 可以不指定,绘制多个应该有问题。 //glVertexPointer(3, GL_FLOAT, 0, 0); glDrawArrays(GL_TRIANGLES, 0, 3); //glDisableClientState(GL_VERTEX_ARRAY); glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);//解除绑定 glFlush(); } //键盘按键回调函数 void keyboardAction(unsigned char key, int x, int y) { switch (key) { case 033: // Escape key exit(EXIT_SUCCESS); break; } }
VAO代表的是一些描述存储在VBO中对象的属性。VAO可以被视为指向对象的高级内存指针,有点类似于C语言指针,但比地址多了跟多的跟踪作用。他们很复杂。
VAO很像是辅助对象,而不是实际的数据存储对象。他们记录了当前绘制过程中的属性,描述对象的属性,而不是已经存储在VBO中原始数据。
VAO并不与VBO直接相关,进过初看起来如此。VAOs节省了设置程序所需的状态的时间。如果没有VAO,你需要调用一堆类似gl*之类的命令(例如glVertexPointer 在绘制时候调用,设置时候真正关联VAO和VBO还是用glVertexPointer等函数的)。
VAO只是存储了顶点数组对象集合,每个顶点数组对象关联VBO,和关联绘制函数绘制状态,需要切换绘制物体,直接一句glBindVertexArray(VAO[i]);切换激活的顶点数组对象即可。
VBO将真实的顶点数据存储在GPU显卡中,提供绘图数据传递性能,绘制时候还是要用glDrawElements等绘图函数,这个glDrawElements在VBO下没有将数据从CPU传递到GPU,但是驱动了GPU中的顶点数据,进行渲染管道的转换和光照计算,像素融合计算,进行stencil depth test, 抖动融合逻辑操作等,提交到当前帧中的后台颜色缓存中,所以在VBO下也要靠减少Draw call来提高图形性能的。
//依赖库glew32.lib freeglut.lib //使用VBO绘制三角形(现代OpenGL方式) # include <GL/glew.h> # include <GL/freeglut.h> #pragma comment(lib, "glew32d.lib") void userInit(); void reshape(int w, int h); void display(void); void keyboardAction(unsigned char key, int x, int y); //VBO句柄 GLuint vboId[2]; GLuint vaoId[2]; int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_SINGLE); glutInitWindowPosition(100, 100); glutInitWindowSize(1024, 1024); glutCreateWindow("Triangle demo"); glewInit(); userInit(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutKeyboardFunc(keyboardAction); glutMainLoop(); return 0; } //自定义初始化函数 void userInit() { glClearColor(0.0, 0.0, 0.0, 0.0); glGenVertexArrays(2, vaoId); //分配vbo句柄 glGenBuffersARB(2, vboId); //创建顶点数据 GLfloat *vertices = (GLfloat*)malloc(sizeof(GLfloat)* 9); memset(vertices, 0, sizeof(GLfloat)* 9); glBindVertexArray(vaoId[0]); //GL_ARRAY_BUFFER_ARB表示作为顶点数组解析 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId[0]);//绑定vbo glColor4f(1.0, 1.0, 0.0, 0.0); GLfloat vertices1[] = { -0.5, -0.5, 0.0, 0.0, 0.0, 0.0, -0.5, 0.5, 0.0 }; memcpy(vertices, vertices1, sizeof(GLfloat)* 9); //拷贝数据 glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)* 9, vertices, GL_STATIC_DRAW_ARB); glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性 glVertexPointer(3, GL_FLOAT, 0, 0); glBindVertexArray(vaoId[1]); glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId[1]); GLfloat vertices2[] = { 0, 0, 0.0, 0.5, -0.5, 0.0, 0.5, 0.5, 0.0 }; memcpy(vertices, vertices2, sizeof(GLfloat)* 9); glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat)* 9, vertices, GL_STATIC_DRAW_ARB); glEnableClientState(GL_VERTEX_ARRAY);//启用顶点数组属性 glVertexPointer(3, GL_FLOAT, 0, 0); // 对CPU禁用VBO glBindBufferARB(GL_VERTEX_ARRAY, 0); free(vertices); // glBufferData,glBufferDataARB 函数会一次拷贝完数据到GPU中,后面清除即可。 } //调整窗口大小回调函数 void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); } //绘制回调函数 void display(void) { glClear(GL_COLOR_BUFFER_BIT); for (int i = 0; i < 2; ++i) { float r = 0; float g = 0; if (i == 0) { r = 1.0f; } else { g = 1.0f; } glColor4f(r, g, 0.0, 0.0); /*glPushMatrix(); glTranslatef(0.5 * i, 0.0f, 0.0f);*/ // vao是关联了VBO的索引结构,通过glBindVertexArray(vaoId);可以切换不同的VBO数据。 glBindVertexArray(vaoId[i]); glDrawArrays(GL_TRIANGLES, 0, 3); //glPopMatrix(); } glFlush(); } //键盘按键回调函数 void keyboardAction(unsigned char key, int x, int y) { switch (key) { case 033: // Escape key exit(EXIT_SUCCESS); break; } }
服务器端VAO(绘制索引为简单), VBO(顶点存放),Shader(可编程管线控制)绘制,每帧绘制只用glDraw
使用VBO,向GPU VBO中传递数据
这里的VAO是:Vertex Array Object和vertex attribute object
(1)VAO需要glBindVertexArray(vaoId);和glEnableVertexAttribArray(index); glVertexAttribPointer关联顶点属性数据和Shader输入属性索引,不能用客户端的的glVertexPointer关联VAO和VBO了,因为要考虑Shader的顶点属性的输入。
(2)无论是VBO数据,还是Shader程序都是载入一次到GPU中,后面每帧渲染时候激活和去激活即可。
(3) 数据不改变时候可以封装到Vertex Array Object中, 实现display时候简单切换;即可以在创建VBO时候,关联好VAO如何解析VBO(也就是glEnableVertexAttribArray(index); glVertexAttribPointer会根据绘制截断指定的数据,但不能glDisableVertexAttribArray(VBO的关闭却是可以的))的状态和索引集合,绘制时候glBindVertexArray(vaoId)切换绘制状态集合,给glUseProgram(programId)绘制即可。
但当Vertext Shader中的数据是改变的,那么在每帧display中都需要指定:glEnableVertexAttribArray,glVertexAttribPointer,而不能封装到Vertex Array Object中。
更多VAO,VBO,Shader使用的提示:
1.没有一个合适的地方给glDisableVertexAttribArray了,事实上调用glBindVertexArray(NULL)的时候里面所有状态都”关掉“了,也就没所谓针对顶点属性的location做其他什么;
2.glBindBuffer(GL_ARRAY_BUFFER, NULL)/glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL)一定要在glBindVertexArray(NULL)后面(不然VAO就把它们也包含了,最后就渲染不出东西了);VAO只是记录了VBO的数据位置,解析VBO GL_ARRAY_BUFFER顶点中位置,uv, 法向量,颜色,雾,边缘的属性索引下标,GL_ELEMENT_ARRAY_BUFFER加载的索引数据的位置等VBO顶点索引数据,当glBindVertexArray(NULL)时候就会关闭索引的记录。
3.glDrawElements里面的东西(顶点索引的属性状态)VAO可没记录保存,因此需要绘制;
4.glVertexPointer那类函数理论上也可以,但是建议还是不要混用deprecated的函数进去了。
(4)顶点属性是并行输入给顶点Shader的,片元Shader是在光栅化插值位置后,也是并行输入处理逐个像素的。
(5)一次DrawCall就会进行一次图形渲染流水线过程(无论是固定管线,还是Shader的,顶点无论是VBO 显卡中,还是CPU RAM中的),进行顶点变换,光照计算,三角形背面剔除顶点Shader;硬件透视除法视口转换,光栅化;片元着色,计算纹理,抗锯齿,雾计算片元着色器计算;alpha检测,深度检测,stencil检测; alpha融合,抖动,逻辑操作,载入当前的后台FrameBuffer中。
所以减少DrawCall次数,单次提交的数据量,和启用的渲染状态计算量,才能正真提高GPU渲染的性能。
CPU的性能在CPU端的运算。
内存的压力在于GPU显存的使用,CPU计算的数据量和算法复杂度, CPU计算的频率。
IO的压力在于场景大小数据量的大小,和是否能够预加载和重用数据。
着色器就是运行在GPU上的一个程序而已。在绘制管线中有几个可能的着色器阶段,每个阶段都有它自己的输入输出。着色器的目的是将输入包括潜在的其他类型数据,转换到输出集中。
每个着色器都在输入集上执行。值得注意的是,在任何阶段,一个着色器都完全独立于那一阶段的其他着色器(最适合并发简单处理,而不是复杂的CPU逻辑)。独立执行的着色器之间不会有交叉。每个输入集的处理从着色器开始到结束阶段。着色器定义了它的输入输出,通常,没有完成输出数据任务的着色器是非法的。
着色器有着输入输出,就好比一个有参数和返回值的函数一样。
输入和输出来自和转到一些地方去了。因此,输入position 肯定在某处被填充了数据。那么这些数据来自哪里呢?顶点着色器的输入被称为顶点属性(vertex attributes)。
每个顶点着色器的输入有一个索引位置称作属性索引(attribute index.), 用于标识从顶点属性(顶点数组)那个位置开始取得顶点属性数据。
#ifndef _SHADER_H_ #define _SHADER_H_ #include <vector> #include <string> #include <cstring> #include <GL/glew.h> class Shader { public: static GLuint createShader(GLenum eShaderType, const std::string &strShaderFile); static GLuint createShader(GLenum eShaderType, const char* fileName); static GLuint createProgram(const std::vector<GLuint> &shaderList); }; #endif
#include <fstream> #include <sstream> #include "Shader.h" //从字符串流构造着色器对象 GLuint Shader::createShader(GLenum eShaderType, const std::string &strShaderFile) { GLuint shader = glCreateShader(eShaderType);//根据类型创建shader const char * strFileData = strShaderFile.c_str(); glShaderSource(shader, 1, &strFileData, NULL);//绑定shader字符串 glCompileShader(shader);//编译shader //检查shader状态 GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { GLint infoLogLength; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); GLchar *strInfoLog = new GLchar[infoLogLength + 1]; glGetShaderInfoLog(shader, infoLogLength, NULL, strInfoLog); const char * strShaderType = NULL; switch (eShaderType) { case GL_VERTEX_SHADER: strShaderType = "vertex"; break; case GL_GEOMETRY_SHADER: strShaderType = "geometry"; break; case GL_FRAGMENT_SHADER: strShaderType = "fragment"; break; } fprintf(stderr, "Compile failure in %s shader:\n%s\n", strShaderType, strInfoLog); delete[] strInfoLog; } return shader; } //从文件构造着色器对象 GLuint Shader::createShader(GLenum eShaderType, const char* fileName) { std::ifstream infile(fileName); if (!infile) { fprintf(stderr, "Could not open file : %s for reading.", fileName); return 0; } std::stringstream buffer; buffer << infile.rdbuf(); infile.close(); return Shader::createShader(eShaderType, buffer.str()); } //构造着色器程序对象 GLuint Shader::createProgram(const std::vector<GLuint> &shaderList) { GLuint programId = glCreateProgram();//创建program for (std::vector<GLuint>::size_type iLoop = 0; iLoop < shaderList.size(); iLoop++) glAttachShader(programId, shaderList[iLoop]);//绑定shader glLinkProgram(programId);//链接shader //检查program状态 GLint status; glGetProgramiv(programId, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLint infoLogLength; glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLogLength); GLchar *strInfoLog = new GLchar[infoLogLength + 1]; glGetProgramInfoLog(programId, infoLogLength, NULL, strInfoLog); fprintf(stderr, "Linker failure: %s\n", strInfoLog); delete[] strInfoLog; } for (size_t iLoop = 0; iLoop < shaderList.size(); iLoop++) glDetachShader(programId, shaderList[iLoop]); return programId; }
////依赖库glew32.lib freeglut.lib ////使用VAO VBO和着色器绘制三角形(现代OpenGL方式) //#include <string> //#include <vector> //#include <GL/glew.h> //#include <GL/freeglut.h> //#pragma comment(lib, "glew32d.lib") //#include "Shader.h" //using namespace std; // //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("Triangle demo"); // // glewInit(); // 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.0f, 0.0f, 1.0f, // 0.0f, 0.5f, 0.0f, 1.0f // }; // //创建vertex array object对象 // glGenVertexArrays(1, &vaoId); // glBindVertexArray(vaoId); // //创建vertex buffer object对象 // glGenBuffers(1, &vboId); // glBindBuffer(GL_ARRAY_BUFFER, vboId); // glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // //glVertexPointer(4, GL_FLOAT, 0, 0);// 一定要VBO激活的时候才能使用,传递数据 // //glEnableClientState(GL_VERTEX_ARRAY); // glBindBuffer(GL_ARRAY_BUFFER, 0); // //创建着色器 // const std::string vertexStr( // "#version 330\n" // "in vec4 position;\n"// layout(location=0) 这个可以不使用,默认应该是该值 // "void main()\n" // "{gl_Position = position;}\n" // ); // const std::string fragmentStr( // "#version 330\n" // "out vec4 outputColor;\n" // "void main()\n" // "{outputColor = vec4(1.0f,1.0f,0.0f,1.0f);}\n" // ); // std::vector<GLuint> idVector; // idVector.push_back(Shader::createShader(GL_VERTEX_SHADER, vertexStr)); // 指定Shader文件对应的着色器类型 // idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER, fragmentStr)); // programId = Shader::createProgram(idVector); //} ////调整窗口大小回调函数 //void reshape(int w, int h) //{ // glViewport(0, 0, (GLsizei)w, (GLsizei)h); //} ////绘制回调函数 //void display(void) //{ // glClear(GL_COLOR_BUFFER_BIT); // glUseProgram(programId); // glBindBuffer(GL_ARRAY_BUFFER, vboId); // //glBindVertexArray(vaoId); // // 当然不用VAO的话也可以只用VBO,这里就是glEnableVertexAttribArray(0),glVertexAttribPointer指定 // glEnableVertexAttribArray(0); // glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); // glDrawArrays(GL_TRIANGLES, 0, 3); // // glBindBuffer(GL_ARRAY_BUFFER, 0); // glUseProgram(0); // glDisableVertexAttribArray(0); // glutSwapBuffers(); //} ////键盘按键回调函数 //void keyboardAction(unsigned char key, int x, int y) //{ // switch (key) // { // case 033: // Escape key // exit(EXIT_SUCCESS); // break; // } //} //依赖库glew32.lib freeglut.lib //使用着色器颜色插值绘制三角形 #include <string> #include <vector> #include <GL/glew.h> #include <GL/freeglut.h> #pragma comment(lib, "glew32d.lib") #include "shader.h" using namespace std; 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 句柄 GLuint offsetLocationId; int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); glutInitWindowPosition(100, 100); glutInitWindowSize(512, 512); glutCreateWindow("Triangle demo"); glewInit(); userInit(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutKeyboardFunc(keyboardAction); glutMainLoop(); return 0; } //自定义初始化函数 void userInit() { glClearColor(0.0, 0.0, 0.0, 0.0); //顶点位置和颜色数据 const GLfloat vertexData[] = { -0.5f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }; //创建vertex array object对象 glGenVertexArrays(1, &vaoId); glBindVertexArray(vaoId); //创建vertex buffer object对象 glGenBuffers(1, &vboId); glBindBuffer(GL_ARRAY_BUFFER, vboId); glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); //启用顶点位置属性索引,要在display中指定的,因为需要开启顶点属性索引才能绘制,特别是绘制物体多的时候,需要切换才能正确绘制。 // 也可以封装在VAO中,只负责启用glEnableVertexAttribArray不关闭即可。 glEnableVertexAttribArray(0); // 激活顶点属性数组 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); //指定Position顶点属性数据格式,大小会根据glDrawArrays截断。 //启用顶点颜色属性索引 glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)48);// 指定Color顶点属性数据格式,大小会根据glDrawArrays截断。 glBindBuffer(GL_ARRAY_BUFFER, 0); // 这里不能关闭,否则Shader取不到数据 //glDisableVertexAttribArray(0); // 去激活VAO的顶点属性 //glDisableVertexAttribArray(1); //从文件创建着色器 /*std::vector<GLuint> idVector; string strPre = "E:\\OpenGL\\OpenGl7thEdition-master\\OpenGl7thEdition-master\\OpenGL_MyProject\\hello\\data"; idVector.push_back(Shader::createShader(GL_VERTEX_SHADER, strPre + "\\vertex.glsl")); idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER, strPre + "\\fragment.glsl")); programId = Shader::createProgram(idVector);*/ //从文件创建着色器 std::vector<GLuint> idVector; const std::string vertexStr( "#version 330\n" "in vec4 pos;\n" "in vec4 incolor;\n" "uniform vec2 offset;\n" "smooth out vec4 thecolor;\n" "void main()\n" "{\n" "vec4 totalOffset = vec4(offset.x, offset.y, 0.0, 0.0);\n" "gl_Position = pos + totalOffset;\n" "thecolor = incolor;}\n" ); const std::string fragmentStr( "#version 330\n" "smooth in vec4 thecolor;\n" "out vec4 outputColor;\n" "void main()\n" "{outputColor = thecolor;}\n" ); idVector.push_back(Shader::createShader(GL_VERTEX_SHADER, vertexStr));// "data\\vertex.glsl")); idVector.push_back(Shader::createShader(GL_FRAGMENT_SHADER, fragmentStr));// "data\\fragment.glsl")); programId = Shader::createProgram(idVector); offsetLocationId = glGetUniformLocation(programId, "offset"); //int nStereoSupport = 0; //glGetIntegerv(GL_STEREO, &nStereoSupport); // Win7 OGL 3.1不支持 //int nDoubleFrameBufferSupport = 0; //glGetIntegerv(GL_DOUBLEBUFFER, &nDoubleFrameBufferSupport);// Win7 OGL 3.1支持 //int nAluColorBuffer = 0; //glGetIntegerv(GL_AUX_BUFFERS, &nAluColorBuffer);// Win7 OGL 3.1不支持,只有0个颜色辅助缓存 } //调整窗口大小回调函数 void reshape(int w, int h) { glViewport(0, 0, (GLsizei)w, (GLsizei)h); } //根据时间计算偏移量 void ComputePositionOffsets(GLfloat &fXOffset, GLfloat &fYOffset) { const GLfloat fLoopDuration = 5.0f; const GLfloat fScale = 3.14159f * 2.0f / fLoopDuration; GLfloat fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f; GLfloat fCurrTimeThroughLoop = fmodf(fElapsedTime, fLoopDuration); fXOffset = cosf(fCurrTimeThroughLoop * fScale) * 0.5f; fYOffset = sinf(fCurrTimeThroughLoop * fScale) * 0.5f; } //绘制回调函数 //void display(void) //{ // // glClear(GL_COLOR_BUFFER_BIT); // // 绑定到VAO状态,也就是封装了通过VAO 的glEnableVertexAttribArray,glVertexAttribPointer关联起来可以解释的VBO数据作为输入 // // 这样通过VAO的切换,就可以在轻松的切换VBO数据源,且正确的解释VBO数据源作为Shader的输入,能够方便的进行绘制切换。 // // // glBindVertexArray(vaoId); // glUseProgram(programId);// 启用GPU中的Shader机器码程序 // GLfloat fXOffset = 0.0f, fYOffset = 0.0f; // ComputePositionOffsets(fXOffset, fYOffset); // glUniform2f(offsetLocationId, fXOffset, fYOffset);//偏移量发送到顶点着色器 // // // //绘制三角形,用glDrawElemenets不能正确绘制,因为这里需要连续的 // glDrawArrays(GL_TRIANGLES, 0, 3); // glUseProgram(0); // // 关闭GL_ARRAY_BUFFER,glDisableVertexAttribArray,也是可以正确绘制的, // // 说明glBindVertexArray(vaoId)是正确封装了需要关联了启用状态和索引关系的集合,直接glBindVertexArray切换绘制即可。 // //glBindBuffer(GL_ARRAY_BUFFER, 0); // 去激活GPU中的该VBO // //glDisableVertexAttribArray(0); // 去激活VAO的顶点属性 // //glDisableVertexAttribArray(1); // // glutSwapBuffers(); //} //绘制回调函数 void display(void) { glClear(GL_COLOR_BUFFER_BIT); //计算偏移量 GLfloat fXOffset = 0.0f, fYOffset = 0.0f; ComputePositionOffsets(fXOffset, fYOffset); glUseProgram(programId); glUniform2f(offsetLocationId, fXOffset, fYOffset);//偏移量发送到顶点着色器 glBindBuffer(GL_ARRAY_BUFFER, vboId); //启用顶点位置属性索引 glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); //绘制三角形 glDrawArrays(GL_TRIANGLES, 0, 3); glBindBuffer(GL_ARRAY_BUFFER, 0); glUseProgram(0); glDisableVertexAttribArray(0); glutSwapBuffers(); glutPostRedisplay();//不断刷新 } //键盘按键回调函数 void keyboardAction(unsigned char key, int x, int y) { switch (key) { case 033: // Escape key exit(EXIT_SUCCESS); break; } }