OpenGL學習(十)-- 着色語言 GLSL 語法介紹

個人 OpenGL 專題學習目錄,但願和你們一塊兒學習交流進步!html


1、簡介

GLSLOpenGL Shading Language) 全稱 OpenGL 着色語言,是用來在 OpenGL 中着色編程的語言,也即開發人員寫的短小的自定義程序,他們是在圖形卡的 GPU上執行的,代替了固定的渲染管線的一部分,使渲染管線中不一樣層次具備可編程性。 GLSL 其使用 C 語言做爲基礎高階着色語言,避免了使用匯編語言或硬件規格語言的複雜性。編程

2、變量命名

GLSL 的變量命名方式與 C 語言相似,可以使用字母,數字以及下劃線,不能以數字開頭。還須要注意的是,變量名不能以 gl_ 做爲前綴,這個是 GLSL 保留的前綴,用於 GLSL 的內部變量。數組

3、數據類型

一、基本數據類型

類型 描述
void C 語言的 void 相似,表示空類型。做爲函數的返回類型,表示這個函數不返回值。
bool 布爾類型truefalse,以及能夠產生布爾型的表達式。
int 有符號整型
uint 無符號整形
float 浮點型

二、特殊類型--紋理採樣類型

類型 描述
sampler1D 用於內建的紋理函數中引用指定的 1D紋理的句柄。只能夠做爲一致變量或者函數參數使用
sampler2D 二維紋理句柄
sampler3D 三維紋理句柄
samplerCube cube map 紋理句柄
sampler1DShadow 一維深度紋理句柄
sampler2DShadow 二維深度紋理句柄

三、聚合類型(向量和矩陣類型)

(1)向量類型

類型 描述
vec2,vec3,vec4 2份量、3份量和4份量浮點向量
ivec2,ivec3,ivec4 2份量、3份量和4份量整數向量
uvec2,uvec3,uvec4 2份量、3份量和4份量無符號整數向量
bvec2,vbec3,bvec4 2份量、3份量和4份量布爾向量

A、􏰴􏰜􏰷􏰸向量聲明--4份量的float 類型向量

vec4 V1;
複製代碼

B、􏰴􏰜􏰷􏰸聲明向量並對其進行構造

vec4 V2 = vec4(1,2,3,4);
複製代碼

C、􏰴􏰜􏰷􏰸向量運算

vec4 v;
vec4 vOldPos = vec4(1,2,3,4);
vec4 vOffset = vec4(1,2,3,4);
//注意:接下來假設全部參與運算的變量已定義並賦值。
v = vOldPos + vOffset;
v = vNewPos;
v += vec4(10,10,10,10);
v = vOldPos * vOffset;
v *= 5;
複製代碼

D、􏰴􏰜􏰷􏰸向量元素的獲取(成分選擇)

向量中單獨的成分能夠經過 {x,y,z,w}, {r,g,b,a} 或者 {s,t,p,q} 的記法來表示。這些不一樣的記法用於 頂點顏色紋理座標。在成分選擇中,你不能夠混合使用這些記法。其中 {s,t,p,q} 中的 p 替換了紋理r 座標,由於與顏色 r 重複了。下面是用法舉例: 例若有向量 v1v2:less

vec3 v1 = {0.5, 0.35, 0.7};
vec4 v2 = {0.1, 0.2, 0.3, 0.4};
複製代碼

能夠經過 {x,y,z,w}, {r,g,b,a} 或者 {s,t,p,q} 來取出向量中的元素值。 經過 x,y,z,w函數

v2.x = 3.0f;
v2.xy = vec2(3.0f,4.0f);
v2.xyz = vec3(3,0f,4,0f,5.0f);
複製代碼

經過 r,g,b,apost

v2.r = 3.0f;
v2.rgba = vec4(1.0f,1.0f,1.0f,1.0f);
複製代碼

經過 s,t,q,r學習

v2.stqr = vec2(1.0f, 0.0f, 0.0f, 1.0f);
複製代碼

錯誤示例:測試

float myQ = v1.q;// 出錯,數組越界訪問,q表明第四個元素
float myRY = v1.ry; // 不合法,混合使用記法
複製代碼

向量還支持一次性對全部份量操做優化

v1.x = v2.x +5.0f; 
v1.y = v2.y +4.0f; 
v1.z = v2.z +3.0f;
v1.xyz = v2.xyz + vec3(5.0f,4.0f,3.0f);
複製代碼

(2)矩陣類型

類型 描述
mat2 或 mat2x2 2x2的浮點數矩陣類型
mat3 或 mat3x3 3x3的浮點數矩陣類型
mat4 或 mat4x4 4x4的浮點數矩陣類型
mat2x3 2列3行的浮點矩陣(OpenGL的矩陣是列主順序的)
mat2x4 2列4行的浮點矩陣
mat3x2 3列2行的浮點矩陣
mat3x4 3列4行的浮點矩陣
mat4x2 4列2行的浮點矩陣
mat4x3 4列3行的浮點矩陣

建立矩陣:ui

mat4 m1,m2,m3;
複製代碼

構造單元矩陣:

mat4 m2 = mat4(1.0f,0.0f,0.0f,0.0f
                    0.0f,1.0f,0.0f,0.0f,
                    0.0f,0.0f,1.0f,0.0f,
                    0.0f,0.0f,0.0f,1.0f);
複製代碼

或者

mat4 m4 = mat4(1.0f);
複製代碼

四、數組

GLSL 中只可使用一維的數組。數組的類型能夠是一切基本類型或者結構體。下面的幾種數組聲明是合法的:

float floatArray[4];
vec4 vecArray[2];
float a[4] = float[](1.0,2.0,3.0,4.0);
vec2 c[2] = vec2[2](vec2(1.0,2.0),vec2(3.0,4.0));
複製代碼

數組類型內建了一個length()函數,能夠返回數組的長度。

lightPositions.length() // 返回數組的長度
複製代碼

五、結構體

結構體能夠組合基本類型和數組來造成用戶自定義的類型。在定義一個結構體的同時,你能夠定義一個結構體實例。或者後面再定義。

struct fogStruct {
 vec4 color;
 float start;
 float end;
 vec3 points[3]; // 固定大小的數組是合法的
} fogVar;
複製代碼

能夠經過 = 爲結構體賦值,或者使用 ==,!= 來判斷兩個結構體是否相等。

fogVar = fogStruct(vec4(1.0,0.0,0.0,1.0),0.5,2.0);
vec4 color = fogVar.color;
float start = fogVar.start;
複製代碼

3、修飾符

一、變量存儲限定符

限定符 描述
(默認的可省略)只是普通的本地變量,可讀可寫,外部不可見,外部不可訪問
const 常量值必須在聲明時初始化,它是隻讀的不可修改的
varying 頂點着色器的輸出,主要負責在 vertexfragment 之間傳遞變量。例如顏色或者紋理座標,(插值後的數據)做爲片斷着色器的只讀輸入數據。必須是全局範圍聲明的全局變量。能夠是浮點數類型的標量,向量,矩陣。不能是數組或者結構體。
uniform 一致變量。在着色器執行期間一致變量的值是不變的。與 const 常量不一樣的是,這個值在編譯時期是未知的是由着色器外部初始化的。一致變量在頂點着色器和片斷着色器之間是共享的。它也只能在全局範圍進行聲明。
attribute 表示只讀的頂點數據,只用在頂點着色器中。數據來自當前的頂點狀態或者頂點數組。它必須是全局範圍聲明的,不能再函數內部。一個 attribute 能夠是浮點數類型的標量,向量,或者矩陣。不能夠是數組或則結構體
centorid varying 在沒有多重採樣的狀況下,與 varying 是同樣的意思。在多重採樣時,centorid varying 在光柵化的圖形內部進行求值而不是在片斷中心的固定位置求值。
invariant (不變量)用於表示頂點着色器的輸出和任何匹配片斷着色器的輸入,在不一樣的着色器中計算產生的值必須是一致的。全部的數據流和控制流,寫入一個 invariant 變量的是一致的。編譯器爲了保證結果是徹底一致的,須要放棄那些可能會致使不一致值的潛在的優化。除非必要,不要使用這個修飾符。在多通道渲染中避免 z-fighting 可能會使用到。

二、函數參數限定符

GLSL 容許自定義函數,但參數默認是以值形式(in 限定符)傳入的,也就是說任何變量在傳入時都會被拷貝一份,若想以引用方式傳參,須要增長函數參數限定符。

限定符 描述
in 用在函數的參數中,表示這個參數是輸入的,在函數中改變這個值,並不會影響對調用的函數產生反作用。(至關於C語言的傳值),這個是函數參數默認的修飾符
out 用在函數的參數中,表示該參數是輸出參數,值是會改變的。
inout 用在函數的參數,表示這個參數便是輸入參數也是輸出參數。

其中使用 inout 方式傳遞的參數便與其餘 OOP 語言中的引用傳遞相似,參數可讀寫,函數內對參數的修改會影響到傳入參數自己。 eg:

vec4 getPosition(out vec4 p){ 
    p = vec4(0.,0.,0.,1.);
    return v4;
}

void doubleSize(inout float size){
    size= size * 3.0  ;
}
複製代碼

3、GLSL 中的運算

⚠️注意 GLSL 中沒有隱式轉換,即使在多維向量中也沒有,相似下面這樣的的賦值都是錯誤的:

// ⚠️錯誤
int a = 2.0;
vec4 v4=vec4(1.0, 1.0, 2, 1.0);
複製代碼

一、不一樣類型 float 與 int 間的運算:

floatint 之間進行運算,須要進行一次顯示轉換,如下表達式都是正確的:

int a = int(2.0);
float a = float(2);

int a = int(2.0)*2 + 1;
float a = float(2)*6.0+2.3;
複製代碼

二、float 與 vec(向量)、mat(矩陣) 的運算:

  • 逐份量運算 vec,mat 這些類型實際上是由 float 複合而成的,當它們與float 運算時,其實就是在每個份量上分別與 float 進行運算,這就是所謂的 逐份量運算GLSL 裏,大部分涉及 vec,mat 的運算都是逐份量運算,但也並不全是。下文中就會講到特例。 逐份量運算 是線性的,這就是說 vec 與 float 的運算結果是仍是 vec

