1. 程式人生 > >OpenGL學習整理------著色器

OpenGL學習整理------著色器

1.著色器

  著色器(Shader)是執行在GPU上的小程式。這些小程式為圖形渲染管線的某個特定部分而執行。從基本意義上說,著色器

只是一種把輸入轉化為輸出的程式。著色器也是一種非常獨立的程式,因為它們之間不能互相通訊;

       著色器之間唯一的溝通只有通過輸入和輸出;

2.GLSL

  著色器是用一種叫GLSL(OpenGL Shader Luanguage) 的類C語言寫成的。

  典型的著色器結構如下

    #version version_number
    in type in_variable_name;
    in type in_variable_name;

    out type out_variable_name;

    uniform type uniform_name;

    int main()
    {
      // 處理輸入並進行一些圖形操作
      ...
      // 輸出處理過的結果到輸出變數
      out_variable_name = weird_stuff_we_processed;
    }

   特別對於頂點著色器,每個輸入變數也叫頂點屬性(Vertex Attribute)。我們能宣告的頂點屬性是有上限的,一般由硬體決定。

   OpenGL確保至少有16個包含4分量的頂點屬性可用,有些硬體或許允許更多的頂點屬性,可以查詢GL_MAX_VERTEX_ATTRIBS來獲取具體上限:

    int nrAttributes;
    glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
    std::cout << "Maximum nr of vertex attributes supported: 
" << nrAttributes << std::endl;

3.資料型別

  GLSL有資料型別可以來指定變數的種類。

  資料型別: int、float、double、uin(unsigned int )t、bool;

  容器型別: Vector、Matrix;

4.向量

  GLSL中的向量是一個可以包含有1、2、3或者4個分量的容器,分量的型別可以是前面預設基礎型別的任意一個。它們可以是下面的形式(n代表分量的數量):

型別 含義
vecn 包含n個float分量的預設向量
bvecn 包含n個bool分量的向量
ivecn 包含n個int分量的向量
uvecn 包含n個unsigned int分量的向量
dvecn 包含n個double分量的向量

 

  大多數時候我們使用vecn,因為float足夠滿足大多數要求了。

 

5.輸入輸出

  我們希望每個著色器都有輸入和輸出,這樣才能進行資料交流和傳遞。

  GLSL定義了inout關鍵字專門來實現這個目的。每個著色器使用這兩個關鍵字設定輸入和輸出,只要一個輸出變數與下一個著色器階段的輸入匹配,它就會傳遞下去。

 

  但在頂點和片段著色器中會有點不同。

  頂點著色器應該接收的是一種特殊形式的輸入,否則就會效率低下。頂點著色器的輸入特殊在,它從頂點資料中直接接收輸入。

  為了定義頂點資料該如何管理,我們使用location這一元資料指定輸入變數,這樣我們才可以在CPU上配置頂點屬性。

  我們已經在前面的教程看過這個了,layout (location = 0)頂點著色器需要為它的輸入提供一個額外的layout標識,這樣我們才能把它連結到頂點資料。

你也可以忽略layout (location = 0)識別符號,通過在OpenGL程式碼中使用glGetAttribLocation查詢屬性位置值(Location),但是我更喜歡在著色器中設定它們,這樣會更容易理解而且節省你(和OpenGL)的工作量。

 

  另一個例外是片段著色器,它需要一個vec4顏色輸出變數,因為片段著色器需要生成一個最終輸出的顏色。

  如果你在片段著色器沒有定義輸出顏色,OpenGL會把你的物體渲染為黑色(或白色)。

 

6.Uniform

  Uniform是一種從CPU中的應用向GPU中的著色器傳送資料的方式,但uniform和頂點屬性有些不同。

  首先,uniform是全域性的(Global)。全域性意味著uniform變數必須在每個著色器程式物件中都是獨一無二的而且它可以被著色器程式的任意著色器在任意階段訪問

  第二無論你把uniform值設定成什麼,uniform會一直儲存它們的資料,直到它們被重置或更新。

 

  頂點著色器中不需要這個uniform,所以我們不用在那裡定義它。

 

  

如果你聲明瞭一個uniform卻在GLSL程式碼中沒用過,編譯器會靜默移除這個變數,導致最後編譯出的版本中並不會包含它,這可能導致幾個非常麻煩的錯誤,記住這點!

 

  當我們在著色器中定義了Uniform後,我們還沒有給它新增任何資料,所以下面我們就做這件事。

  我們首先需要找到著色器中uniform屬性的索引/位置值。當我們得到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);