自學(xué)OpenGL(七)-著色器

前言

著色器是運行在GPU上的程序俩檬,為圖形渲染管線特定部分而運行萎胰,從某種意義上來說,著色器是把輸入轉(zhuǎn)化為輸出的程序棚辽。著色器程序是完全獨立的程序技竟,著色器之間不能直接通信,只能通過輸入輸出實現(xiàn)通信屈藐。在前面的幾篇文章中榔组,粗略地介紹了一下著色器,接下來我將詳細的介紹著色器估盘。

GLSL

著色器是使用一種叫GLSL的類C語言寫成的瓷患。GLSL是為圖形計算量身定制的,它包含一些針對向量和矩陣操作的有用特性遣妥。用法與特性可學(xué)習(xí) GLSL 中文手冊

封裝我們自己的著色器類

通過我們幾篇博客了解到著色器需要編寫擅编、編譯、管理著色器箫踩,這是件麻煩事爱态,在著色器主題的最后,我們會寫一個類來讓我們的生活輕松一點境钟,它可以從硬盤讀取著色器锦担,然后編譯并鏈接它們,并對它們進行錯誤檢測慨削,這就變得很好用了洞渔。這也會讓你了解該如何封裝目前所學(xué)的知識到一個抽象對象中。

我們會把著色器類全部放在在頭文件里缚态,主要是為了學(xué)習(xí)用途磁椒,當然也方便移植。類結(jié)構(gòu)設(shè)計如下:

  1. 聲明一個抽象類 ShaderStreamInterface玫芦,該抽象類代表一個著色器程序的 GLSL 源代碼浆熔。
#define _SHADER_SRC(...) #__VA_ARGS__
#define SHADER_SRC(...) _SHADER_SRC(__VA_ARGS__)

class ShaderStreamInterface {
public:
    virtual char *getVertexCode() = 0;
    
    virtual char *getFragmentCode() = 0;
protected:
    char *vertextCode;
    char *fragmentCode;
};
  1. 聲明一個著色器類 Shader,勇于編譯桥帆、鏈接医增、管理著色器慎皱。

在Shader.h 文件中代碼如下

#define GLEW_STATIC
#include <GL/glew.h>
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include "ShaderStreamInterface.h"
/** 著色器類 */
class Shader {
    
public:
    // 程序ID
    unsigned int ID;
    // 構(gòu)造器讀取并構(gòu)建著色器
    Shader(ShaderStreamInterface *shaderStream);
    // 使用/激活程序
    Shader * use();
    // uniform工具函數(shù)
    Shader * setBool(const std::string &name, bool value);
    Shader * setInt(const std::string &name, int value);
    Shader * setFloat(const std::string &name, float value);
    Shader * setColor(const std::string &name, float red, float green, float blue, float alpha);
};

在Shader.cpp 文件中的代碼如下:


Shader:: Shader(ShaderStreamInterface *shaderStream) {
    // 1. 從文件路徑中獲取頂點/片段著色器
    const char* vShaderCode = shaderStream->getVertexCode();
    const char* fShaderCode = shaderStream->getFragmentCode();
    
    // 2. 編譯著色器
    unsigned int vertex, fragment;
    int success;
    char infoLog[512];

    // 頂點著色器
    vertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex, 1, &vShaderCode, NULL);
    glCompileShader(vertex);
    // 打印編譯錯誤(如果有的話)
    glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
    if(!success) {
        glGetShaderInfoLog(vertex, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    };

    // 片段著色器也類似
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment, 1, &fShaderCode, NULL);
    glCompileShader(fragment);
    
    // 著色器程序
    ID = glCreateProgram();
    glAttachShader(ID, vertex);
    glAttachShader(ID, fragment);
    glLinkProgram(ID);
    // 打印連接錯誤(如果有的話)
    glGetProgramiv(ID, GL_LINK_STATUS, &success);
    if(!success) {
        glGetProgramInfoLog(ID, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }

    // 刪除著色器,它們已經(jīng)鏈接到我們的程序中了叶骨,已經(jīng)不再需要了
    glDeleteShader(vertex);
    glDeleteShader(fragment);
}

Shader * Shader:: use() {
    glUseProgram(ID);
    return this;
}

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

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

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

Shader * Shader:: setColor(const std::string &name, float red, float green, float blue, float alpha) {
    glUniform4f(glGetUniformLocation(ID, name.c_str()), red, green, blue, alpha);
    return this;
}

上面的設(shè)計方式有如下幾大優(yōu)點:

  1. 功能職責(zé)分明茫多,ShaderStreamInterface 抽象類專注于著色器代碼的編寫、加載和管理邓萨,地梨,Shader 類專注于著色器的編譯、鏈接缔恳、管理著色器的生命周期宝剖。
  2. 擴展性強,如果需要編寫新的著色器代碼歉甚,只需要新寫一個類繼承自 ShaderStreamInterface 即可万细。
  3. 易于調(diào)試,可以分別對 Shader 類以及 ShaderStreamInterface 其子類進行單元測試纸泄。

