1、Mac上opengl搭建、查看opengl版本、簡單的shder編程

 

嗯,小白一枚,剛入坑,遇到太多各種各樣的教程和坑了,花了兩天時間終於能把簡單的shader顯示出來了,附上教程,共勉喲

1、opengl搭建編程環境 

按照現在的大多數方法,方便快捷的一種

命令行輸入

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Homebrew安裝成功後,會自動創建目錄 /usr/local/Cellar 來存放Homebrew安裝的程序,可以在finder下點擊shift+command+G進入/usr/local/Cellar文件夾

命令行輸入

brew install glfw3

link編譯鏈接(如果已經鏈接會提示Warning),命令行輸入

brew link glfw3

這裏我解釋一下,我沒有使用freeglut、glut以及glew,就是因爲他們,讓我廢了半天勁,才跑通了第一個shader。

官網網址https://glad.dav1d.de

將語言(Language)設置爲C/C++,在API選項中,選擇3.3以上的OpenGL(gl)版本(我們的教程中將使用3.3版本,但更新的版本也能正常工作)。之後將模式(Profile)設置爲Core,並且保證生成加載器(Generate a loader)的選項是選中的。現在可以先(暫時)忽略拓展(Extensions)中的內容。都選擇完之後,點擊生成(Generate)按鈕來生成庫文件。

GLAD現在應該提供給你了一個zip壓縮文件,包含兩個頭文件目錄,和一個glad.c文件。將兩個頭文件目錄(gladKHR)複製到/usr/local/include文件夾中,或者將整個glad文件夾放到任何一個你喜歡的路徑下並添加glad.c文件到你的工程中(就是在與你的main.c在一個目錄下)

去官網下載。這裏附上一個 GitHub 的下載鏈接。是 glm 0.9.8.0 版本。下載完後解壓一下,把整個glm文件夾放在一個你喜歡的位置。

-------------------------------------------------------------------------------------------------------------------

2、查看opengl版本

對於mac,在app srore中搜索OpenGL Extensions Viewer並下載

下載後打開,就可以看到本機的opengl版本和很多信息。GLSL一般是和opengl版本對應的,比如opengl4.1,對應GLSL就是410的版本

好的,到此我們需要的環境和準備工作都好了,開始第一個工程。首先要配置一下編程環境。打開Xcode,新建一個project

選擇Command Line Tool,輸入你的項目名稱並選擇語言爲c++,根據你自己的選擇,以下都會使用c++爲示例,並選擇工程的保存位置。

然後來關鍵的配置部分

build settings中加入頭文件和庫文件的路徑,並在build phases中加入對應的庫,具體如圖(爲了篇幅,所以大家可以點擊圖片查看大圖

到這裏,配置結束,開始加載第一個shader程序啦!

-------------------------------------------------------------------------------------------------------------------

3、簡單的shder編程

一共需要這麼幾個文件

其中,glad.cpp就是我們之前下載的glad文件夾中的代碼,拷貝自己的工程中。其餘對應代碼如下:具體代碼的解釋可以看註釋掉的部分,或者參考https://github.com/wangdingqiao/noteForOpenGL/blob/master/triangle/coloredTriangle/triangle03.cpp

main.cpp


#include <glad/glad.h> //必須在第一行
#include <GLFW/glfw3.h> //不再需要GL/glew.h
#include "shader.h"
#include <iostream>
//
//void framebuffer_size_callback(GLFWwindow* window, int width, int height);
//void processInput(GLFWwindow *window);
//
//// settings
//const unsigned int SCR_WIDTH = 800;
//const unsigned int SCR_HEIGHT = 600;
//
//const char *vertexShaderSource = "#version 330 core\n"
//"layout (location = 0) in vec3 aPos;\n"
//"void main()\n"
//"{\n"
//"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
//"}\0";
//const char *fragmentShaderSource = "#version 330 core\n"
//"out vec4 FragColor;\n"
//"void main()\n"
//"{\n"
//"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
//"}\n\0";
//
//int main()
//{
//    // glfw: initialize and configure
//    // ------------------------------
//    glfwInit();
//    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
//    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
//    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//
//#ifdef __APPLE__
//    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
//#endif
//
//    // glfw window creation
//    // --------------------
//    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//    if (window == NULL)
//    {
//        std::cout << "Failed to create GLFW window" << std::endl;
//        glfwTerminate();
//        return -1;
//    }
//    glfwMakeContextCurrent(window);
//    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
//
//    // glad: load all OpenGL function pointers
//    // ---------------------------------------
//    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
//    {
//        std::cout << "Failed to initialize GLAD" << std::endl;
//        return -1;
//    }
//
//
//    // build and compile our shader program
//    // ------------------------------------
//    // vertex shader
//    int vertexShader = glCreateShader(GL_VERTEX_SHADER);
//    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
//    glCompileShader(vertexShader);
//    // check for shader compile errors
//    int success;
//    char infoLog[512];
//    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
//    if (!success)
//    {
//        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
//        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
//    }
//    // fragment shader
//    int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
//    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
//    glCompileShader(fragmentShader);
//    // check for shader compile errors
//    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
//    if (!success)
//    {
//        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
//        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
//    }
//    // link shaders
//    int shaderProgram = glCreateProgram();
//    glAttachShader(shaderProgram, vertexShader);
//    glAttachShader(shaderProgram, fragmentShader);
//    glLinkProgram(shaderProgram);
//    // check for linking errors
//    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
//    if (!success) {
//        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
//        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
//    }
//    glDeleteShader(vertexShader);
//    glDeleteShader(fragmentShader);
//
//    // set up vertex data (and buffer(s)) and configure vertex attributes
//    // ------------------------------------------------------------------
//    float vertices[] = {
//        -0.5f, -0.5f, 0.0f, // left
//        0.5f, -0.5f, 0.0f, // right
//        0.0f,  0.5f, 0.0f  // top
//    };
//
//    unsigned int VBO, VAO;
//    glGenVertexArrays(1, &VAO);
//    glGenBuffers(1, &VBO);
//    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
//    glBindVertexArray(VAO);
//
//    glBindBuffer(GL_ARRAY_BUFFER, VBO);
//    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//
//    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
//    glEnableVertexAttribArray(0);
//
//    // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
//    glBindBuffer(GL_ARRAY_BUFFER, 0);
//
//    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
//    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
//    glBindVertexArray(0);
//
//
//    // uncomment this call to draw in wireframe polygons.
//    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//
//    // render loop
//    // -----------
//    while (!glfwWindowShouldClose(window))
//    {
//        // input
//        // -----
//        processInput(window);
//
//        // render
//        // ------
//        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
//        glClear(GL_COLOR_BUFFER_BIT);
//
//        // draw our first triangle
//        glUseProgram(shaderProgram);
//        glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
//        glDrawArrays(GL_TRIANGLES, 0, 3);
//        // glBindVertexArray(0); // no need to unbind it every time
//
//        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
//        // -------------------------------------------------------------------------------
//        glfwSwapBuffers(window);
//        glfwPollEvents();
//    }
//
//    // optional: de-allocate all resources once they've outlived their purpose:
//    // ------------------------------------------------------------------------
//    glDeleteVertexArrays(1, &VAO);
//    glDeleteBuffers(1, &VBO);
//
//    // glfw: terminate, clearing all previously allocated GLFW resources.
//    // ------------------------------------------------------------------
//    glfwTerminate();
//    return 0;
//}
//
//// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
//// ---------------------------------------------------------------------------------------------------------
//void processInput(GLFWwindow *window)
//{
//    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
//        glfwSetWindowShouldClose(window, true);
//}
//
//// glfw: whenever the window size changed (by OS or user resize) this callback function executes
//// ---------------------------------------------------------------------------------------------
//void framebuffer_size_callback(GLFWwindow* window, int width, int height)
//{
//    // make sure the viewport matches the new window dimensions; note that width and
//    // height will be significantly larger than specified on retina displays.
//    glViewport(0, 0, width, height);
//}

#include <vector>
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);

const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600;

