1,OpenGL是什麼
2,OpenGL能做什麼
3,OpenGL不能做什麼
4,OpenGL VS DirectX
- OpenGL只能做渲染,DirectX除了渲染以外還能做許多其它的,比如,DirectX 裏面包含d3dxmath可以用來做3D數學運算;DirectX裏面包含的外部設備接口模塊可以用來接受外部設備的輸入。
- OpenGL只是一個定義了一些接口的標準,只要實現了這些接口,那麼就算是實現了OpenGL,相反的,DirectX則只有微軟自己實現的那一份代碼,所有人都使用微軟給出的那份代碼。
- OpenGL能跨平臺,幾乎所有的平臺都支持OpenGL,從移動設備到PC產品再到主機平臺,都支持OpenGL,而DirectX則只有微軟自己的XBox和Windows支持。
- 速度方面,DirectX完爆OpenGL,對同一硬件而言,DirectX是OpenGL 渲染速度的兩倍多。
5,MAC OpenGL環境搭建
- MacPorts是MAC 平臺用來安裝第三方庫的軟件,與 Linux上的apt-install有點像,大家可以從https://www.macports.org/下載
- glfw,OpenGL的擴展程序,因爲OpenGL只是一個渲染接口,如果要在具體平臺上寫OpenGL渲染代碼,就要使用 qlfw等庫將OpenGL 和本地窗口等環境聯繫起來,安裝glfw只需要使用mac ports即可(sudo port install glfw)。
- glew,與 glfw類似,使用sudo port install glew安裝。
- XCode,MAC平臺開發唯一的神器,別告訴我在mac你還在用Eclipse寫C++。
6,Demo代碼
/*
* 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,有興趣的歡迎評閱。