OpenGL學習筆記: (1)mac下OpenGL環境搭建

1,OpenGL是什麼

       OpenGL(全寫Open Graphics Library)是個定義了一個跨編程語言、跨平臺的編程接口規格的專業的圖形程序接口。它用於三維圖像(二維的亦可),是一個功能強大,調用方便的底層圖形庫。

2,OpenGL能做什麼

        OpenGL能用來開發跨平臺的渲染引擎,在Android、OSX、iOS、Windows、PS等平臺均可使用 OpenGL(ES)。

3,OpenGL不能做什麼

        OpenGL不能做物理模擬,OpenGL不能做網絡通信,一句話,除了渲染以外的事情,OpenGL都做不了,OpenGL只是一個3D渲染API接口標準。

4,OpenGL VS DirectX

        OpenGL和DirectX 相比有以下幾點不同:
  1. OpenGL只能做渲染,DirectX除了渲染以外還能做許多其它的,比如,DirectX 裏面包含d3dxmath可以用來做3D數學運算;DirectX裏面包含的外部設備接口模塊可以用來接受外部設備的輸入。
  2. OpenGL只是一個定義了一些接口的標準,只要實現了這些接口,那麼就算是實現了OpenGL,相反的,DirectX則只有微軟自己實現的那一份代碼,所有人都使用微軟給出的那份代碼。
  3. OpenGL能跨平臺,幾乎所有的平臺都支持OpenGL,從移動設備到PC產品再到主機平臺,都支持OpenGL,而DirectX則只有微軟自己的XBox和Windows支持。
  4. 速度方面,DirectX完爆OpenGL,對同一硬件而言,DirectX是OpenGL 渲染速度的兩倍多。

5,MAC OpenGL環境搭建

我在MAC上使用OpenGL時,使用瞭如下一些工具和庫:Mac Ports、glfw、glew、XCode。
  1. MacPorts是MAC 平臺用來安裝第三方庫的軟件,與 Linux上的apt-install有點像,大家可以從https://www.macports.org/下載
  2. glfw,OpenGL的擴展程序,因爲OpenGL只是一個渲染接口,如果要在具體平臺上寫OpenGL渲染代碼,就要使用 qlfw等庫將OpenGL 和本地窗口等環境聯繫起來,安裝glfw只需要使用mac ports即可(sudo port install glfw)。
  3. glew,與 glfw類似,使用sudo port install glew安裝。
  4. XCode,MAC平臺開發唯一的神器,別告訴我在mac你還在用Eclipse寫C++。

6,Demo代碼

        在這裏,我只給出一個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 <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;
}


我整個開發環境如下所示:(從後面給的Git上下載代碼可直接打開的哦)


程序的結果如下:

                  


按下鍵盤上任何鍵後(ESC則會退出渲染程序),變換成如下:

                 


        在學習OpenGL的時候,我自己封裝了一些基本的類,比如 Program、shader、Texture等,這些代碼我放在GitHub上,其中包含我上面列出的代碼,第一個程序比較簡單,我也不願意花更多時間去講一些東西,大家只需要從Git下過來編譯即可得到相同的結果,至於 OpenGL的後序學習和我編寫的代碼,我將會慢慢發出來,代碼部分會發至Git,有興趣的歡迎評閱。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章