OpenGL學習3——著色器

著色器(shaders)

  • 簡單的理解澡刹,著色器就是將輸入轉(zhuǎn)換為輸出的程序篓像,同時著色器也是非常獨立的程序动遭,它們之間的通訊只能通過它們的輸入和輸出善茎。


    OpenGL著色器主要內(nèi)容

1. GLSL

  • 著色器典型的結(jié)構(gòu):
#version version_number
in type in_variable_name;
in type in_variable_name;

out type out_variable_name;

void main()
{
    // 處理輸入明垢,做一些圖形操作
    // 將處理結(jié)果設置到輸出變量
    out_variable_name = werid_stuff_we_processed;
}
1. 聲明版本(和指定渲染模式)蚣常。
2. 定義輸入變量。
3. 定義輸出變量痊银。
4. 在main函數(shù)中設置輸出變量的值抵蚊。
  • 當針對頂點著色器時,輸入變量也稱為頂點屬性(vertex attribute)溯革。我們可以聲明的頂點屬性的最大數(shù)量由硬件限制贞绳,但是OpenGL保證我們至少可以使用16個4維頂點屬性變量。硬件具體支持的數(shù)量可以通過查詢GL_MAX_VERTEX_ATTRIBS獲得致稀。
int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

2. 類型

  • 矢量(vector):在GLSL中矢量是一個由1,2,3或4個基本類型組件組成的容器冈闭。可以有如下的形式(其中n代表組件的數(shù)量):
    • vecn:n個float的矢量(默認)豺裆。
    • bvecn:n個boolean的矢量拒秘。
    • ivecn:n個整數(shù)的矢量。
    • uvecn:n個無符號整數(shù)的矢量臭猜。
    • dvecn:n個double的矢量躺酒。
  • 矢量通過vec.x, vec.y, vec.z, vec.w訪問相應的組件。在GLSL中蔑歌,顏色還可以通過rgba訪問羹应,紋理(texture)可以通過stpq訪問。
  • 矢量允許我們進行一些靈活的組件選擇次屠,叫做swizzling园匹,語法類似下面的代碼:
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
// 傳入到構(gòu)造函數(shù)中
vec2 vect = vec2(0.5, 0.7);
vec4 = result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);

3. 輸入和輸出

  • 當一個輸出變量與下一著色器階段的輸入變量匹配時,變量將在兩個著色器間傳遞劫灶。
  • 在輸入輸出方面裸违,比較特殊的兩個著色器是頂點著色器和片元著色器。
      1. 頂點著色器:直接從頂點數(shù)據(jù)接收輸入本昏,使用location元數(shù)據(jù)來指定輸入變量以便我們可以在CPU側(cè)配置頂點屬性供汛。
      1. 片元著色器:因為片元著色器需要生成一個最終輸出顏色,因此該著色器需要一個vec4的顏色輸出變量。如果沒有指定一個輸出顏色怔昨,那么片元的顏色將是不確定的雀久。
  • 如果我們要在兩個著色器之間傳遞變量,那么我們需要在發(fā)送的著色器中聲明一個輸出變量趁舀,在接收的著色器中聲明一個匹配的輸入變量赖捌。這樣,在鏈接著色器程序時矮烹,兩個著色器的變量會鏈接到一起越庇。下面的著色器顯示從頂點著色器傳遞顏色值到片元著色器。
// 頂點著色器
#version 330 core
layout (location = 0) in vec3 aPos;

out vec4 vertexColor;   // 指定一個顏色輸出給片元著色器

void main()
{
    gl_Position = vec4(aPos, 1.0);
    vertexColor = vec4(0.5, 0.0, 0.0, 1.0);   // 設置輸出變量值 dark-red
}
// 片元著色器
#version 330 core
out vec4 FragColor;

in vec4 vertexColor;   // 從頂點著色器獲取值的輸入變量(相同的名稱和數(shù)據(jù)類型)

void main()
{
    FragColor = vertexColor;  // 設置輸出變量值
}
  • 將上述著色器應用到我們前一篇博客的代碼中擂送,運行效果如下:


    著色器傳遞變量效果

4. Uniforms

  • Uniforms是另一種從CPU側(cè)悦荒,即我們的OpenGL程序傳遞數(shù)據(jù)到GPU中的著色器的方式。
  • Uniforms與頂點屬性的區(qū)別:
      1. Uniforms是全局的嘹吨,意味著每個著色器程序?qū)ο笾邪嵛叮瑄niform變量是唯一的,且可以從著色器程序中任何階段的任何著色器進行訪問蟀拷。
      1. 不管你將uniforms值設置為什么碰纬,值會一直保持直到被重置或更新。
  • 使用uniform關鍵字聲明Uniform變量问芬。
#version 330 core
out vec4 FragColor;

uniform vec4 ourColor;   // 該變量的值在我們的OpenGL代碼中設置

void main()
{
    FragColor = ourColor;  // 設置輸出變量值
}
  • 注意:如果你聲明了一個uniform悦析,但是沒有在你的GLSL代碼使用,那么編譯器將會在編譯版本中移除此衅,這可能導致一些不確定的問題强戴。
  • 設置uniform變量的值
float timeValue = glfwGetTime();
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");

glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
1. glfwGetTime()函數(shù)獲取程序運行時間,使用`sin()`函數(shù)轉(zhuǎn)換為一個[0.0, 1.0]之間的顏色值挡鞍。
2. glGetUniformLocation()函數(shù)查詢uniform變量的位置(示例中為ourColor)骑歹。
3. 使用getUniform4f()函數(shù)設置uniform變量的值。
  • 將上述片元著色器應用到我們前一篇博客的代碼中墨微,運行效果如下道媚,矩形將在綠色和黑色之間變化:


    Uniform設置效果圖1
Uniform設置效果圖2

5. 更多屬性(attributes)

  • 為頂點數(shù)據(jù)添加顏色屬性:
// 頂點數(shù)據(jù)
float vertices[] = {
    // positions        // colors
    -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
};
  • 調(diào)整頂點著色器以接收頂點數(shù)據(jù)中的顏色屬性:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

void main()
{
    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    ourColor = aColor;   // 將頂點數(shù)據(jù)輸入的顏色設置為輸出
}
  • 調(diào)整片元著色器接收頂點著色器的顏色輸出:
#version 330 core
out vec4 FragColor;
in vec3 ourColor;

void main()
{
    FragColor = vec4(ourColor, 1.0f);
}
  • 包含位置和顏色屬性的VBO內(nèi)存可能如下圖所示:(圖片取自書中
    VBO內(nèi)存示意圖
  • 使用glVertexAttribPointer函數(shù)更新頂點數(shù)據(jù)的格式,參數(shù)的值設置對比上圖的內(nèi)存情形:
// 1. 設置頂點位置屬性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 2. 設置頂點顏色屬性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
  • 運行結(jié)果:


    添加顏色屬性運行效果

6. 編寫我們自己的著色器類

  • 編寫一個著色器類將頂點著色器和片元著色器的創(chuàng)建和編譯翘县,以及著色器程序的鏈接封裝起來最域。
// 著色器頭文件
#pragma once

#include <glad/glad.h>

#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

class Shader
{
public:
    // the program ID
    unsigned int ID;

    // constructor reads and builds the shader
    Shader(const char* vertexPath, const char* fragmentPath);
    // use/active the shader
    void use();
    // utility uniform functions
    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;
};
// 著色器類實現(xiàn)文件
#include <iostream>
#include <fstream>
#include "shader.h"

Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
    //------1. 從指定路徑讀取頂點/片元著色器程序內(nèi)容
    std::string vertexCode;
    std::string fragmentCode;
    std::ifstream vShaderFile;
    std::ifstream fShaderFile;
    // ensure ifstream objects can throw exceptions
    vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    try
    {
        // open file 
        vShaderFile.open(vertexPath);
        fShaderFile.open(fragmentPath);
        std::stringstream vShaderStream, fShaderStream;
        // read file's buffer contents into stream
        vShaderStream << vShaderFile.rdbuf();
        fShaderStream << fShaderFile.rdbuf();
        // close file handlers
        vShaderFile.close();
        fShaderFile.close();
        // convert stream into string
        vertexCode = vShaderStream.str();
        fragmentCode = fShaderStream.str();
    }
    catch(std::ifstream::failure e)
    {
        std::cout << "EEROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    }
    const char* vShaderCode = vertexCode.c_str();
    const char* fShaderCode = fragmentCode.c_str();
    //------2. 創(chuàng)建和編譯著色器
    unsigned int vertex, fragment;
    int success;
    char infoLog[512];
    //---------2.1 創(chuàng)建和編譯頂點著色器
    vertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex, 1, &vShaderCode, NULL);
    glCompileShader(vertex);
    // print compile errors if any
    glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(vertex, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    //---------2.2 創(chuàng)建和編譯片元著色器
    fragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment, 1, &fShaderCode, NULL);
    glCompileShader(fragment);
    glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
    if (!success)
    {
        glGetShaderInfoLog(fragment, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    }
    //------3. 鏈接著色器程序
    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::LINK_FAILED\n" << infoLog << std::endl;
    }
    //------4. 鏈接著色器程序后刪除著色器對象
    glDeleteShader(vertex);
    glDeleteShader(fragment);
}
// 激活著色器程序
void Shader::use()
{
    glUseProgram(ID);
}
// 一些輔助函數(shù),用于設置unfirom的值
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);
}
  • 著色器類的調(diào)用
// 聲明著色器類(路徑取決于shader文件的存放位置锈麸,文件后綴無關緊要)
Shader ourShader("./shaders/VertexShader.vs", "./shaders/FragmentShader.fs");
// 在渲染循環(huán)中調(diào)用
ourShader.use();
ourShader.setFloat("ourColor", 0.2f);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末镀脂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子忘伞,更是在濱河造成了極大的恐慌狗热,老刑警劉巖钞馁,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虑省,死亡現(xiàn)場離奇詭異匿刮,居然都是意外死亡,警方通過查閱死者的電腦和手機探颈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門熟丸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人伪节,你說我怎么就攤上這事光羞。” “怎么了怀大?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵纱兑,是天一觀的道長。 經(jīng)常有香客問我化借,道長潜慎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任蓖康,我火速辦了婚禮铐炫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒜焊。我一直安慰自己倒信,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布泳梆。 她就那樣靜靜地躺著鳖悠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪优妙。 梳的紋絲不亂的頭發(fā)上乘综,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音鳞溉,去河邊找鬼瘾带。 笑死,一個胖子當著我的面吹牛熟菲,可吹牛的內(nèi)容都是我干的看政。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼抄罕,長吁一口氣:“原來是場噩夢啊……” “哼允蚣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呆贿,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤嚷兔,失蹤者是張志新(化名)和其女友劉穎森渐,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冒晰,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡同衣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了壶运。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耐齐。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蒋情,靈堂內(nèi)的尸體忽然破棺而出埠况,到底是詐尸還是另有隱情,我是刑警寧澤棵癣,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布辕翰,位于F島的核電站,受9級特大地震影響狈谊,放射性物質(zhì)發(fā)生泄漏喜命。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一的畴、第九天 我趴在偏房一處隱蔽的房頂上張望渊抄。 院中可真熱鬧,春花似錦丧裁、人聲如沸护桦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽二庵。三九已至,卻和暖如春缓呛,著一層夾襖步出監(jiān)牢的瞬間催享,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工哟绊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留因妙,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓票髓,卻偏偏與公主長得像攀涵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子洽沟,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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