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(); 

}
 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM