学习自:

https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/#_7

首先放一张效果图:

 

本次教程,将着色器单独定义了一个类,方便代码阅读与编写。

1,首先新建要给shader类:shader_s.h

(1)shader_s.h

 1 #ifndef SHADER_H
 2 #define SHADER_H
 3 
 4 #include <glad/glad.h> // 包含glad来获取所有的必须OpenGL头文件
 5 
 6 #include <string>
 7 #include <fstream>
 8 #include <sstream>
 9 #include <iostream>
10 
11 
12 class Shader
13 {
14 public:
15     // 程序ID
16     unsigned int ID;
17 
18     // 构造器读取并构建着色器
19     Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
20     // 使用/激活程序
21     void use();
22     // uniform工具函数
23     void setBool(const std::string &name, bool value) const;
24     void setInt(const std::string &name, int value) const;
25     void setFloat(const std::string &name, float value) const;
26 private:
27     void checkCompileErrors(unsigned int shader, std::string type);
28 };
29 
30 #endif

(2)将头文件中的方法逐一实现

注意的是,我们类的写法,与链接中的写法有不同之处。

#include "shader_s.h"

Shader::Shader(const GLchar * vertexPath, const GLchar * fragmentPath)
{
    // 1. 从文件路径中获取顶点/片段着色器
    std::string vertexCode;
    std::string fragmentCode;
    std::ifstream vShaderFile;
    std::ifstream fShaderFile;
    // 保证ifstream对象可以抛出异常:
    vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    try
    {
        // 打开文件
        vShaderFile.open(vertexPath);
        fShaderFile.open(fragmentPath);
        std::stringstream vShaderStream, fShaderStream;
        // 读取文件的缓冲内容到数据流中
        vShaderStream << vShaderFile.rdbuf();
        fShaderStream << fShaderFile.rdbuf();
        // 关闭文件处理器
        vShaderFile.close();
        fShaderFile.close();
        // 转换数据流到string
        vertexCode = vShaderStream.str();
        fragmentCode = fShaderStream.str();
    }
    catch (std::ifstream::failure e)
    {
        std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    }
    const char* vShaderCode = vertexCode.c_str();
    const char * fShaderCode = fragmentCode.c_str();
    // 2. 编译着色器
    unsigned int vertex, fragment;
    // 顶点着色器    vs
    vertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex, 1, &vShaderCode, NULL);
    glCompileShader(vertex);
    checkCompileErrors(vertex, "VERTEX");
    // 片段着色器    fs
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment, 1, &fShaderCode, NULL);
    glCompileShader(fragment);
    checkCompileErrors(fragment, "FRAGMENT");
    // 着色器程序
    ID = glCreateProgram();
    glAttachShader(ID, vertex);
    glAttachShader(ID, fragment);
    glLinkProgram(ID);
    checkCompileErrors(ID, "PROGRAM");
    // 删除着色器,它们已经链接到我们的程序中了,已经不再需要了    glDeleteShader(vertex);
    glDeleteShader(fragment);
}

void Shader::use()
{
    glUseProgram(ID);
}

void Shader::setBool(const std::string & name, bool value) const
{
    glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}

void Shader::setInt(const std::string & name, int value) const
{
    glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}

void Shader::setFloat(const std::string & name, float value) const
{
    glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}

void Shader::checkCompileErrors(unsigned int shader, std::string type)
{
    int success;
    char infoLog[1024];
    if (type != "PROGRAM")
    {
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (!success)
        {
            glGetShaderInfoLog(shader, 1024, NULL, infoLog);
            std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
        }
    }
    else
    {
        glGetProgramiv(shader, GL_LINK_STATUS, &success);
        if (!success)
        {
            glGetProgramInfoLog(shader, 1024, NULL, infoLog);
            std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
        }
    }
}

2,主程序的使用

注意,我们的50行,在使用类的构造方法时候,传入的两个字符串表示的是我们的两个本地文件地址,这两个文件,表示的是,我们的两个shader脚本,需要我们的手动创建,并且加入代码。

两个脚本在最后给出。

 (1)主程序TestShader.cpp

有一点需要注意的是,在vs中,我们自定义的类,如果要引用,用的是双引号 “”

