标签:c++ opengl 图形 3d游戏开发 顶点缓存对象
过年前忍不住买了本新版的OpenGL编程指南,主要的目的还是为了系统的学习着色器编程,另外就是接触新版的OpenGL技术和思想。看了几页,就过年了QAQ.回来后也是各种不在状态,不想上班,不想工作,不想写代码。。。昨天终于强迫自己继续看书,也找回了些状态。#version 330 uniform mat4 model_matrix; uniform mat4 projection_matrix; layout (location = 0) in vec4 position; layout (location = 1) in vec4 color; out vec4 vs_fs_color; void main(void) { vs_fs_color = color; gl_Position = projection_matrix * (model_matrix * position); }
#version 330 in vec4 vs_fs_color; layout (location = 0) out vec4 color; void main(void) { color = vs_fs_color; }
typedef struct { GLenum type; const char* filename; GLuint shader; } ShaderInfo; GLuint LoadShaders( ShaderInfo* );
///////////////////////////////////////////////////////////////////////////// // // --- LoadShaders.cxx --- // ////////////////////////////////////////////////////////////////////////////// #include <cstdlib> #include <iostream> / #define GLEW_STATIC #include <GL/glew.h> #include "LoadShaders.h" #ifdef __cplusplus extern "C" { #endif // __cplusplus //---------------------------------------------------------------------------- static const GLchar* ReadShader( const char* filename ) { #ifdef WIN32 FILE* infile; fopen_s( &infile, filename, "rb" ); #else FILE* infile = fopen( filename, "rb" ); #endif // WIN32 if ( !infile ) { #ifdef _DEBUG std::cerr << "Unable to open file '" << filename << "'" << std::endl; #endif /* DEBUG */ return NULL; } fseek( infile, 0, SEEK_END ); int len = ftell( infile ); fseek( infile, 0, SEEK_SET ); GLchar* source = new GLchar[len+1]; fread( source, 1, len, infile ); fclose( infile ); source[len] = 0; return const_cast<const GLchar*>(source); } //---------------------------------------------------------------------------- GLuint LoadShaders( ShaderInfo* shaders ) { if ( shaders == NULL ) { return 0; } GLuint program = glCreateProgram(); //1.first,创建着色器程序实例。返回整形,类似于指针一样的东西。 ShaderInfo* entry = shaders; while ( entry->type != GL_NONE ) { GLuint shader = glCreateShader( entry->type ); //根据信息(枚举值),创建对应的着色器。 entry->shader = shader; const GLchar* source = ReadShader( entry->filename ); //读取着色器字符串 if ( source == NULL ) { for ( entry = shaders; entry->type != GL_NONE; ++entry ) { glDeleteShader( entry->shader ); //读取文件出错,删除着色器对象。 entry->shader = 0; } return 0; } glShaderSource( shader, 1, &source, NULL ); //将shader,着色器对象与相应的着色器字符串关联。 delete [] source; //删除着色器字符串,glShaderSource应该会做一个拷贝的操作,保存了字符串信息。 glCompileShader( shader ); //编译着色器 GLint compiled; glGetShaderiv( shader, GL_COMPILE_STATUS, &compiled ); //检查编译是否成功,输出编译信息。 if ( !compiled ) { #ifdef _DEBUG GLsizei len; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &len ); //获取编译日志的长度 GLchar* log = new GLchar[len+1]; glGetShaderInfoLog( shader, len, &len, log ); //取得日志 std::cerr << "Shader compilation failed: " << log << std::endl; //打印 delete [] log; #endif /* DEBUG */ return 0; } glAttachShader( program, shader ); //装配,将着色器对象关联都着色器程序中 //???一个着色器可以关联多个同一类型的着色器么?如多个顶点着色器你,不过好像没有什么意义 ++entry; } #ifdef GL_VERSION_4_1 if ( GLEW_VERSION_4_1 ) { // glProgramParameteri( program, GL_PROGRAM_SEPARABLE, GL_TRUE ); //需要多个着色器程序时使用4.1以上的OpenGL } #endif /* GL_VERSION_4_1 */ glLinkProgram( program ); //连接各个模块成完整的着色器程序。 GLint linked; glGetProgramiv( program, GL_LINK_STATUS, &linked );//检查连接过程是否成功,获取失败是的日志爱 if ( !linked ) { #ifdef _DEBUG GLsizei len; glGetProgramiv( program, GL_INFO_LOG_LENGTH, &len ); GLchar* log = new GLchar[len+1]; glGetProgramInfoLog( program, len, &len, log ); std::cerr << "Shader linking failed: " << log << std::endl; delete [] log; #endif /* DEBUG */ for ( entry = shaders; entry->type != GL_NONE; ++entry ) { glDeleteShader( entry->shader );//连接出现错误,删除相应着色器 entry->shader = 0; } return 0; } return program; } //---------------------------------------------------------------------------- #ifdef __cplusplus } #endif // __cplusplus
#include"shaderBase.h" #include "include\vmath.h" #include "include\LoadShaders.h" float shade_aspect = 800/600; //固定的长宽比,由于之前的窗口初始化代码走的的固定流水线过程,这里并没有重构。 GLuint render_prog; GLuint shade_vao[1]; GLuint shade_vbo[1]; GLuint shade_ebo[1]; GLint render_model_matrix_loc; GLint render_projection_matrix_loc; void shadeBaseInit() { static ShaderInfo shader_info[] = { { GL_VERTEX_SHADER, "../8edlib/shaders/primitive_restart.vs.glsl"}, { GL_FRAGMENT_SHADER, "../8edlib/shaders/primitive_restart.fs.glsl"}, { GL_NONE, NULL }, };// render_prog = LoadShaders(shader_info);//着色器程序生成 glUseProgram( render_prog );//使用该着色器程序 GLenum error = glGetError();//之前因为源代码,还有书本的错误,各种glGetError找错误。。。。 const GLubyte* errorStr = gluErrorString(error); render_model_matrix_loc = glGetUniformLocation( render_prog, "model_matrix");//获取Uniform变量在着色器程序中的位置。 error = glGetError(); errorStr = gluErrorString(error); render_projection_matrix_loc = glGetUniformLocation( render_prog, "projection_matrix"); error = glGetError(); errorStr = gluErrorString(error); ////三角形数据,顶点,颜色,索引 // A single triangle static const GLfloat vertex_positions[] = { -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, }; // Color for each vertex static const GLfloat vertex_colors[] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f }; // Indices for the triangle strips static const GLuint vertex_indices[] = { 0, 1, 2 }; //set up the element array buffer glGenBuffers(1, shade_ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, shade_ebo[0]); glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(vertex_indices), vertex_indices, GL_STATIC_DRAW ); //set up then vertex attributes glGenVertexArrays(1, shade_vao); //顶点数组对象标识申请,管理着顶点属性的集合 glBindVertexArray(shade_vao[0]);//在这里将做3个事情:1、如果参数是非0,并且是glGenvertexArrays()返回的新值,未经glBindVertexArray的 //那么他将创建一个新的顶点数组对象(这里才真正创建),并且与其名称关联起来。 //2、如果绑定包已经创建过的顶点数组对象,那么该顶点数组对象将被激活。这便于帧间切换绘制数据 //3、如果输入参数是0那么OpenGL将不再使用程序所分配的任何顶点数组对象,并且将渲染状态重设为默认值。 glGenBuffers(1, shade_vbo);//申请顶点缓存对象标识。 glBindBuffer(GL_ARRAY_BUFFER, shade_vbo[0]);//指定顶点缓存对象的用途,GL_ARRAY_BUFFER表示顶点数据。之力glBindBuffer同样完成了3项工作: //1、如果是第一次绑定对象(第二个参数),他是一个非0的无符号整型,那么将创建一个与名称对应的(第一个参数)新的缓存对象 //2、如果绑定到一个已经创建的缓存对象,那么她将被激活为当前使用对象。 //3、如果第二个参数是0,那么OpenGL不再为当前名称(第一个参数)应用任何缓存对象。 glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_positions) + sizeof(vertex_colors), NULL, GL_STATIC_DRAW);//所有前置工作准备好后,就到了向 //缓存对象输入数据的环节:glBufferData( GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage ), glBufferData是真正为缓存对象分配存储空间。 //1、target目标:顶点数据(GL_ARRAY_BUFFER)、索引数据(GL_ELEMENT_ARRAY_BUFFER)、OpenGL的像素数据(GL_PIXEL_UNPACK_BUFFER)、 //从OpenGL中获取的像素数据(GL_PIXEL_PACK_BUFFER)、缓存之间复制数据(GL_COPY_READ_BUFFER/GL_COPY_WRITE_BUFFER)、 //纹理缓存中存储的纹理数据(GL_TEXTURE_BUFFER)、一致性变量(GL_UNIFORM_BUFFER) //2、size:表示缓存数据的总量,字节数。 //3、data:是客户端应用程序的内存指针,数据的来源。要么是NULL,否则如果合法则将会有size大小的数据从客户端拷贝到服务端(显卡内存),如果data数据未初始化 //将保留size大小的内存备用。 //4、usage:用于设置分配数据之后的读取和写入方式,这关系都OpenGL对于缓存对象存储数据中的最优分配方案的管理。说白了,这个参数试图向OpenGL提供 //这堆数据的用途,是否只读,是否静态,用于绘制?拷贝?通过内置标识符的方式告诉OpenGL,OpenGL根据信息来优化内存分配,管理。 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertex_positions), vertex_positions );//用数据部分替换目标缓存的内容,注意在glBufferData中我们的data为NULL,所以在这里才真正初始化。 //这个接口使得我们的操作更为灵活,对数据的组织更为紧凑,因为我们只为顶点坐标数据和颜色数据分配了一块连续的空间。 //第二个参数是偏移地址,第三个参数是替换数据的大小,第四个参数是客户端内存指针,也就是数据源。需要注意的是不可超越glBufferData保留的内存。 //到这里关于缓存对象的操作只是最简单的部分,还有很多OpenGL接口供我们去灵活控制,优化。 glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertex_positions), sizeof(vertex_colors), vertex_colors ); glVertexAttribPointer( 0, 4, GL_FLOAT, GL_FALSE, 0, NULL );//最后需要在顶点着色器和缓村数据之间建立联系,以便着色器使用。 //glVertexAttribPointer(GLuint index, Glint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer ) //index:着色器中属性的位置,还记得layout(location = 0)的定义么,就是这个location. //size:表示每个顶点该属性需要更新的分量数目,这里顶点坐标为4个float,所以就是4.值的范围可以是1,2,3,4或GL_BGRA //type:数据类型 //normalized:使用顶点数据之前是否要进行归一化。 //stride:每组数据之间是否要进行偏移,如果是0则,数据是紧密的。 //pointer:表示缓存对象中,从开始位置开始计算数组数据的偏移值 glVertexAttribPointer( 1, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid *)sizeof(vertex_positions) ); glEnableVertexAttribArray(0);//参数对应于glVertexAttribPointer的index.也就是location值,与这个值相关联的定点数组将被启用。 glEnableVertexAttribArray(1);//启用颜色属性数组 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } void shadeBaseDisplay() { float t = float(GetTickCount() & 0x1FFF) / float(0x1FFF); static float q = 0.0f; //static const vmath::vec3 X(1.0f, 0.0f, 0.0f ); //static const vmath::vec3 Y(0.0f, 1.0f, 0.0f ); //static const vmath::vec3 Z(0.0f, 0.0f, 1.0f ); vmath::mat4 model_matrix; //setup glEnable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //active simple shading program glUseProgram( render_prog );//确定使用了对应的着色器程序 //set up the model and projection matrix vmath::mat4 projection_matrix(vmath::frustum(-1.0f, 1.0f, -shade_aspect, shade_aspect, 1.0f, 500.0f));//生成投影矩阵 glUniformMatrix4fv(render_projection_matrix_loc, 1, GL_FALSE, projection_matrix);//glUniform*()是一系列的函数,他们的作用是 //设置uniform变量的值,第一个参数是uniform的位置,通过glGetUniformLocation()获取,第二个参数是,有多少个对应 //数据集(前面函数名称后缀,这里是Matrix4fv是矩阵,第三个参数说明了数据的读取顺序,GL_FALSE以列为主序,否则以行为主序) //第四个参数是数据源。 GLenum error = glGetError(); const GLubyte* errorStr = gluErrorString(error); //set up for a glDrawElements call glBindVertexArray(shade_vao[0]);//已绑定过的顶点缓存对象,确保激活为使用对象。 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, shade_ebo[0]);//同理索引缓存激活 //draw arrays... model_matrix = vmath::translate( -3.0f, 0.0f, -5.0f ); glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, model_matrix); error = glGetError(); errorStr = gluErrorString(error); glDrawArrays(GL_TRIANGLES, 0, 3 );//绘制三角形,非索引绘制 error = glGetError(); errorStr = gluErrorString(error); //DrawElements model_matrix = vmath::translate( -1.0f, 0.0f, -5.0f ); glUniformMatrix4fv( render_model_matrix_loc, 1, GL_FALSE, model_matrix ); error = glGetError(); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, NULL);//根据顶点索引进行绘制。 error = glGetError(); //DrawElementBaseVertex model_matrix = vmath::translate(1.0f, 0.0f, -5.0f ); glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, model_matrix ); glDrawElementsBaseVertex(GL_TRIANGLES, 3, GL_UNSIGNED_INT, NULL, 1);//也是根据顶点索引绘制,本质上和glDrawElements没区别。 //根据偏移量灵活选择绘制数据,使得动画数据每一帧都可以索引相同的数据集。 error = glGetError(); //DrawArraysInstanced model_matrix = vmath::translate(3.0f, 0.0f, -5.0f); glUniformMatrix4fv(render_model_matrix_loc, 1, GL_FALSE, model_matrix ); glDrawArraysInstanced(GL_TRIANGLES, 0, 3, 1 ); //glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count GLsizei primcount),对glDrawArray() primcount次调用。 //first, count 指定了传递给glDrawArrays的范围。 error = glGetError(); //对了差点忘了那个书本和源代码的失误:glUniformMatrix4fv(render_model_matrix_loc, 4, GL_FALSE, model_matrix ); //第二个参数都写成4了,第二个参数的意义应该是多少个函数对应类型的数据集,这里是4*4的矩阵,无论是模型视图矩阵,还是投影 //变换矩阵都是1才对。不然OpenGL将产生无效操作,的错误信息。 } void shadeBaseUpdate(float dt) { }
OpenGL学习日记-2015.3.5——Hello glsl(着色器)
标签:c++ opengl 图形 3d游戏开发 顶点缓存对象
原文地址:http://blog.csdn.net/coderling/article/details/44128767