int main(int argc, char** argv)
{
    
    if (!glfwInit()) 
    {
        std::cout << "Error::GLFW could not initialize GLFW!" << std::endl;
        return -1;
    }
    
    std::cout << "Start OpenGL core profile version 3.3" << std::endl;
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    //glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif

    
    GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT,
                                          "Demo of triangle", NULL, NULL);
    if (!window)
    {
        std::cout << "Error::GLFW could not create winddow!" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    
    glfwSetKeyCallback(window, key_callback);
    
    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
    

    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
    

    GLfloat vertices[] = {
        -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
        0.5f, 0.0f, 0.0f,  0.0f, 1.0f, 0.0f,
        0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f
    };

    GLuint VAOId, VBOId;

    glGenVertexArrays(1, &VAOId);
    glBindVertexArray(VAOId);

    glGenBuffers(1, &VBOId);
    glBindBuffer(GL_ARRAY_BUFFER, VBOId);

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
                          6 * sizeof(GL_FLOAT), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
                          6 * sizeof(GL_FLOAT), (GLvoid*)(3 * sizeof(GL_FLOAT)));
    glEnableVertexAttribArray(1);
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
    

    Shader shader("/Users/****/Documents/test/light_shader/light_shader/triangle.vertex", "/Users/****/Documents/test/light_shader/light_shader/triangle.frag");
    
 
    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();
        
   
        glClearColor(0.18f, 0.04f, 0.14f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        
 
        glBindVertexArray(VAOId);
        shader.use();
        glDrawArrays(GL_TRIANGLES, 0, 3);
        
        glBindVertexArray(0);
        glUseProgram(0);
        
        glfwSwapBuffers(window); 
    }
  
    glDeleteVertexArrays(1, &VAOId);
    glDeleteBuffers(1, &VBOId);
    glfwTerminate();
    return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE);
    }
}

main中被註釋掉的部分,是不需要外部讀取shader文件的代碼實現,不過這種方法不太適合以後的修改。所以加入了shader.h文件實現第二種方法。注意修改shader的加載路徑,   

Shader shader("/Users/****Documents/test/light_shader/light_shader/triangle.vertex", "/Users/****/Documents/test/light_shader/light_shader/triangle.frag");

裏面的****需要根據自己的文件路徑進行修改

shader.h

#ifndef shader_h
#define shader_h

//#include <GL/glew.h>
#include <iterator>     // std::istreambuf_iterator
#include <string>
#include <vector>å
#include <iostream>
#include <fstream>

struct ShaderFile
{
    GLenum shaderType;
    const char* filePath;
    ShaderFile(GLenum type, const char* path)
    :shaderType(type), filePath(path){}
};