也就是 #include "shader_s.h" 跟链接中的也有区别

  1 #include <glad/glad.h>
  2 #include <GLFW/glfw3.h>
  3 
  4 #include "shader_s.h"
  5 
  6 #include <iostream>
  7 
  8 void framebuffer_size_callback(GLFWwindow* window, int width, int height);
  9 void processInput(GLFWwindow *window);
 10 
 11 // settings
 12 const unsigned int SCR_WIDTH = 800;
 13 const unsigned int SCR_HEIGHT = 600;
 14 
 15 int main()
 16 {
 17     // glfw: initialize and configure
 18     // ------------------------------
 19     glfwInit();
 20     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
 21     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
 22     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
 23 
 24 #ifdef __APPLE__
 25     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
 26 #endif
 27 
 28     // glfw window creation
 29     // --------------------
 30     GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
 31     if (window == NULL)
 32     {
 33         std::cout << "Failed to create GLFW window" << std::endl;
 34         glfwTerminate();
 35         return -1;
 36     }
 37     glfwMakeContextCurrent(window);
 38     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
 39 
 40     // glad: load all OpenGL function pointers
 41     // ---------------------------------------
 42     if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
 43     {
 44         std::cout << "Failed to initialize GLAD" << std::endl;
 45         return -1;
 46     }
 47 
 48     // build and compile our shader program
 49     // ------------------------------------
 50     Shader ourShader("../res/shader.vs", "../res/shader.fs"); // you can name your shader files however you like
 51 
 52     // set up vertex data (and buffer(s)) and configure vertex attributes
 53     // ------------------------------------------------------------------
 54     float vertices[] = {
 55         // 位置信息         // 颜色信息
 56          0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,  // 右下
 57         -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,  // 左下
 58          0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f   // 顶部 
 59     };
 60 
 61     unsigned int VBO, VAO;
 62     glGenVertexArrays(1, &VAO);
 63     glGenBuffers(1, &VBO);
 64     //首先绑定顶点数组对象,然后绑定并设置顶点缓冲区,然后配置顶点属性。    
 65     glBindVertexArray(VAO);
 66 
 67     glBindBuffer(GL_ARRAY_BUFFER, VBO);
 68     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
 69 
 70     // 位置属性
 71     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
 72     glEnableVertexAttribArray(0);
 73     // 颜色属性
 74     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
 75     glEnableVertexAttribArray(1);
 76     /*
 77     glVertexAttribPointer 指定了渲染时索引值为 index 的顶点属性数组的数据格式和位置。
 78     void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,const GLvoid * pointer);
 79     参数:
 80     (1)index
 81     指定要修改的顶点属性的索引值
 82     (2)size
 83     指定每个顶点属性的组件数量。必须为1、2、3或者4。初始值为4。(如position是由3个(x,y,z)组成,而颜色是4个(r,g,b,a))
 84     (4)type
 85     指定数组中每个组件的数据类型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值为GL_FLOAT。
 86     (5)normalized
 87     指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE)。
 88     (6)stride
 89     指定连续顶点属性之间的偏移量。如果为0,那么顶点属性会被理解为:它们是紧密排列在一起的。初始值为0。
 90     (7)pointer
 91     指定第一个组件在数组的第一个顶点属性中的偏移量。该数组与GL_ARRAY_BUFFER绑定,储存于缓冲区中。初始值为0;
 92     */
 93 
 94     // 循环渲染
 95     while (!glfwWindowShouldClose(window))
 96     {
 97         // input
 98         // -----
 99         processInput(window);
100 
101         // render
102         // ------
103         glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
104         glClear(GL_COLOR_BUFFER_BIT);
105 
106         // render the triangle
107         ourShader.use();
108         glBindVertexArray(VAO);
109         glDrawArrays(GL_TRIANGLES, 0, 3);
110 
111         // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
112         // -------------------------------------------------------------------------------
113         glfwSwapBuffers(window);
114         glfwPollEvents();
115     }
116 
117     // optional: de-allocate all resources once they've outlived their purpose:
118     // ------------------------------------------------------------------------
119     glDeleteVertexArrays(1, &VAO);
120     glDeleteBuffers(1, &VBO);
121 
122     // glfw: terminate, clearing all previously allocated GLFW resources.
123     // ------------------------------------------------------------------
124     glfwTerminate();
125     return 0;
126 }
127 
128 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
129 // ---------------------------------------------------------------------------------------------------------
130 void processInput(GLFWwindow *window)
131 {
132     if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
133         glfwSetWindowShouldClose(window, true);
134 }
135 
136 // glfw: whenever the window size changed (by OS or user resize) this callback function executes
137 // ---------------------------------------------------------------------------------------------
138 void framebuffer_size_callback(GLFWwindow* window, int width, int height)
139 {
140     // make sure the viewport matches the new window dimensions; note that width and 
141     // height will be significantly larger than specified on retina displays.
142     glViewport(0, 0, width, height);
143 }

(2) 脚本编写

TestShader是我的项目名,然后新建文件夹res,存放我们的脚本,然后创建两个脚本,名字自己取就好,主程序中对应好就好了。

 

a)顶点着色器(Vetex Shader)shader.vs

#version 330 core
layout (location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0 
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1

out vec3 ourColor; // 向片段着色器输出一个颜色

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor; // 将ourColor设置为我们从顶点数据那里得到的输入颜色
}

b)片段着色器(fragment Shader)shader.fs

1 #version 330 core
2 out vec4 FragColor;  
3 in vec3 ourColor;
4 
5 void main()
6 {
7     FragColor = vec4(ourColor, 1.0);
8 }

3,最后运行我们的程序,就可以得到下面的效果图: