1、前期基礎儲備
筆者以前的四篇文綜述了Android中使用OpenGL ES繪製基本圖形和實現了簡單的相機預覽,初次接觸OpenGL ES開發的讀者可能對其中新的概念比較迷惑,尤爲是其中的頂點着色器(Vertex Shader)和片元着色器(Fragment Shader),咱們知道,在OpenGL中頂點着色器是針對每一個頂點執行一次,用於肯定頂點的位置。片元着色器是針對每一個片元,片元能夠理解爲每一個像素,用於肯定每一個片元(像素)的顏色或者紋理。以下是在相機預覽中使用的兩個着色器的代碼段:java
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"attribute vec2 inputTextureCoordinate;" +
"varying vec2 textureCoordinate;" +
"void main()" +
"{"+
"gl_Position = vPosition;"+
"textureCoordinate = inputTextureCoordinate;" +
"}"; android
private final String fragmentShaderCode =
"#extension GL_OES_EGL_image_external : require\n"+ //相機預覽時的片元指令
"precision mediump float;" + //相機預覽時的片元指令
"varying vec2 textureCoordinate;\n" +
"uniform samplerExternalOES s_texture;\n" + //相機預覽時的片元指令
"void main() {" +
" gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
"}";
兩段着色器中,使用的代碼不是Java代碼,語法也不是Java層面的,而是一種OpenGL特有的語法:GLSL語法編程
GLSL又叫OpenGL着色語言(OpenGL Shading Language),是用來在OpenGL中着色編程的語言,是一種面向過程的語言,基本的語法和C/C++基本相同,他們是在圖形卡的GPU (Graphic Processor Unit圖形處理單元)上執行的,代替了固定的渲染管線的一部分,使渲染管線中不一樣層次具備可編程性。好比:視圖轉換、投影轉換等。GLSL(GL Shading Language)的着色器代碼分紅2個部分:Vertex Shader(頂點着色器)和Fragment Shader(片元着色器)。
在前面的文章中,咱們基本上使用的都是很是簡單的着色器,基本上沒有使用過GLSL的內置函數,可是在後面咱們完成其餘的功能的時候應該就會用到這些內置函數了。好比說相機的各類濾鏡、各類靜態、動態貼紙,其實現少不了特定片元着色器的支持。數組
在筆者的文章《Android使用GPUImage實現濾鏡效果精煉詳解(一)》中經過一些示例代碼實現了調用GPUImage處理靜態圖片,爲其設置各類濾鏡效果,在GitHub上找到GPUImage的開源庫打開以後,就能夠發現,其實現各類濾鏡效果底層依靠的也是OpenGL,每種不一樣濾鏡效果,其片元着色器也是特定的,以黑白濾鏡的片元着色器爲例:函數
public static final String GRAYSCALE_FRAGMENT_SHADER = "" +
"precision highp float;\n" +
"\n" +
"varying vec2 textureCoordinate;\n" +
"\n" +
"uniform sampler2D inputImageTexture;\n" +
"\n" +
"const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);\n" +
"\n" +
"void main()\n" +
"{\n" +
" lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n" +
" float luminance = dot(textureColor.rgb, W);\n" +
"\n" +
" gl_FragColor = vec4(vec3(luminance), textureColor.a);\n" +
"}";
看起來,一頭霧水,實際上該代碼段是由OpenGL的特有語言GLSL構成的,GLSL的變量申明、GLSL語言的內置函數、GLSL的各類修飾符…只要掌握了GLSL語法,那麼後期構造片元着色器會十分的便利,筆者是作相機開發的,工做中建立新濾鏡效果,離不開GPUImage,其原生的片元着色器一部分能夠拿來直接使用,可是更多的都是要在其基礎上作修改的,使之符合本身的濾鏡須要,而在改造過程當中,GLSL語法掌握就更爲迫切了。
注:任何一種OpenGL程序本質上均可以被分爲兩部分:CPU端運行的部分,採用C++、Java之類的語言編寫;以及GPU端運行的部分,使用GLSL語言編寫。ui
2、上代碼,具體實現
(1)基本數據類型
GLSL中的數據類型主要分爲標量、向量、矩陣、採樣器、結構體、數組、空類型七種類型:
1)標量
標量表示的是隻有大小沒有方向的量,在GLSL中標量只有bool、int和float三種。對於int,和C同樣,能夠寫爲十進制、八進制或者十六進制。對於標量的運算,咱們最須要注意的是精度,防止溢出問題。
2)向量
向量咱們能夠看作是數組,在GLSL一般用於儲存顏色、座標等數據,針對維數,可分爲二維、三維和四位向量。針對存儲的標量類型,能夠分爲bool、int和float。共有vec二、vec三、vec4,ivec二、ivec三、ivec四、bvec二、bvec3和bvec4九種類型,數組表明維數、i表示int類型、b表示bool類型。須要注意的是,GLSL中的向量表示豎向量,因此與矩陣相乘進行變換時,矩陣在前,向量在後(與DirectX正好相反)。orm
做爲顏色向量時,用rgba表示份量,就如同取數組的中具體數據的索引值。三維顏色向量就用rgb表示份量。好比對於顏色向量vec4 color,color[0]和color.r都表示color向量的第一個值,也就是紅色的份量。
做爲位置向量時,用xyzw表示份量,xyz分別表示xyz座標,w表示向量的模。三維座標向量爲xyz表示份量,二維向量爲xy表示份量。
做爲紋理向量時,用stpq表示份量,三維用stp表示份量,二維用st表示份量。對象
3)矩陣
在GLSL中矩陣擁有2二、3三、4*4三種類型的矩陣,分別用mat二、mat三、mat4表示。咱們能夠把矩陣看作是一個二維數組,也能夠用二維數組下表的方式取裏面具體位置的值。
4)採樣器
採樣器是專門用來對紋理進行採樣工做的,在GLSL中通常來講,一個採樣器變量表示一副或者一套紋理貼圖。所謂的紋理貼圖能夠理解爲咱們看到的物體上的皮膚。紋理查找須要指定哪一個紋理或者紋理單元將指定查找。索引
sampler1D // 訪問一個一維紋理
sampler2D // 訪問一個二維紋理
sampler3D // 訪問一個三維紋理
samplerCube // 訪問一個立方貼圖紋理
sampler1DShadow // 訪問一個帶對比的一維深度紋理
sampler2DShadow // 訪問一個帶對比的二維深度紋理圖片
uniform sampler2D grass;
vcc2 coord = vec2(100, 100);
vec4 color = texture2D(grass, coord);
5)結構體
和C語言中的結構體相同,用struct來定義結構體,這是惟一的用戶定義類型。
struct light
{
vec3 position;
vec3 color;
};
light ceiling_light;
6)數組
數組知識也和C中相同,不一樣的是數組聲明時能夠不指定大小,可是建議在沒必要要的狀況下,仍是指定大小的好。
// 建立一個10個元素的數組
vec4 points[10];
// 建立一個不指定大小的數組
vec4 points[];
points[2] = vec4(1.0); // points如今大小爲3
points[7] = vec4(2.0); // points如今大小爲8
7)空類型
空類型用void表示,僅用來聲明不返回任何值得函數。
數據聲明示例:
float a=1.5;
int b=2;
bool c=true;
vec2 d=vec2(1.1,2.2);
vec3 e=vec3(1.1,2.2,3.3)
vec4 f=vec4(vec3,1.1);
vec4 g=vec4(1.0); //至關於vec(1.0,1.0,1.0,1.0)
vec4 h=vec4(a,a,1.5,a);
mat2 i=mat2(1.1,1.5,2.2,4.4);
mat2 j=mat2(1.8); //至關於mat2(1.8,1.8,1.8,1.8)
mat3 k=mat3(e,e,1.2,1.4,1.6);
(2)運算符
GLSL中的運算符包括(越靠前,運算優先級越高):
索引:[ ]
前綴自加和自減:++,–-
一元非和邏輯非:~,!
加法和減法:+,-
等於和不等於:==,!=
邏輯異或:^^
三元運算符號,選擇:? :
成員選擇與混合:.
後綴自加和自減:++,–-
乘法和除法:*,/
關係運算符:>,<,=,>=,<=,<>
邏輯與:&&
邏輯或:||
賦值預算:=,+=,-=,*=,/=
(3)類型轉換
GLSL的類型轉換與C不一樣。在GLSL中類型不能夠自動提高,好比float a=1;就是一種錯誤的寫法,必須嚴格的寫成float a=1.0,也不能夠強制轉換,即float a=(float)1; 也是錯誤的寫法,可是能夠用內置函數來進行轉換,如float a=float(1);還有float a=float(true);(true爲1.0,false爲0.0)等,值得注意的是,低精度的int不能轉換爲高精度的float。
(4)限定符
觀察文首的兩段着色器,能夠看到對於變量使用了不一樣的修飾符,如下是GLSL修飾符的主要類型:
attritude:通常用於各個頂點各不相同的量。如頂點顏色、座標等。
uniform:通常用於對於3D物體中全部頂點都相同的量。好比光源位置,統一變換矩陣等。
varying:表示易變量,通常用於頂點着色器傳遞到片元着色器的量。
const:常量。 限定符與java限定符相似,放在變量類型以前,而且只能用於全局變量。在GLSL中,沒有默認限定符一說。
(5)流程控制
GLSL中的流程控制與C中基本相同,主要有條件判斷和各類循環:
if( )、if( )else、if( )else if( )else
while()和dowhile( )
for( ; ; )
break和continue
(6)函數修飾符
GLSL中也能夠定義函數,定義函數的方式也與C語言基本相同。函數的返回值能夠是GLSL中的除了採樣器的任意類型。對於GLSL中函數的參數,能夠用參數用途修飾符來進行修飾,經常使用函數修飾符以下:
in:輸入參數,無修飾符時默認爲此修飾符。
out:輸出參數。
inout:既能夠做爲輸入參數,又能夠做爲輸出參數。
(6)浮點精度
與頂點着色器不一樣的是,在片元着色器中使用浮點型時,必須指定浮點類型的精度,不然編譯會報錯。精度有三種,分別爲:
lowp:低精度。8位。
mediump:中精度。10位。
highp:高精度。16位。
固然,也能夠在片元着色器中設置默認精度,只須要在片元着色器最上面加上precision <精度> <類型>便可制定某種類型的默認精度。其餘狀況相同的話,精度越高,畫質越好,使用的資源也越多。
(7)着色器代碼結構
GLSL程序的結構和C語言差很少,main()方法表示入口函數,能夠在其上定義函數和變量,在main中能夠引用這些變量和函數。定義在函數體之外的叫作全局變量,定義在函數體內的叫作局部變量。與高級語言不一樣的是,變量和函數在使用前必須聲明,不能在使用的後面聲明變量或者函數。
3、GLSL內建變量和內置函數
在上面,咱們瞭解了GLSL的基礎語法:數據類型、運算符、控制流,接下來,咱們要了解另一些關鍵的內容:GLSL爲方便調用,內置了一些變量和函數,在着色器構造中,少不了這些知識的運用。在上面glsl提供了很是豐富的函數庫,供咱們使用,這些功能都是很是有用且會常常用到的. 這些函數按功能區分大改能夠分紅7類:
(1)頂點着色器的內建變量
gl_Position:頂點座標
gl_PointSize:點的大小,沒有賦值則爲默認值1,一般設置繪圖爲點繪製纔有意義。
(2)片元着色器的內建變量
gl_FragCoord:當前片元相對窗口位置所處的座標。
gl_FragFacing:bool型,表示是否爲屬於光柵化生成此片元的對應圖元的正面。 輸出變量:
gl_FragColor:當前片元顏色
gl_FragData:vec4類型的數組。向其寫入的信息,供渲染管線的後繼過程使用。
GLSL提供了很是豐富的函數庫,供咱們使用,這些功能都是很是有用並且常常會使用到的,這些函數按照功能區分大體能夠分爲7類:
(1)通用函數庫
(2)三角函數&角度
(3)指數函數
(4)幾何函數
(5)矩陣函數
(6)向量函數
(7)紋理查詢函數
紋理採樣函數中,3D在OpenGLES2.0並非絕對支持。咱們再次暫時無論3D紋理採樣函數。重點只對texture2D函數進行說明。texture2D擁有三個參數,第一個參數表示紋理採樣器。第二個參數表示紋理座標,能夠是二維、三維、或者四維。第三個參數加入後只能在片元着色器中調用,且只對採樣器爲mipmap類型紋理時有效。
在看完以上的OpenGL內建變量和內置函數以後,在返回前面,查看黑白濾鏡的片元着色器,就會發現,各個變量的聲明和使用均可以看得懂了,GPUImage中每種不一樣類型的濾鏡實現都是採用了相似的方式,感興趣的讀者能夠查看GPUImage的Android庫:android-gpuimage
4、延伸知識,JSON語法
(1)JSON簡介
JSON指的是JavaScript對象表示法(JavaScript Object Notation)
JSON是輕量級的文本交換格式,相似於XML,是純文本
JSON獨立於語言,具備層級結構(值中存在值)
JSON具備自我描述性,更易理解(人類可讀)
JSON使用JavaScript語法來描述數據對象,可是JSON仍然獨立於語言和平臺。
(2)相比於XML的不一樣之處
JSON比XML更小、更快、更易解析
沒有結束標籤
更短,讀寫速度更快
使用數組
不使用保留字
(3)JSON語法規則
JSON語法是JavaScript對象表示語法的子集
數據在 名稱/值 對中
數據由逗號隔開
花括號保存對象
方括號保存數組
(4)JSON 名稱/值 對
JSON 數據的書寫格式是:名稱/值對。
名稱/值對包括:字段名稱(在雙引號中),後面寫一個冒號,而後是值:
「firstName」 : 「John」
(5)JSON值
JSON 值能夠是:
數字(整數或浮點數)
字符串(在雙引號中)
邏輯值(true 或 false)
數組(在方括號中)
對象(在花括號中)
Null
(6)JSON對象
JSON 對象在花括號中書寫:對象能夠包含多個名稱/值對:
{ 「firstName」:」John」 , 「lastName」:」Doe」 }
(7)JSON 數組
JSON 數組在方括號中書寫:數組可包含多個對象:
{
「employees」: [
{ 「firstName」:」John」 , 「lastName」:」Doe」 },
{ 「firstName」:」Anna」 , 「lastName」:」Smith」 },
{ 「firstName」:」Peter」 , 「lastName」:」Jones」 }
]
}
相關語法能夠搜索 GLSL 中文手冊 獲取更多的語法知識