• LearnOpenGL学习笔记(四)——着色器类编写


    之前我们将着色器的代码用glsl写好之后,保存为字符串指针,然后用一个函数去编译它,这是一种手段,对于简单的着色器代码可以这样。但当我们针对复杂的着色器,我们发现编写、编译、管理着色器是一件麻烦事。我们用一个类将着色器的所有编译,链接,管理都放在一个文件里。再将着色器源码单独设置成.glsl文件用来,从文件流读取,不再放到c++编译器里了。这样主函数就比较简洁了。 我们建立一个类shader,将一切着色器的步骤都在这个类里封装了,这样我们在主函数实例化它,我们就直接可以使用着色器不用在意内部的具体情况(从文件流读取,编译,链接)。

    因为我们是在.h文件里面实现的这些步骤,包括函数的具体实现我们都放到.h文件里了,所以我们还需要一些特殊处理
    
    #ifndef SHADER_H  //先测试x是否被宏定义过
    #define SHADER_H    //如果没有宏定义下面就宏定义x并编译下面的语句
    #include <glad/glad.h>; // 包含glad来获取所有的必须OpenGL头文件
    #include <string> 
    #include <fstream> //file stream  ,fstream是C++ STL中对文件操作的合集,包含了常用的所有文件操作。
    #include <sstream> //字符串流,可以支持C风格的串流的输入输出操作
    #include <iostream> 
    
    #endif    //如果已经定义了则编译#endif后面的语句
    

    然后我们可以声明这个类的结构了:

    class Shader { 
    public: 
         // 程序ID 
         unsigned int ID; 
    
         // 构造器读取并构建着色器 
         Shader(const GLchar* vertexPath, const GLchar* fragmentPath); 
         // 使用/激活程序 
         void use(); 
         // uniform工具函数 
         void setBool(const std::string &name, bool value) const; 
         void setInt(const std::string &name, int value) const; 
         void setFloat(const std::string &name, float value) const; 
    };

    看着比较简单,接下来我们对函数的具体实现,分析,首先是最重要的构造函数,这一个函数里面,将一切编译链接都做完了。构造函数需要传入两个参数的文件地址(针对vs的.sln文件目录来说,我们把文件放到那里,就只需要一个名字就可以了),不用管第三个,那是一个几何地址,设为NULL。

    Shader(const char* vertexPath, const char* fragmentPath, const char* geometryPath = nullptr)
    { // 1. 从文件路径中获取顶点/片段着色器
    //声明一些对象,ifstream 的意思是 从硬盘到内存的文件流对象,这个用来存储我们的着色器源码,并且对其进行处理
    std::string vertexCode;
    std::string fragmentCode;
    std::string geometryCode;
    std::ifstream vShaderFile;
    std::ifstream fShaderFile;
    std::ifstream gShaderFile;
    
    //保证ifstream对象可以抛出异常,防止错误读取,出现故障,即使停止。
    vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    gShaderFile.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
    stringvertexCode = vShaderStream.str();
    fragmentCode = fShaderStream.str();
    
    
    // if geometry shader path is present, also load a geometry shader//这一段不用管。
    if (geometryPath != nullptr)
    {
    gShaderFile.open(geometryPath);
    std::stringstream gShaderStream;
    gShaderStream << gShaderFile.rdbuf();
    gShaderFile.close();
    geometryCode = gShaderStream.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. compile shaders
    unsigned int vertex, fragment;
    // vertex shader
    vertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex, 1, &vShaderCode, NULL);
    glCompileShader(vertex);
    checkCompileErrors(vertex, "VERTEX");
    // fragment Shader
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment, 1, &fShaderCode, NULL);
    glCompileShader(fragment);
    checkCompileErrors(fragment, "FRAGMENT");
    // if geometry shader is given, compile geometry shader
    unsigned int geometry;
    if (geometryPath != nullptr)
    {
    const char * gShaderCode = geometryCode.c_str();
    geometry = glCreateShader(GL_GEOMETRY_SHADER);
    glShaderSource(geometry, 1, &gShaderCode, NULL);
    glCompileShader(geometry);
    checkCompileErrors(geometry, "GEOMETRY");
    }
    // shader Program
    ID = glCreateProgram();
    glAttachShader(ID, vertex);
    glAttachShader(ID, fragment);
    if (geometryPath != nullptr)
    glAttachShader(ID, geometry);
    glLinkProgram(ID);
    checkCompileErrors(ID, "PROGRAM");
    // delete the shaders as they're linked into our program now and no longer necessery
    glDeleteShader(vertex);
    glDeleteShader(fragment);
    
    //不管这个
    if (geometryPath != nullptr)
    glDeleteShader(geometry);
    
    }
    

    好了,现在我们已经成功创造出来了这个类的构造函数,接下来我们设计一下几个公有函数就好了,它们都很简单。 激活程序:

    void use() { 
    glUseProgram(ID); 
    }

    // uniform工具函数:

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

    void setFloat(const std::string &name, float value) const { glUniform1f(glGetUniformLocation(ID, name.c_str()), value); } 好了,这样我们就解决了所有的问题,它让我们的使用变得非常简单易。  

    Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs"); 
    
    ... while(...) { 
    
    ourShader.use(); 
    
    ourShader.setFloat("someUniform", 1.0f); DrawStuff(); 
    
    }
    
    
  • 相关阅读:
    迁移式学习
    VMware Workstation 16激活码
    OpenStack安装部署
    git码云操作
    vs 2019 正则替换
    linux中Redis单机安装
    ASP.NET/C#执行数据库过程函数带RETURN的项目接收。
    IDEA配置部属Tomcat
    Java集合之HashMap源码分析(put()方法)
    反编译一款APP然后重新打包(Windows环境)
  • 原文地址:https://www.cnblogs.com/zobol/p/10717532.html
Copyright © 2020-2023  润新知