class Shader
{
public:
    Shader(const char* vertexPath, const char* fragPath) :programId(0)
    {
        std::vector<ShaderFile> fileVec;
        fileVec.push_back(ShaderFile(GL_VERTEX_SHADER, vertexPath));
        fileVec.push_back(ShaderFile(GL_FRAGMENT_SHADER, fragPath));
        loadFromFile(fileVec);
    }
    Shader(const char* vertexPath, const char* fragPath, const char* geometryPath) :programId(0)
    {
        std::vector<ShaderFile> fileVec;
        fileVec.push_back(ShaderFile(GL_VERTEX_SHADER, vertexPath));
        fileVec.push_back(ShaderFile(GL_FRAGMENT_SHADER, fragPath));
        fileVec.push_back(ShaderFile(GL_GEOMETRY_SHADER, geometryPath));
        loadFromFile(fileVec);
    }
    void use()
    {
        glUseProgram(this->programId);
    }
    ~Shader()
    {
        if (this->programId)
        {
            glDeleteProgram(this->programId);
        }
    }
public:
    GLuint programId;
private:
    /*
     * ¥”Œƒº˛º”‘ÿ∂•µ„∫Õ∆¨‘™◊≈…´∆˜
     * ¥´µ›≤Œ ˝Œ™ [(◊≈…´∆˜Œƒº˛¿‡–Õ£¨◊≈…´∆˜Œƒº˛¬∑æ∂)+]
     */
    void loadFromFile(std::vector<ShaderFile>& shaderFileVec)
    {
        std::vector<GLuint> shaderObjectIdVec;
        std::string vertexSource, fragSource;
        std::vector<std::string> sourceVec;
        size_t shaderCount = shaderFileVec.size();
        // ∂¡»°Œƒº˛‘¥¥˙¬Î
        for (size_t i = 0; i < shaderCount; ++i)
        {
            std::string shaderSource;
            if (!loadShaderSource(shaderFileVec[i].filePath, shaderSource))
            {
                std::cout << "Error::Shader could not load file:" << shaderFileVec[i].filePath << std::endl;
                return;
            }
            sourceVec.push_back(shaderSource);
        }
        bool bSuccess = true;
        // ±‡“Îshader object
        for (size_t i = 0; i < shaderCount; ++i)
        {
            GLuint shaderId = glCreateShader(shaderFileVec[i].shaderType);
            const char *c_str = sourceVec[i].c_str();
            glShaderSource(shaderId, 1, &c_str, NULL);
            glCompileShader(shaderId);
            GLint compileStatus = 0;
            glGetShaderiv(shaderId, GL_COMPILE_STATUS, &compileStatus); // ºÏ≤ȱ‡“Î◊¥Ã¨
            if (compileStatus == GL_FALSE) // ªÒ»°¥ÌŒÛ±®∏Ê
            {
                GLint maxLength = 0;
                glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &maxLength);
                std::vector<GLchar> errLog(maxLength);
                glGetShaderInfoLog(shaderId, maxLength, &maxLength, &errLog[0]);
                std::cout << "Error::Shader file [" << shaderFileVec[i].filePath << " ] compiled failed,"
                << &errLog[0] << std::endl;
                bSuccess = false;
            }
            shaderObjectIdVec.push_back(shaderId);
        }
        // ¡¥Ω”shader program
        if (bSuccess)
        {
            this->programId = glCreateProgram();
            for (size_t i = 0; i < shaderCount; ++i)
            {
                glAttachShader(this->programId, shaderObjectIdVec[i]);
            }
            glLinkProgram(this->programId);
            GLint linkStatus;
            glGetProgramiv(this->programId, GL_LINK_STATUS, &linkStatus);
            if (linkStatus == GL_FALSE)
            {
                GLint maxLength = 0;
                glGetProgramiv(this->programId, GL_INFO_LOG_LENGTH, &maxLength);
                std::vector<GLchar> errLog(maxLength);
                glGetProgramInfoLog(this->programId, maxLength, &maxLength, &errLog[0]);
                std::cout << "Error::shader link failed," << &errLog[0] << std::endl;
            }
        }
        // ¡¥Ω”ÕÍ≥…∫Ûdetach ≤¢ Õ∑≈shader object
        for (size_t i = 0; i < shaderCount; ++i)
        {
            if (this->programId != 0)
            {
                glDetachShader(this->programId, shaderObjectIdVec[i]);
            }
            glDeleteShader(shaderObjectIdVec[i]);
        }
    }
    /*
     * ∂¡»°◊≈…´∆˜≥Öڑ¥¬Î
     */
    bool loadShaderSource(const char* filePath,std::string& source)
    {
        source.clear();
        std::ifstream in_stream(filePath);
        if (!in_stream)
        {
            return false;
        }
        source.assign(std::istreambuf_iterator<char>(in_stream),
                      std::istreambuf_iterator<char>()); // Œƒº˛¡˜µ¸¥˙∆˜ππ‘Ï◊÷∑˚¥Æ
        return true;
    }
};


#endif /* shader_h */

triangle.frag

#version 330

in vec3 vertColor;
out vec4 color;

void main()
{
	color = vec4(vertColor, 1.0);
}

triangle.vertex

#version 330

layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;

out vec3 vertColor;

void main()
{
	gl_Position = vec4(position, 1.0);
	vertColor = color;
}

 好了,到此,一切準備就緒。看一下效果

-----------------------------------------------------------------------------------------

最後,附上一個教程網站,大家不懂的地方或者需要更多解釋的地方,都可去看看https://learnopengl-cn.github.io

-----------------------------------------------------------------------------------------

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