intvec,mat 之間是不可運算的,由於 vecmat 中的每個份量都是 float 類型的,沒法與 int 進行逐份量計算。

下面枚舉了幾種 floatvec,mat 運算的狀況:

vec3 a  = vec3(1.0, 2.0, 3.0);
mat3 m  = mat3(1.0);
float s = 10.0;

vec3 b  = s * a; // vec3(10.0, 20.0, 30.0)
vec3 c  = a * s; // vec3(10.0, 20.0, 30.0)
mat3 m2 = s * m; // = mat3(10.0)
mat3 m3 = m * s; // = mat3(10.0)
複製代碼

三、vec(向量) 與 vec(向量)運算:

兩向量間的運算首先要保證操做數的階數都相同,不然不能計算。例如: vec3*vec2vec4+vec3 等等都是不行的。

它們的計算方式是兩操做數在同位置上的份量分別進行運算,其本質仍是逐份量進行的,這和上面所說的 float 類型的逐份量運算可能有一點點差別,相同的是 vecvec 運算結果仍是 vec,且階數不變。

vec3 a = vec3(1.0, 2.0, 3.0);
vec3 b = vec3(0.1, 0.2, 0.3);
vec3 c = a + b; // = vec3(1.1, 2.2, 3.3);
vec3 d = a * b; // = vec3(0.1, 0.4, 0.9);
複製代碼

vec-vec.png

四、vec(向量) 與 mat(矩陣):

要保證操做數的階數相同,且 vecmat 間只存在乘法運算。 它們的計算方式和線性代數中的矩陣乘法相同,不是逐份量運算

vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2.,  3., 4.);
vec2 w = m * v; // = vec2(1. * 10. + 3. * 20., 2. * 10. + 4. * 20.)

vec2 v = vec2(10., 20.);
mat2 m = mat2(1., 2.,  3., 4.);
vec2 w = v * m; // = vec2(1. * 10. + 2. * 20., 3. * 10. + 4. * 20.)
複製代碼

五、mat(矩陣) 與 mat(矩陣):

⚠️要保證操做數的階數相同。

matmat 的運算中,除了乘法是線性代數中的矩陣乘法外,其他的運算仍爲逐份量運算。簡單說就是隻有乘法是特殊的,其他都和 vecvec 運算相似。

mat2 a = mat2(1., 2.,  3., 4.);
mat2 b = mat2(10., 20.,  30., 40.);
mat2 c = a * b; // mat2(1.*10.+3.*20.,2.*10.+4.*20.,1.* 30.+3.*40.,2.* 30.+4.*40.);

mat2 d = a+b;// mat2(1.+10.,2.+20.,3.+30.,4.+40);
複製代碼

4、類型轉換

GLSL 能夠經過構造函數進行顯式轉換,方法以下:

bool t= true;
bool f = false;

int a = int(t); // true轉換爲1或1.0
int a1 = int(f);// false轉換爲0或0.0

float b = float(t);
float b1 = float(f);

bool c = bool(0);// 0或0.0轉換爲false
bool c1 = bool(1);// 非0轉換爲true

bool d = bool(0.0);
bool d1 = bool(1.0);
複製代碼

5、精度限定

GLSL 在進行光柵化着色的時候,會產生大量的浮點數運算,這些運算多是當前設備所不能承受的,因此 GLSL 提供了 3 種浮點數精度,咱們能夠根據不一樣的設備來使用合適的精度。 在變量前面加上 highpmediumplowp 便可完成對該變量的精度聲明:

lowp float color;
varying mediump vec2 Coord;
lowp ivec2 foo(lowp mat3);
highp mat4 m;
複製代碼

咱們通常在 片元着色器(fragment shader) 最開始的地方加上 precision mediump float; 便設定了默認的精度,這樣全部沒有顯式代表精度的變量都會按照設定好的默認精度來處理。

6、控制語句

在語法上,GLSLC 很是類似, 也有 if else、for、while、do while,使用 continue 跳入下一次循環,break 結束循環。

for (l = 0; l < numLights; l++) {
    if (!lightExists[l]);
        continue;
    color += light[l];
}

while (i < num) {
    sum += color[i];
    i++;
}

do {
    color += light[lightNum];
    lightNum--;
} while (lightNum > 0)
複製代碼

除了這些,GLSL 還多了一種特殊的控制語句 discard,它會當即跳出片元着色器,並不在向下任何語句。也就不執行後面的片斷着色操做,片斷也不會寫入幀緩衝區。

if (true)
    discard;
複製代碼

⚠️注意 GLSL 函數中沒有遞歸!

以上的總結參考了並部分摘抄瞭如下文章,很是感謝如下做者的分享!:

一、做者吃代碼的兔子窩的《初探 GLSL 着色器(一)》

三、做者吃代碼的兔子窩的《初探 GLSL 着色器(二)》

四、做者jeffasd的《glsl語言基礎》

轉載請備註原文出處,不得用於商業傳播——凡幾多

相關文章
相關標籤/搜索