/* * File: Shader.h * author: 张雄 * date: 2016_02_21 * purpose: 用于定义OpenGL shader操作的接口 */ #ifndef __ZX_NXENGINE_SHADER_H__ #define __ZX_NXENGINE_SHADER_H__ #include "../common/NXCore.h" namespace NX { class Shader{ public: /* * <函数功能> * 构造函数 * * <函数参数> * szFilePath: 要使用的shader代码的路径,需要保证的是,仅使用szFilePath,在 * 程序中即可找到shader文件。 * uShaerType: 要使用的shader 代码的shader类型,此类型有GL_VERTEX_SHADER等 * 与OpenGL中使用的值完全相同。 */ Shader(const char* szFilePath, GLenum uShaderType); virtual ~Shader(); public: /* * <函数功能> * 编译shader,shader文件和类型由构造函数给出 */ virtual std::string Compile(); public: operator GLuint(){ return m_uShaderId; } private: std::string ReadShaderSource(); private: GLuint m_uShaderId; GLenum m_uShaderType; std::string m_strShaderSourceFilePath; }; } #endif
#include <fstream> #include <string> #include "NXShader.h" #include "NXLog.h" NX::Shader::Shader(const char* szFilePath, GLenum uShaderType){ m_strShaderSourceFilePath = (szFilePath); m_uShaderId = 0; m_uShaderType = uShaderType; } NX::Shader::~Shader(){ if(m_uShaderId != 0){ glDeleteShader(m_uShaderId); m_uShaderId = 0; m_uShaderType = 0; } } std::string NX::Shader::Compile(){ std::string strErr(256 + 1, 0); m_uShaderId = glCreateShader(m_uShaderType); const std::string strShaderSrc = ReadShaderSource(); if(strShaderSrc.empty()){ sprintf((char*)strErr.c_str(), "shader file [%s] not exist or is empty", m_strShaderSourceFilePath.c_str()); glb_GetLog().log("%s", strErr.c_str()); return strErr; } const char * szShaderSrc = strShaderSrc.c_str(); glShaderSource(m_uShaderId, 1, &szShaderSrc, NULL); glCompileShader(m_uShaderId); glGetShaderInfoLog(m_uShaderId, (GLuint)strErr.length(), NULL, &(strErr[0])); glb_GetLog().log("compile shader [%s] with compile msg [%s]", m_strShaderSourceFilePath.c_str(), (strErr[0] == 0 ? "Compile succeed" : strErr.c_str())); return strErr; } std::string NX::Shader::ReadShaderSource(){ std::ifstream in(m_strShaderSourceFilePath); std::string line; std::string strShaderSrc; while(std::getline(in, line)){ line += "\r\n"; strShaderSrc += line; } in.close(); return strShaderSrc;; }
/* * File: Application.h * author: 张雄 * date: 2016_02_21 * purpose: 用于定义OpenGL应用程序接口,注意,此接口使用了glfw和glew,需要先安装此工具库, * 在mac上可使用macports安装,具体操作请google macports,Linux系统使用apt-get, * windows系统请直接下载库和头文件(自己编译估计有点坑) */ #ifndef __ZX_NXENGINE_APPLICATION_H__ #define __ZX_NXENGINE_APPLICATION_H__ #include "../common/NXcore.h" namespace NX { class Application{ public: Application(); virtual ~Application(); public: /* * <函数功能> * 使用glfw和glew库初始化OpenGL环境,注意,此函数应该是构造函数之后调用的第一个类函数, * 在调用此函数并且成功之前,不得调用其它OpenGL函数。其它类如果继承此类,则需要首先调用 * 此类的Init函数,再执行子类的Init代码。 * * <函数参数> * vCmdLine: 命令行字符串数组,与C的main函数类似 * iCmdCount: 命令行字符串个数 * iWidth: OpenGL使用的窗口的宽(以像素为单位) * iHeight: OpenGL使用的窗口的高(以像素为单位) * <返回值> * true: 初始化成功,在init之后,即可调用其它OpenGL函数 * false: 初始化失败 */ virtual bool Init(__in const char* vCmdLine[], __in const int iCmdCount, __in const int iWidth, __in const int iHeight); /* * <函数功能> * 游戏中的Tick函数。 * * <函数参数> * iTime: 时间,以秒为单位 */ virtual void Tick(const double DeltaTime); /* * <函数功能> * 游戏中的Render函数,主要完成游戏中的渲染 * */ virtual void Render(); /* * <函数功能> * 游戏中的主循环,其它类若要继承此类,最好不要重载Run函数,以免不必要的麻烦 * */ virtual void Run(); public: /* * <函数功能> * 错误处理函数 * * <函数参数> * 与glfw中错误处理回调函数的参数完全相同。 */ virtual void OnError(int error, const char* description); /* * <函数功能> * 错误处理函数 * * <函数参数> * 与glfw中键盘事件回调函数的参数意义完全相同 */ virtual void OnKeyEvent(int key, int scancode, int action, int mods); }; } #endif
#include <cstdio> #include <cstdlib> #include <ctime> #include <GL/glew.h> #include <GLFW/glfw3.h> #include "NXApplication.h" #include "../math/NXMath.h" #include "../common/NXLog.h" static NX::Application* g_pThis = NULL; static GLFWwindow* g_window = NULL; static void error_callback(int error, const char* description); static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods); NX::Application::Application(){ g_pThis = NULL; g_window = NULL; } NX::Application::~Application(){ g_pThis = NULL; g_window = NULL; } bool NX::Application::Init(const char* vCmdLine[], const int iCmdCount, const int iWidth, const int iHeight){ g_pThis = this; NX::InitNXMath(); if (!glfwInit()) { fprintf(stderr, "Failed initialize GLFW."); exit(EXIT_FAILURE); return false; } { glfwSetErrorCallback(error_callback); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 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); } { GLFWwindow* window = glfwCreateWindow(iWidth, iHeight, "OpenGL", NULL, NULL); if(!window) { std::fprintf(stderr, "Failed to create GLFW window."); glfwTerminate(); exit(EXIT_FAILURE); return false; } g_window = window; glfwMakeContextCurrent(window); glfwSetKeyCallback(window, key_callback); } glb_GetLog().logToConsole("OpenGL version supported by this platform (%s)", glGetString(GL_VERSION)); glb_GetLog().log("OpenGL version supported by this platform (%s)", glGetString(GL_VENDOR)); glewExperimental = GL_TRUE; glewInit(); return true; } void NX::Application::Tick(const double DeltaTime){ static double iTotalTime = 0; iTotalTime += DeltaTime; static char Title[32]; static int iFrameCount = 0; ++iFrameCount; if(iTotalTime >= 1.0){ sprintf(Title, "fps: %.2f", (iFrameCount / iTotalTime)); iFrameCount = 0; iTotalTime = 0; glfwSetWindowTitle(g_window, Title); } } void NX::Application::Render(){ static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f }; glClearBufferfv(GL_COLOR, 0, green); } void NX::Application::OnError(int error, const char* description){ std::fputs(description, stderr); } void NX::Application::OnKeyEvent(int key, int scancode, int action, int mods){ if(g_window == NULL){ return; } if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS){ glfwSetWindowShouldClose(g_window, GL_TRUE); } } void NX::Application::Run(){ static double PreTime = glfwGetTime(); static double NowTime = glfwGetTime(); while(!glfwWindowShouldClose(g_window)){ NowTime = glfwGetTime(); Tick(NowTime - PreTime); Render(); glFlush(); glfwSwapBuffers(g_window); glfwPollEvents(); PreTime = NowTime; } } static void error_callback(int error, const char* description) { if(!g_pThis){ return; } g_pThis->OnError(error, description); } static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if(!g_window){ return; } g_pThis->OnKeyEvent(key, scancode, action, mods); }
#ifndef __ZX_OPENGL_APPLICATION_CHAP1_1_H__ #define __ZX_OPENGL_APPLICATION_CHAP1_1_H__ #include <GL/glew.h> #include <GLFW/glfw3.h> #include "../engine/render/NXApplication.h" #include "../engine/common/NXLog.h" #include "../engine/render/NXProgram.h" class AppChap1_1: public NX::Application{ public: typedef struct vertex{ GLfloat x; GLfloat y; GLfloat r; GLfloat g; GLfloat b; GLfloat a; }vertex; public: AppChap1_1(); virtual ~AppChap1_1(); public: virtual bool Init(const char* vCmdLine[], const int iCmdCount, const int iWidth, const int iHeight); virtual void Tick(const double DeltaTime); virtual void Render(); virtual void OnKeyEvent(int key, int scancode, int action, int mods); private: GLuint m_uVBO1; GLuint m_uVBO2; GLuint m_uVAO1; GLuint m_uVAO2; GLuint m_UsedVBO; GLuint m_UsedVAO; NX::Program* m_pg; }; #endif
#include <iostream> #include "AppChap1_1.h" #include "NXShader.h" AppChap1_1::AppChap1_1(){ m_uVAO1 = 0; m_uVAO2 = 0; m_uVBO1 = 0; m_uVBO2 = 0; m_UsedVBO = 0; } AppChap1_1::~AppChap1_1(){ } bool AppChap1_1::Init(const char* vCmdLine[], const int iCmdCount, const int iWidth, const int iHeight){ if(!NX::Application::Init(vCmdLine, iCmdCount, iWidth, iHeight)){ return false; } {//vao1 glGenVertexArrays(1, &m_uVAO1); glBindVertexArray(m_uVAO1); } {//vbo1 glGenBuffers(1, &m_uVBO1); glBindBuffer(GL_ARRAY_BUFFER, m_uVBO1); vertex v[4] = { {-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f}, {-1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f}, {1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f} }; glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } {//vao2 glGenVertexArrays(1, &m_uVAO2); glBindVertexArray(m_uVAO2); } {//vbo2 glGenBuffers(1, &m_uVBO2); glBindBuffer(GL_ARRAY_BUFFER, m_uVBO2); vertex v[4] = { {-0.8f, 0.8f, 0.0f, 1.0f, 1.0f, 1.0f}, {0.8f, 0.8f, 1.0f, 0.0f, 1.0f, 1.0f}, {-0.8f, -0.8f, 1.0f, 1.0f, 0.0f, 1.0f}, {0.8f, -0.8f, 0.0f, 0.0f, 0.0f, 1.0f} }; glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } m_UsedVAO = m_uVAO1; m_UsedVBO = m_uVBO1; {//program m_pg = new NX::Program(); m_pg->AddShader("./redbook/Chap1/VS1_1.glsl", GL_VERTEX_SHADER); m_pg->AddShader("./redbook/Chap1/FS1_1.glsl", GL_FRAGMENT_SHADER); m_pg->LinkProgram(); m_pg->UseProgram(); } return true; } void AppChap1_1::Tick(const double DeltaTime){ } void AppChap1_1::Render(){ m_pg->UseProgram(); glClearColor(0.0f, 0.25f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBindVertexArray(m_UsedVAO); glBindBuffer(GL_ARRAY_BUFFER, m_UsedVBO); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (void*)0); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (void*)8); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); //glViewport(100, 100, 400, 100); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } void AppChap1_1::OnKeyEvent(int key, int scancode, int action, int mods){ Application::OnKeyEvent(key, scancode, action, mods); if(action == GLFW_PRESS){ return; } std::cout << "change buffer..." << std::endl; if(m_UsedVBO == m_uVBO1){ m_UsedVBO = m_uVBO2; }else{ m_UsedVBO = m_uVBO1; } if(m_UsedVAO == m_uVAO1){ m_UsedVAO = m_uVAO2; }else{ m_UsedVAO = m_uVAO1; } }
#include <iostream> #include <memory> #include "NXShader.h" #include "NXLog.h" #include "NXVector.h" #include "NXMatrix.h" #include "DemoHeader.h" int main(int argc, const char* argv[]){ NX::glb_GetLog().logToConsole("begin main"); std::auto_ptr<NX::Application> app(new AppChap1_1()); if(!app->Init(argv, argc, 800, 800)){ std::cout << "failed init application..." << std::endl; return 1; } NX::glb_GetLog().logToConsole("begin application"); app->Run(); NX::glb_GetLog().logToConsole("end main"); return 0; }
#version 410 core in vec4 Color; out vec4 vFinalColor; void main(void){ vFinalColor = Color; }
#version 330 core layout(location = 0) in vec2 vPosition; layout(location = 1) in vec4 vColor; out vec4 Color; void main(){ gl_Position = vec4(vPosition, -0.5f, 1.0f); Color = vColor; }
按下键盘上任何键后(ESC则会退出渲染程序),变换成如下:
在学习OpenGL的时候,我自己封装了一些基本的类,比如 Program、shader、Texture等,这些代码我放在GitHub上,其中包含我上面列出的代码,第一个程序比较简单,我也不愿意花更多时间去讲一些东西,大家只需要从Git下过来编译即可得到相同的结果,至于 OpenGL的后序学习和我编写的代码,我将会慢慢发出来,代码部分会发至Git,有兴趣的欢迎评阅。