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

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

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值