個人
OpenGL
專題學習目錄,但願和你們一塊兒學習交流進步!html
- OpenGL學習(一)-- 術語瞭解
- OpenGL學習(二)-- Xcode 搭建 OpenGL 環境
- OpenGL學習(三)-- OpenGL 基礎渲染
- OpenGL學習(四)-- 正面&背面剔除和深度測試
- OpenGL學習(五)-- 裁剪與混合
- OpenGL學習(六)-- 基礎紋理
- OpenGL學習(七)-- 基礎變化綜合練習實踐總結
- OpenGL學習(八)-- OpenGL ES 初探(上)
- OpenGL學習(九)-- OpenGL ES 初探(下)GLKit
- OpenGL學習(十)-- 着色語言 GLSL 語法介紹
- OpenGL學習(十一)-- 用 GLSL 實現加載圖片
- OpenGL學習(十二)-- OpenGL ES 紋理翻轉的策略對比
GLSL
(OpenGL Shading Language) 全稱 OpenGL 着色語言,是用來在 OpenGL 中着色編程的語言,也即開發人員寫的短小的自定義程序,他們是在圖形卡的 GPU上執行的,代替了固定的渲染管線的一部分,使渲染管線中不一樣層次具備可編程性。 GLSL
其使用 C 語言做爲基礎高階着色語言,避免了使用匯編語言或硬件規格語言的複雜性。編程
GLSL
的變量命名方式與 C 語言相似,可以使用字母,數字以及下劃線,不能以數字開頭。還須要注意的是,變量名不能以 gl_
做爲前綴,這個是 GLSL
保留的前綴,用於 GLSL
的內部變量。數組
類型 | 描述 |
---|---|
void | 跟 C 語言的 void 相似,表示空類型。做爲函數的返回類型,表示這個函數不返回值。 |
bool | 布爾類型,true 或 false,以及能夠產生布爾型的表達式。 |
int | 有符號整型 |
uint | 無符號整形 |
float | 浮點型 |
類型 | 描述 |
---|---|
sampler1D | 用於內建的紋理函數中引用指定的 1D紋理的句柄。只能夠做爲一致變量或者函數參數使用 |
sampler2D | 二維紋理句柄 |
sampler3D | 三維紋理句柄 |
samplerCube | cube map 紋理句柄 |
sampler1DShadow | 一維深度紋理句柄 |
sampler2DShadow | 二維深度紋理句柄 |
類型 | 描述 |
---|---|
vec2,vec3,vec4 | 2份量、3份量和4份量浮點向量 |
ivec2,ivec3,ivec4 | 2份量、3份量和4份量整數向量 |
uvec2,uvec3,uvec4 | 2份量、3份量和4份量無符號整數向量 |
bvec2,vbec3,bvec4 | 2份量、3份量和4份量布爾向量 |
vec4 V1;
複製代碼
vec4 V2 = vec4(1,2,3,4);
複製代碼
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;
複製代碼
向量中單獨的成分能夠經過 {x,y,z,w}, {r,g,b,a} 或者 {s,t,p,q} 的記法來表示。這些不一樣的記法用於 頂點,顏色,紋理座標。在成分選擇中,你不能夠混合使用這些記法。其中 {s,t,p,q} 中的 p 替換了紋理的 r 座標,由於與顏色 r 重複了。下面是用法舉例: 例若有向量 v1 和 v2: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,a:post
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);
複製代碼
類型 | 描述 |
---|---|
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;
複製代碼
限定符 | 描述 |
---|---|
(默認的可省略)只是普通的本地變量,可讀可寫,外部不可見,外部不可訪問 | |
const | 常量值必須在聲明時初始化,它是隻讀的不可修改的 |
varying | 頂點着色器的輸出,主要負責在 vertex 和 fragment 之間傳遞變量。例如顏色或者紋理座標,(插值後的數據)做爲片斷着色器的只讀輸入數據。必須是全局範圍聲明的全局變量。能夠是浮點數類型的標量,向量,矩陣。不能是數組或者結構體。 |
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 ;
}
複製代碼
⚠️注意 GLSL
中沒有隱式轉換,即使在多維向量中也沒有,相似下面這樣的的賦值都是錯誤的:
// ⚠️錯誤
int a = 2.0;
vec4 v4=vec4(1.0, 1.0, 2, 1.0);
複製代碼
float 與 int 之間進行運算,須要進行一次顯示轉換,如下表達式都是正確的:
int a = int(2.0);
float a = float(2);
int a = int(2.0)*2 + 1;
float a = float(2)*6.0+2.3;
複製代碼
- 逐份量運算 vec,mat 這些類型實際上是由 float 複合而成的,當它們與float 運算時,其實就是在每個份量上分別與 float 進行運算,這就是所謂的 逐份量運算。GLSL 裏,大部分涉及 vec,mat 的運算都是逐份量運算,但也並不全是。下文中就會講到特例。 逐份量運算 是線性的,這就是說 vec 與 float 的運算結果是仍是 vec。
int 與 vec,mat 之間是不可運算的,由於 vec 和 mat 中的每個份量都是 float 類型的,沒法與 int 進行逐份量計算。
下面枚舉了幾種 float 與 vec,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)
複製代碼
兩向量間的運算首先要保證操做數的階數都相同,不然不能計算。例如: vec3*vec2
和 vec4+vec3
等等都是不行的。
它們的計算方式是兩操做數在同位置上的份量分別進行運算,其本質仍是逐份量進行的,這和上面所說的 float 類型的逐份量運算可能有一點點差別,相同的是 vec 與 vec 運算結果仍是 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 與 mat 間只存在乘法運算。 它們的計算方式和線性代數中的矩陣乘法相同,不是逐份量運算。
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 的運算中,除了乘法是線性代數中的矩陣乘法外,其他的運算仍爲逐份量運算。簡單說就是隻有乘法是特殊的,其他都和 vec 與 vec 運算相似。
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);
複製代碼
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);
複製代碼
GLSL
在進行光柵化着色的時候,會產生大量的浮點數運算,這些運算多是當前設備所不能承受的,因此 GLSL
提供了 3 種浮點數精度,咱們能夠根據不一樣的設備來使用合適的精度。 在變量前面加上 highp
、mediump
、lowp
便可完成對該變量的精度聲明:
lowp float color;
varying mediump vec2 Coord;
lowp ivec2 foo(lowp mat3);
highp mat4 m;
複製代碼
咱們通常在 片元着色器(fragment shader) 最開始的地方加上 precision mediump float;
便設定了默認的精度,這樣全部沒有顯式代表精度的變量都會按照設定好的默認精度來處理。
在語法上,GLSL
與 C 很是類似, 也有 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
函數中沒有遞歸!
以上的總結參考了並部分摘抄瞭如下文章,很是感謝如下做者的分享!:
轉載請備註原文出處,不得用於商業傳播——凡幾多