OpenGL笔记<第一章> 构建 GLSL class
恭喜,我们终于很扎实地完成了第一章——glsl 入门
不幸的是,it's not the basic of GLSL shader ,我们下一节开篇,basic of GLSL shader
在下一章开篇之前,我们先来把我们第一章学到的东西封装一下,大家应该能够体会到,很多东西都是固定的模块,所以,为了进一步的盖高楼,我们把根基技术包装为一个产品,之后我们就不重复做根基的制作了。
更不幸的是,看了下第二章第一节:
漫反射环,什么光强表面辐射啊,光反射啊,线性代数吖,虽然对于学院派公式不是很头疼,但是其中的流程细节对图像的形成产生的影响还需要整几天,写完这篇就滚去研究啦。
废话不多说嘞,我们今天要把之前所学的一些模块封装成一个完整的类,以便使用,天天调用宏是不是手很累嘞,虽然这样做能把握很多细节,但是为了快速开发,还是需要封装一下子。
周一课比较多,只好大晚上才来更新,不好意思要晚一天了
///////////////////////////////////////// 更新分割线 //////////////////////////////////
我们封装一个类,依旧是需要拿一个例子来检测这个类的正确性,我们依旧选择第四节的旋转三角形来测验,其实哪个都无所谓啦,我们主要封装几个类使我们的编程更方便一点。
我们所做的也可以理解成是一个方便开发的小小型类库
构架设计
我们需要一个窗口类,使得内部自动完成一些状态的设置,比如一些常用的:开启双缓存,开启深度测试,开启RGBA模式等等,还有一些初始化窗口的操作等等一系列重复性动作,我们让他们在类中自动完成。
当我们创建一个窗口时,我们只需要传入一些窗口的特征:大小,标题,等。
它还包括渲染过程的控制,窗口的建立和消亡,外部相应等。
我们需要一个开发设计类,让我们的开发人员只需写一个类即可,写好之后,传入到另外一个接口中,系统即可自动完成。
我们需要一个底层glsl类,它用来处理着色器的编译连接使用,以及错误异常机制的处理,以及提供一些常用的底层接口,比如:
任一枚举类型的字符串数据(意思是,如果我们调用opengl API的时候,接受一个返回的枚举参数,我们需要知道它的字符串信息,我们需要一个接口去做);
还有设置uniform数据信息等。
实现
我们一直都提倡,先看效果,再讲述过程,今天所述的不是效果,是一个小小型类库设计,所以我们展示它的运作形式。
我们现在可能是个精简版,功能不是很全可能有些地方不是很合理,这里只提供作一种思想方式。
原来准备整成网盘资料分享的,后来想想,还是放在这里吧
底层glsl类
#ifndef _GLSLPROGRAM_H #define _GLSLPROGRAM_H #include <glad/glad.h> #include <stdexcept> #include <string> #include <map> #include <glm\glm.hpp> class GLSLException : public std::runtime_error { public: GLSLException(const std::string& msg) :std::runtime_error(msg) { } }; class GLSLProgram { public: GLSLProgram(); GLSLProgram(const GLSLProgram&) = delete; //forbid copying the object of class GLSLProgram& operator=(const GLSLProgram& rhs) = delete; ~GLSLProgram(); void compileShader(const GLchar* filename, GLenum type) throw(GLSLException); void compileShader(const std::string& source, GLenum type, const GLchar* filename = nullptr) throw(GLSLException); void link() /* 连接程序 */ throw(GLSLException); void use() /* 使用程序 */ throw(GLSLException); void validate() /* 功能详解见 cpp */ throw(GLSLException); GLint getHandle()const; GLboolean isLinked()const; void printActiveUniform()const; void printActiveAttribs()const; void printActiveUniformBlocks()const; const GLchar* _stringOftype(GLenum type)const; void BindAttribLoc(GLuint location, const GLchar* name); //我们只写uniform variable 设置相关的函数,ubo //关于vert Attributes,属于vbo,因不同程序而异,另做处理,或者读者自行添加 void setUniform(const GLchar* name, GLint val); void setUniform(const GLchar* name, GLuint val); void setUniform(const GLchar* name, GLfloat val); void setUniform(const GLchar* name, GLboolean val); void setUniform(const GLchar* name, const glm::vec2 & v); void setUniform(const GLchar* name, const glm::vec3 & v); void setUniform(const GLchar* name, const glm::vec4 & v); void setUniform(const GLchar* name, const glm::mat4 & m); void setUniform(const GLchar* name, const glm::mat3 & m); void setUniform(const GLchar* name, GLfloat x, GLfloat y, GLfloat z); private: GLint _handle; GLboolean _linked; std::map<std::string, GLint> _uniformLocations; void _initLocation(); GLint _getUniformLoc(const GLchar* name); }; #endif //_GLSLPROGRAM_H
貌似没什么说的。
就是之前几节的整合,之前都讲过
#include "glslProgram.h" #include <fstream> #include <sstream> #include <iostream> // 因为其他开发者包含文件的时候值包含头文件, // 所以我们可以大胆地cpp里面引用所有的namespace using namespace std; using namespace glm; GLSLProgram::GLSLProgram() :_handle(0) ,_linked(GL_FALSE) { } GLSLProgram::~GLSLProgram() { if (!_handle)return; //查询有多少个shader连接在program上 GLint numShaders{ 0 }; glGetProgramiv(_handle, GL_ATTACHED_SHADERS, &numShaders); //获取所有shader的名字,然后从程序中删除 GLuint* shaderNames = new GLuint[numShaders]; glGetAttachedShaders(_handle, numShaders, nullptr, shaderNames); for (int i{ 0 }; i < numShaders; ++i) glDeleteShader(shaderNames[i]); //删除shader program glDeleteProgram(_handle); delete[] shaderNames; } void GLSLProgram::compileShader(const GLchar* filename, GLenum type) { ifstream infile(filename); if (!infile.is_open()) { string msg = string("Shader file: ") + filename + " is failed to open"; throw GLSLException(msg); } if (_handle <= 0) { _handle = glCreateProgram(); if (_handle <= 0) throw GLSLException("Unable to create a shader program."); } //获取内容 stringstream shadercode; shadercode << infile.rdbuf(); infile.close(); compileShader(shadercode.str(), type, filename); } void GLSLProgram::compileShader(const string& source, GLenum type, const GLchar* filename) { if (_handle <= 0) { _handle = glCreateProgram(); if (_handle <= 0) throw GLSLException("Unable to create a shader program."); } GLint shaderHandle = glCreateShader(type); if (0 == shaderHandle) //Check the state of Creating Shader { string msg = string(_stringOftype(type)) + " Shader program is failed to create."; throw GLSLException("msg"); } const GLchar* shaderSource = source.c_str(); const GLchar* codeArray[]{ shaderSource }; glShaderSource(shaderHandle, 1, codeArray, nullptr); glCompileShader(shaderHandle); //Check the state of Compiling shader GLint result; glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &result); if (GL_FALSE == result) { string msg = string{ _stringOftype(type) } +"shader compilation failed.\n"; GLint logLenth; glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLenth); if (logLenth > 0) { GLchar* c_log = new GLchar[logLenth]; GLsizei written{ 0 }; glGetShaderInfoLog(shaderHandle, logLenth, &written, c_log); msg += c_log; delete[] c_log; } throw GLSLException(msg); } else glAttachShader(_handle, shaderHandle); } void GLSLProgram::link() { if (_linked)return; if (_handle <= 0) throw GLSLException("program hasn't been compiled."); glLinkProgram(_handle); GLint status{ 0 }; glGetProgramiv(_handle, GL_LINK_STATUS, &status); if (GL_FALSE == status) { string msg{ "failed to link shader program.\n" }; GLint logLength; glGetProgramiv(_handle, GL_INFO_LOG_LENGTH, &logLength); if (logLength > 0) { GLchar* c_log = new GLchar[logLength]; GLsizei written; glGetShaderInfoLog(_handle, logLength, &written, c_log); msg += c_log; delete[] c_log; } throw GLSLException(msg); } else { _linked = GL_TRUE; //因为当着色器程序连接完成之后,各个量的location值就确定了,我们顺便做一个映射 _initLocation(); } } void GLSLProgram::use() { if (_handle <= 0 || (!_linked)) throw GLSLException("Shader has not been linked."); glUseProgram(_handle); } GLint GLSLProgram::getHandle()const { return _handle; } GLboolean GLSLProgram::isLinked()const { return _linked; } void GLSLProgram::_initLocation() { _uniformLocations.clear(); GLint numUniforms{ 0 }; glGetProgramInterfaceiv(_handle, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numUniforms); GLenum properties[] = { GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_BLOCK_INDEX }; for (GLint i = 0; i < numUniforms; ++i) { GLint results[4]; glGetProgramResourceiv(_handle, GL_UNIFORM, i, 4, properties, 4, nullptr, results); if (results[3] != -1) continue; // Skip uniforms in blocks GLint bufSize = results[0] + 1; GLchar * name = new GLchar[bufSize]; glGetProgramResourceName(_handle, GL_UNIFORM, i, bufSize, nullptr, name); _uniformLocations[name] = results[2]; delete[] name; } } GLint GLSLProgram::_getUniformLoc(const GLchar* name) { auto pos = _uniformLocations.find(name); if (pos == _uniformLocations.end()) { _uniformLocations[name] = glGetUniformLocation(_handle, name); } return _uniformLocations[name]; } void GLSLProgram::printActiveUniform()const { GLint numUniforms{ 0 }; glGetProgramInterfaceiv(_handle, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numUniforms); GLenum properties[] = { GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_BLOCK_INDEX }; cout << "Active uniforms:" << endl; for (int i = 0; i < numUniforms; ++i) { GLint results[4]; glGetProgramResourceiv(_handle, GL_UNIFORM, i, 4, properties, 4, nullptr, results); if (results[3] != -1) continue; // Skip uniforms in blocks GLint bufSize = results[0] + 1; char * name = new char[bufSize]; glGetProgramResourceName(_handle, GL_UNIFORM, i, bufSize, nullptr, name); cout << "Location: " << results[2] << " " << name << " (" << _stringOftype(results[1]) << ") " << endl; delete[] name; } } void GLSLProgram::printActiveAttribs()const { GLint numAttribs; glGetProgramInterfaceiv(_handle, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &numAttribs); GLenum properties[] = { GL_NAME_LENGTH, GL_TYPE, GL_LOCATION }; cout << "Active attributes:" << endl; for (int i = 0; i < numAttribs; ++i) { GLint results[3]; glGetProgramResourceiv(_handle, GL_PROGRAM_INPUT, i, 3, properties, 3, nullptr, results); GLint bufSize = results[0] + 1; char * name = new char[bufSize]; glGetProgramResourceName(_handle, GL_PROGRAM_INPUT, i, bufSize, nullptr, name); cout << "Location: " << results[2] << " " << name << " (" << _stringOftype(results[1]) << ") " << endl; delete[] name; } } void GLSLProgram::printActiveUniformBlocks()const { GLint numBlocks{ 0 }; glGetProgramInterfaceiv(_handle, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &numBlocks); GLenum blockProps[] = { GL_NUM_ACTIVE_VARIABLES, GL_NAME_LENGTH }; GLenum blockIndex[] = { GL_ACTIVE_VARIABLES }; GLenum props[] = { GL_NAME_LENGTH, GL_TYPE, GL_BLOCK_INDEX }; for (int block = 0; block < numBlocks; ++block) { GLint blockInfo[2]; glGetProgramResourceiv(_handle, GL_UNIFORM_BLOCK, block, 2, blockProps, 2, nullptr, blockInfo); GLint numUnis = blockInfo[0]; GLchar * blockName = new GLchar[blockInfo[1] + 1]; glGetProgramResourceName(_handle, GL_UNIFORM_BLOCK, block, blockInfo[1] + 1, nullptr, blockName); cout << R"+(Uniform block ")+" << blockName << R"+(" : )+" << endl; delete[] blockName; GLint * unifIndexes = new GLint[numUnis]; glGetProgramResourceiv(_handle, GL_UNIFORM_BLOCK, block, 1, blockIndex, numUnis, nullptr, unifIndexes); for (int unif = 0; unif < numUnis; ++unif) { GLint uniIndex = unifIndexes[unif]; GLint results[3]; glGetProgramResourceiv(_handle, GL_UNIFORM, uniIndex, 3, props, 3, nullptr, results); GLint bufSize = results[0] + 1; GLchar * name = new GLchar[bufSize]; glGetProgramResourceName(_handle, GL_UNIFORM, uniIndex, bufSize, nullptr, name); cout << " " << name << "(" << _stringOftype(results[1]) << ")" << endl; delete[] name; } delete[] unifIndexes; } } void GLSLProgram::BindAttribLoc(GLuint location, const GLchar* name) { glBindAttribLocation(_handle, location, name); } const GLchar* GLSLProgram::_stringOftype(GLenum type)const { switch (type) { //shader enum case GL_VERTEX_SHADER: return "vertex"; case GL_FRAGMENT_SHADER: return "fragment"; case GL_GEOMETRY_SHADER: return "geometry"; case GL_TESS_CONTROL_SHADER: return "tess_control"; case GL_TESS_EVALUATION_SHADER: return "tess_evaluation"; case GL_COMPUTE_SHADER: return "compute"; //data enum case GL_FLOAT: return "float"; case GL_FLOAT_VEC2: return "fvec2"; case GL_FLOAT_VEC3: return "fvec3"; case GL_FLOAT_VEC4: return "fvec4"; case GL_FLOAT_MAT2: return "fmat2"; case GL_FLOAT_MAT2x3: return "fmat2*3"; case GL_FLOAT_MAT2x4: return "fmat2*4"; case GL_FLOAT_MAT3: return "fmat3"; case GL_FLOAT_MAT3x2: return "fmat3*2"; case GL_FLOAT_MAT3x4: return "fmat3*4"; case GL_FLOAT_MAT4: return "fmat4"; case GL_FLOAT_MAT4x2: return "fmat4*2"; case GL_FLOAT_MAT4x3: return "fmat4*3"; case GL_DOUBLE: return "double"; case GL_DOUBLE_VEC2: return "lfvec2"; case GL_DOUBLE_VEC3: return "lfvec3"; case GL_DOUBLE_VEC4: return "lfvec4"; case GL_DOUBLE_MAT2: return "lfmat2"; case GL_DOUBLE_MAT2x3: return "lfmat2*3"; case GL_DOUBLE_MAT2x4: return "lfmat2*4"; case GL_DOUBLE_MAT3: return "lfmat3"; case GL_DOUBLE_MAT3x2: return "lfmat3*2"; case GL_DOUBLE_MAT3x4: return "lfmat3*4"; case GL_DOUBLE_MAT4: return "lfmat4"; case GL_DOUBLE_MAT4x2: return "lfmat4*2"; case GL_DOUBLE_MAT4x3: return "lfmat4*3"; case GL_INT: return "int"; case GL_INT_VEC2: return "int_vec2"; case GL_INT_VEC3: return "int_vec3"; case GL_INT_VEC4: return "int_vec4"; case GL_UNSIGNED_BYTE: return "unsignedByte"; case GL_UNSIGNED_INT: return "unsignedInt"; case GL_BOOL: return "bool"; default: return "?"; } } void GLSLProgram::validate() /* 验证程序 ** 首先,它会检查程序是否可执行 ** 此函数一般用于程序开发阶段,验证过程产生的信息会被存储在program日志中 ** 当渲染程序发布后,并且当可编程着色器是当前状态的一部分时,OpenGL实现必须进行的验证操作。 ** 当当前程序对象中的任何两个活动样品是不同类型但是指向相同纹理图像单元时, ** 函数glDrawArrays和glDrawElements会产生GL_INVALID_OPERATION错误。 ** 当渲染程序发布后,程序很难捕获这些错误,即使能,也会大大降低效率。 ** 所以,我们建议程序在开发阶段调用glValidateProgram函数检测这些问题。 */ { if (!_linked) throw GLSLException("Program isn't linked."); GLint status; glValidateProgram(_handle); glGetProgramiv(_handle, GL_VALIDATE_STATUS, &status); if (GL_FALSE == status) { // Store log and return false GLint length{ 0 }; string msg{ "Program failed to validate\n" }; glGetProgramiv(_handle, GL_INFO_LOG_LENGTH, &length); if (length > 0) { GLchar *c_log = new GLchar[length]; GLint written{ 0 }; glGetProgramInfoLog(_handle, length, &written, c_log); msg += c_log; delete[] c_log; } throw GLSLException(msg); } } void GLSLProgram::setUniform(const GLchar* name, GLint val) { glUniform1i(_getUniformLoc(name), val); } void GLSLProgram::setUniform(const GLchar* name, GLuint val) { glUniform1ui(_getUniformLoc(name), val); } void GLSLProgram::setUniform(const GLchar* name, GLfloat val) { glUniform1f(_getUniformLoc(name), val); } void GLSLProgram::setUniform(const GLchar* name, GLboolean val) { setUniform(name, (GLuint)val); } void GLSLProgram::setUniform(const GLchar* name, const glm::vec2 & v) { glUniform2f(_getUniformLoc(name), v.x, v.y); } void GLSLProgram::setUniform(const GLchar* name, const glm::vec3 & v) { glUniform3f(_getUniformLoc(name), v.x, v.y, v.z); } void GLSLProgram::setUniform(const GLchar* name, const glm::vec4 & v) { glUniform4f(_getUniformLoc(name), v.x, v.y, v.z, v.w); } void GLSLProgram::setUniform(const GLchar* name, const glm::mat4 & m) { glUniformMatrix4fv(_getUniformLoc(name), 1, GL_FALSE, &m[0][0]); } void GLSLProgram::setUniform(const GLchar* name, const glm::mat3 & m) { glUniformMatrix3fv(_getUniformLoc(name), 1, GL_FALSE, &m[0][0]); } void GLSLProgram::setUniform(const GLchar* name, GLfloat x, GLfloat y, GLfloat z) { setUniform(name, vec3{ x,y,z }); }
窗口
#ifndef _RUNNER_ #define _RUNNER_ #include "uniformExample.h" #include <GLFW\glfw3.h> #include <map> #include <string> #define widthWIN 1024 #define heightWIN 768 class WinRunner { public: WinRunner(const std::string& windowTitle, const int width = widthWIN, const int height = heightWIN) { //Initialize the GLFW if (!glfwInit()) exit(EXIT_FAILURE); // set the version of OpenGL to 4.3 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //一些窗口的属性 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); //可改变大小 glfwWindowHint(GLFW_DECORATED, GL_TRUE); //有标题栏和边框 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); //..还有很多属性,可按需求添加 //创建窗口 _window = glfwCreateWindow(widthWIN, heightWIN, windowTitle.c_str(), NULL, NULL); if (!_window) { glfwTerminate(); //终止窗口程序,除去前缀单词可查 exit(EXIT_FAILURE); } glfwMakeContextCurrent(_window); //将当前窗口设置为当前上下文(渲染环境) glfwGetFramebufferSize(_window, &_win_width, &_win_height); if (!gladLoadGL()) { exit(-1); } //加载glad glClearColor(0.5f, 0.5f, 0.5f, 1.0f); //Debug信息 glDebugMessageCallback(debugCallback, NULL); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, GL_DEBUG_SEVERITY_NOTIFICATION, -1, "Start debugging"); } int run(uniform_Example& win) { win.init(); win.resize(_win_width, _win_height); mainLoop(win); glfwTerminate(); return EXIT_SUCCESS; } private: GLFWwindow* _window; GLint _win_width, _win_height; void mainLoop(uniform_Example & win) { while (!glfwWindowShouldClose(_window) && !glfwGetKey(_window, GLFW_KEY_ESCAPE)) { win.update(GLfloat(glfwGetTime())); win.render(); glfwSwapBuffers(_window);// glfw下的双缓存转换 glfwPollEvents(); // Waits until events are queued and processes them } } static void APIENTRY debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * msg, const void * param) { std::string sourceStr; switch (source) { case GL_DEBUG_SOURCE_WINDOW_SYSTEM: sourceStr = "WindowSys"; break; case GL_DEBUG_SOURCE_APPLICATION: sourceStr = "App"; break; case GL_DEBUG_SOURCE_API: sourceStr = "OpenGL"; break; case GL_DEBUG_SOURCE_SHADER_COMPILER: sourceStr = "ShaderCompiler"; break; case GL_DEBUG_SOURCE_THIRD_PARTY: sourceStr = "3rdParty"; break; case GL_DEBUG_SOURCE_OTHER: sourceStr = "Other"; break; default: sourceStr = "Unknown"; } std::string typeStr; switch (type) { case GL_DEBUG_TYPE_ERROR: typeStr = "Error"; break; case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: typeStr = "Deprecated"; break; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: typeStr = "Undefined"; break; case GL_DEBUG_TYPE_PORTABILITY: typeStr = "Portability"; break; case GL_DEBUG_TYPE_PERFORMANCE: typeStr = "Performance"; break; case GL_DEBUG_TYPE_MARKER: typeStr = "Marker"; break; case GL_DEBUG_TYPE_PUSH_GROUP: typeStr = "PushGrp"; break; case GL_DEBUG_TYPE_POP_GROUP: typeStr = "PopGrp"; break; case GL_DEBUG_TYPE_OTHER: typeStr = "Other"; break; default: typeStr = "Unknown"; } std::string sevStr; switch (severity) { case GL_DEBUG_SEVERITY_HIGH: sevStr = "HIGH"; break; case GL_DEBUG_SEVERITY_MEDIUM: sevStr = "MED"; break; case GL_DEBUG_SEVERITY_LOW: sevStr = "LOW"; break; case GL_DEBUG_SEVERITY_NOTIFICATION: sevStr = "NOTIFY"; break; default: sevStr = "UNK"; } printf("%s:%s[%s](%d): %s\n", sourceStr.c_str(), typeStr.c_str(), sevStr.c_str(), id, msg); } }; #endif
emmm,GLUT库已经很久不更新维护了,会出现很多不便,我们用最新的GLFW库,而且GLFW也有很多学习资源,是目前很流行的一个库,所以,我们也与时俱进。
相关配置:https://blog.csdn.net/qq_37338983/article/details/78997179
再也不需要我们最开始那一节讲的那么麻烦了,也再也不用在程序最前面加一大段配置代码了,照着上面流程走一遍就都可以了,很方便,也不会出现GLUT库使用的时候一些库文件包含问题,最主要的是GLFW中关于窗口的动作和渲染过程都是可见可控的,显而易见,GLFW并没有GLUT中的loop循环回调函数,而是自己写的while循环,关于两者的优劣,在此处不做争辩。
而且GLFW库中所有的函数不仅提示参数而且还会提示函数功能简述
所以,还是很好的,很方便的。
相关的解释也都有,关于函数的作用,把前缀抹去,单词是可查的。
设计开发类
#ifndef _UNIFORM_EXAMPLE_H #define _UNIFORM_EXAMPLE_H #include "glslProgram.h" class uniform_Example { public: uniform_Example(); void compileLink(const GLchar** filenamePtr, const GLint arraySize, const GLenum* typePtr); /*@brief 用于编译连接使用着色器 @param 参数采用类似于OpenGL的参数机制,第一个传入含有所有要编译的着色器文件的名字的数组地址 @param 第二个参数为param1所指数组的大小 @param 第三个参数为每个文件代表的着色器类型 */ void init(); void update(const GLfloat t); void render(); void resize(const GLfloat w, const GLfloat h); private: GLuint vaoHandle; GLSLProgram pro; GLfloat angle; GLfloat win_width; GLfloat win_height; glm::mat4 rotationMatrix; }; #endif //_UNIFORM_EXAMPLE_H
这就是第四节中我们讲的那个旋转三角形,只不过用类的形式写了一下
main函数
#include "WinRunner.h" int main(int argc, char** argv) { WinRunner win{ "rotated triangle",1024,768 }; uniform_Example exam; const GLchar* filenames[]{ "basic.vert" ,"basic.frag" }; GLenum types[]{ GL_VERTEX_SHADER ,GL_FRAGMENT_SHADER }; exam.compileLink(&filenames[0], 2, types); win.run(exam); }
关于编译连接函数的参数设计在glslprogram中有介绍,按照opengl 宏函数参数设计习惯写的。
End
上述就是我们的小型类库设计。
当然,会有一些设计上面的不足,但目前看来还可以。
改进
按照面向对象的程序设计方法学,类的设计是为了各个模块开发的独立性,但,貌似我们的类有着莫大的联系。
我们的WinRunner中需要有程序设计开发人员的类对象,这就使得,窗口类需要依赖程序设计人员的类,而程序设计人员设计完成之后还要去改动窗口类。
当然,这是可以规避的,不然,C++岂不是白学了!!
只是时间关系,没有整。
我们可以把程序设计开发类抽象出一个基类,程序开发设计的类继承之,将常用的接口抽象出去。
而在WinRunner中只需要有基类的对象即可,和开发设计类之间的交互接口参数为基类的引用即可,它可以接受任意开发设计派生出的类
就此,WinRunner完全独立于程序开发设计类了,当然我们上面那个只是为了演示这个类系统,而并没有做抽象,而是直接在WinRunner的数据成员中创建了一个开发设计类的对象,貌似比较糟糕,很不独立,哈哈,你们改了就可以了。
到这里,我们就真的,把第一章画上了一个完美的句号。
感谢您的支持,生活愉快~