大家好,歡迎來到聽風(fēng)的OpenGL日常。
寫在前面
本篇我們通過OpenGL的數(shù)據(jù)流動來理解著色器,通過數(shù)據(jù)流會很清楚的看到OpenGL著色器的工作原理。所以今天要說的內(nèi)容全部跟GLSL語言有關(guān),當(dāng)然這樣的一篇不可能全部講完(可能連皮毛都沒有),理解著色器是如何傳數(shù)據(jù)的才是我們的重點(diǎn)。
話不多說,一圖以蔽之。

如圖中,上部CPU的部分為頂點(diǎn)數(shù)據(jù),其中包含頂點(diǎn)、顏色數(shù)據(jù)(一般情況下);下部分橙色為頂點(diǎn)著色器,綠色為片元著色器;gl_Position內(nèi)置變量用于頂點(diǎn)位置設(shè)置,gl_FlagColor內(nèi)置變量用于片元顏色設(shè)置。
著色器程序示例
頂點(diǎn)著色器
頂點(diǎn)著色器shader.vs:
#version 330 core
layout (location=0) in vec3 position;
layout (location=1) in vec3 color;
attribute vec2 tex_coord;
varying vec2 transform_to_frag;
out vec4 vertex_color;
void main()
{
gl_Position = vec4(position, 1.0);
vertex_color = vec4(1.0f, 0.0f, 0.0f, 1.0f);
}
片元著色器
片元著色器shader.frag
#version 330 core
in vec4 vertex_color;
varying vec2 transform_to_frag;
uniform sampler2D color_map;
out vec4 color;
void main()
{
color = vertex_color;
gl_FragColor = texture2D(color_map, transform_to_frag);
}
由屬性展開去
in/out
如其英文所代表的含義,理解起來也很直觀,對于每一個著色器而言,從外部獲取的數(shù)據(jù)為in屬性,傳出的數(shù)據(jù)為out屬性;
這里有一點(diǎn),對于頂點(diǎn)著色器中的out屬性變量,如在片元著色器中有同名的in屬性變量,則為同一變量,因?yàn)檫@樣的屬性不可以在全局使用,所以采用這樣的方式可以傳值。(還有其他方式也可以傳值)
layout (location=0)

在上面的頂點(diǎn)著色器中,
layout (location=0) in vec3 position;
layout (location=1) in vec3 color;
包含有layout (location=0)屬性的in變量,在前面的幾節(jié)中,我們知道著色器通過glVertexAttribPointer分配頂點(diǎn)數(shù)據(jù),其中,location決定著色器從頂點(diǎn)緩存中獲取數(shù)據(jù)的方式。
// 位置屬性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
// 顏色屬性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3* sizeof(GLfloat)));
glEnableVertexAttribArray(1);

頂點(diǎn)和顏色數(shù)據(jù)在內(nèi)存中的分配如圖,glEnableVertexAttribArray中的參數(shù)標(biāo)識location的數(shù)字,表示向著色器中傳送的數(shù)據(jù)在內(nèi)存中的起始點(diǎn);每個Vertex在內(nèi)存中都是固定的大小,包含我們所需要的所有數(shù)據(jù),在選取其中的數(shù)據(jù)時,需要提供步長來指明下一次的初始位置,如圖,每個頂點(diǎn)數(shù)據(jù)有6個字節(jié),位置與顏色數(shù)據(jù)分別占了3個字節(jié)。
attribute

attribute變量是只能在vertex shader中使用的變量。(它不能在fragment shader中聲明attribute變量,也不能被fragment shader中使用)
一般用attribute變量來表示一些頂點(diǎn)的數(shù)據(jù),如:頂點(diǎn)坐標(biāo),法線,紋理坐標(biāo),頂點(diǎn)顏色等。
在application中,一般用函數(shù)glBindAttribLocation來綁定每個attribute變量的位置,然后用函數(shù)glVertexAttribPointer為每個attribute變量賦值。
varing

varying變量是vertex和fragment shader之間做數(shù)據(jù)傳遞用的。一般vertex shader修改varying變量的值,然后fragment shader使用該varying變量的值。因此varying變量在vertex和fragment shader二者之間的聲明必須是一致的。所以它是只著色器之間使用,不能被應(yīng)用程序調(diào)用;
uniform

uniform變量是外部程序傳遞給(vertex和fragment)shader的變量。因此它是application通過函數(shù)glUniform**函數(shù)賦值的。在(vertex和fragment)shader程序內(nèi)部,uniform變量就像是C語言里面的常量(const ),它不能被shader程序修改。(shader只能用,不能改)
如果uniform變量在vertex和fragment兩者之間聲明方式完全一樣,則它可以在vertex和fragment共享使用。(相當(dāng)于一個被vertex和fragment shader共享的全局變量)
uniform變量一般用來表示:變換矩陣,材質(zhì),光照參數(shù)和顏色等信息。
向量
關(guān)于這一節(jié)從網(wǎng)上復(fù)制了一下,就不做具體討論了,需要注意的是“重組”的概念。
和其他編程語言一樣,GLSL有數(shù)據(jù)類型可以來指定變量的種類。GLSL中包含C等其它語言大部分的默認(rèn)基礎(chǔ)數(shù)據(jù)類型:int、float、double、uint和bool。GLSL也有兩種容器類型,它們會在這個教程中使用很多,分別是向量(Vector)和矩陣(Matrix),今天只針對向量做一些介紹。
GLSL中的向量是一個可以包含有1、2、3或者4個分量的容器,分量的類型可以是前面默認(rèn)基礎(chǔ)類型的任意一個。它們可以是下面的形式(n代表分量的數(shù)量):
| 類型 | 含義 |
|---|---|
| vecn | 包含n個float分量的默認(rèn)向量 |
| bvecn | 包含n個bool分量的向量 |
| ivecn | 包含n個int分量的向量 |
| uvecn | 包含n個unsigned int分量的向量 |
| dvecn | 包含n個double分量的向量 |
一個向量的分量可以通過vec.x這種方式獲取,這里x是指這個向量的第一個分量。你可以分別使用.x、.y、.z和.w來獲取它們的第1、2、3、4個分量。GLSL也允許你對顏色使用rgba,或是對紋理坐標(biāo)使用stpq訪問相同的分量。
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
你可以使用上面4個字母任意組合來創(chuàng)建一個和原來向量一樣長的(同類型)新向量,只要原來向量有那些分量即可;然而,你不允許在一個vec2向量中去獲取.z元素。我們也可以把一個向量作為一個參數(shù)傳給不同的向量構(gòu)造函數(shù),以減少需求參數(shù)的數(shù)量:
vec2 vect = vec2(0.5f, 0.7f);
vec4 result = vec4(vect, 0.0f, 0.0f);
vec4 otherResult = vec4(result.xyz, 1.0f);
寫在后面
個人理解著色器更靠近硬件,可以直接使用硬件進(jìn)行計算以提升效率,但是外部程序并不能用高級語言來對其進(jìn)行操作,所以著色器程序就建立起來一個這樣的橋梁,由外部獲取再由著色器計算并最終渲染,GLSL語言提供了外部傳入以及傳出的“接口”。