現(xiàn)在整個著色器類已經(jīng)設(shè)計完成赖钞,接下來我們來完成繪制一個有顏色的三角形

繪制有顏色的三角形

  1. 定義 ShaderStream 類繼承自 ShaderStreamInterface,并編寫頂點著色器和片段著色器代碼
    .hpp
#include <stdio.h>
#include "ShaderStreamInterface.h"
/** 章節(jié)案例 */
class ShaderStream: public ShaderStreamInterface {
public:
    ShaderStream();
    char *getVertexCode() {
        return vertextCode;
    }
    char *getFragmentCode() {
        return fragmentCode;
    }
protected:
    char *vertexStream();
    char *fragmentStream();
};

.cpp

ShaderStream:: ShaderStream() {
    // 頂點著色器
    vertextCode = vertexStream();
    // 片段著色器代碼
    fragmentCode = fragmentStream();
}

char * ShaderStream:: vertexStream() {
    return SHADER_SRC(
                      \#version 330 core\n
                      layout (location = 0) in vec3 aPos;
                      layout (location = 1) in vec3 aColor;

                      out vec3 ourColor;

                      void main()
                      {
                          gl_Position = vec4(aPos, 1.0);
                          ourColor = aColor;
                      }
            );
}

char * ShaderStream:: fragmentStream() {
    return SHADER_SRC(
                      \#version 330 core\n
                      out vec4 FragColor;
                      in vec3 ourColor;
                      void main() {
                          FragColor = vec4(ourColor, 1.0);
                      }
    );
}
  1. 來到主應(yīng)用程序 main 函數(shù)中聘裁,引入相關(guān)類
#include "ShaderStream.hpp"
#include "Shader.hpp"
  1. 初始化glfw雪营,初始化窗口,初始化 glew衡便,前面已經(jīng)講過了献起,這里不再重復(fù)。
  2. 初始化著色器
    // 著色器代碼對象
    ShaderStream *shaderCode = new ShaderStream();
    // 著色器程序
    Shader *shader = new Shader(shaderCode);
  1. 創(chuàng)建頂點數(shù)組镣陕、頂點緩沖對象谴餐、頂點數(shù)組對象,這塊是跟之前你好 三角形一樣一樣的
    // 創(chuàng)建頂點數(shù)組多想 VAO
    float vertices[] = {
        // 位置              // 顏色
         0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下
        -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下
         0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 頂部
    };
    
    unsigned int vao, vbo;
    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);
    glBindVertexArray(vao);
    
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    // 位置屬性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 顏色屬性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
    glEnableVertexAttribArray(1);
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
  1. 執(zhí)行渲染循環(huán)
    while (!glfwWindowShouldClose(window)) {
        procesInput(window);
        glClearColor(0.2, 0.2, 0.3, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        shader->use();
        glBindVertexArray(vao);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glfwSwapBuffers(window);
        glfwPollEvents();
        
    }
  1. 處理退出呆抑,釋放資源
    std::cout << "Hello, World!\n";
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);
    glDeleteProgram(shader->ID);
    glfwTerminate();

運行command + R 運行岂嗓,得到一個五彩斑斕的三角形:


FileSharing.png

如果你運行出來有問題或者出錯了,情仔細檢查一下代碼鹊碍,或者在本文下方下載筆者寫的demo厌殉。

源代碼在這里:https://github.com/muxueChen/LearnOpenGL

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市侈咕,隨后出現(xiàn)的幾起案子年枕,更是在濱河造成了極大的恐慌,老刑警劉巖乎完,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異品洛,居然都是意外死亡树姨,警方通過查閱死者的電腦和手機摩桶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帽揪,“玉大人硝清,你說我怎么就攤上這事∽” “怎么了芦拿?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長查邢。 經(jīng)常有香客問我蔗崎,道長,這世上最難降的妖魔是什么扰藕? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任缓苛,我火速辦了婚禮,結(jié)果婚禮上邓深,老公的妹妹穿的比我還像新娘未桥。我一直安慰自己,他們只是感情好芥备,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布冬耿。 她就那樣靜靜地躺著,像睡著了一般萌壳。 火紅的嫁衣襯著肌膚如雪亦镶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天讶凉,我揣著相機與錄音染乌,去河邊找鬼。 笑死懂讯,一個胖子當著我的面吹牛荷憋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播褐望,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼勒庄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瘫里?” 一聲冷哼從身側(cè)響起实蔽,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谨读,沒想到半個月后局装,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年铐尚,在試婚紗的時候發(fā)現(xiàn)自己被綠了拨脉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡宣增,死狀恐怖玫膀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情爹脾,我是刑警寧澤帖旨,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站灵妨,受9級特大地震影響解阅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闷串,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一瓮钥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烹吵,春花似錦碉熄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凉蜂,卻和暖如春琼梆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背窿吩。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工茎杂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人纫雁。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓煌往,卻偏偏與公主長得像,于是被迫代替她去往敵國和親轧邪。 傳聞我的和親對象是個殘疾皇子刽脖,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容