OpenGL学习笔记: mac下OpenGL环境搭建

Posted 计算机科学家的世界

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenGL学习笔记: 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,有兴趣的欢迎评阅。
















以上是关于OpenGL学习笔记: mac下OpenGL环境搭建的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL学习笔记: mac下OpenGL环境搭建

OpenGL学习笔记:iOS下GLES环境配置

OpenGL学习笔记:iOS下GLES环境配置

openGL 学习之路一 在mac电脑上搭建opengl环境

计算机图形学-mac系统下Xcode中OpenGL开发环境配置。

我的OpenGL学习进阶之旅OpenGL的坐标系的学习笔记