OpenGL(全写Open Graphics Library)是个定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。
OpenGL能用来开发跨平台的渲染引擎,在Android、OSX、iOS、Windows、PS等平台均可使用 OpenGL(ES)。
OpenGL不能做物理模拟,OpenGL不能做网络通信,一句话,除了渲染以外的事情,OpenGL都做不了,OpenGL只是一个3D渲染API接口标准。
OpenGL和DirectX 相比有以下几点不同:
我在MAC上使用OpenGL时,使用了如下一些工具和库:Mac Ports、glfw、glew、XCode。
在这里,我只给出一个demo代码,即使用Gourand着色画一个正方形,代码主要包含以下几部分。
/*
* 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
#include
#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
#include
#include
#include
#include
#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
#include
#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
#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
#include
#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 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;
}
我整个开发环境如下所示:(从后面给的Git上下载代码可直接打开的哦)
按下键盘上任何键后(ESC则会退出渲染程序),变换成如下:
在学习OpenGL的时候,我自己封装了一些基本的类,比如 Program、shader、Texture等,这些代码我放在GitHub上,其中包含我上面列出的代码,第一个程序比较简单,我也不愿意花更多时间去讲一些东西,大家只需要从Git下过来编译即可得到相同的结果,至于 OpenGL的后序学习和我编写的代码,我将会慢慢发出来,代码部分会发至Git,有兴趣的欢迎评阅。