在Android中使用OpenGL ES开发第(五)节:GLSL基础语法
一、前期基礎(chǔ)儲(chǔ)備
筆者之前的四篇文綜述了Android中使用OpenGL ES繪制基本圖形和實(shí)現(xiàn)了簡單的相機(jī)預(yù)覽,初次接觸OpenGL ES開發(fā)的讀者可能對其中新的概念比較迷惑,尤其是其中的頂點(diǎn)著色器(Vertex Shader)和片元著色器(Fragment Shader),我們知道,在OpenGL中頂點(diǎn)著色器是針對每個(gè)頂點(diǎn)執(zhí)行一次,用于確定頂點(diǎn)的位置。片元著色器是針對每個(gè)片元,片元可以理解為每個(gè)像素,用于確定每個(gè)片元(像素)的顏色或者紋理。如下是在相機(jī)預(yù)覽中使用的兩個(gè)著色器的代碼段:
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"attribute vec2 inputTextureCoordinate;" +
"varying vec2 textureCoordinate;" +
"void main()" +
"{"+
"gl_Position = vPosition;"+
"textureCoordinate = inputTextureCoordinate;" +
"}";
private final String fragmentShaderCode =
"#extension GL_OES_EGL_image_external : require\n"+ //相機(jī)預(yù)覽時(shí)的片元指令
"precision mediump float;" + //相機(jī)預(yù)覽時(shí)的片元指令
"varying vec2 textureCoordinate;\n" +
"uniform samplerExternalOES s_texture;\n" + //相機(jī)預(yù)覽時(shí)的片元指令
"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圖形處理單元)上執(zhí)行的,代替了固定的渲染管線的一部分,使渲染管線中不同層次具有可編程性。比如:視圖轉(zhuǎn)換、投影轉(zhuǎn)換等。GLSL(GL Shading Language)的著色器代碼分成2個(gè)部分:Vertex Shader(頂點(diǎn)著色器)和Fragment Shader(片元著色器)。
在前面的文章中,我們基本上使用的都是非常簡單的著色器,基本上沒有使用過GLSL的內(nèi)置函數(shù),但是在后面我們完成其他的功能的時(shí)候應(yīng)該就會(huì)用到這些內(nèi)置函數(shù)了。比如說相機(jī)的各種濾鏡、各種靜態(tài)、動(dòng)態(tài)貼紙,其實(shí)現(xiàn)少不了特定片元著色器的支持。
在筆者的文章《Android使用GPUImage實(shí)現(xiàn)濾鏡效果精煉詳解(一)》中通過一些示例代碼實(shí)現(xiàn)了調(diào)用GPUImage處理靜態(tài)圖片,為其設(shè)置各種濾鏡效果,在GitHub上找到GPUImage的開源庫打開之后,就可以發(fā)現(xiàn),其實(shí)現(xiàn)各種濾鏡效果底層依靠的也是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" +
"}";
看起來,一頭霧水,實(shí)際上該代碼段是由OpenGL的特有語言GLSL構(gòu)成的,GLSL的變量申明、GLSL語言的內(nèi)置函數(shù)、GLSL的各種修飾符…只要掌握了GLSL語法,那么后期構(gòu)造片元著色器會(huì)十分的便利,筆者是做相機(jī)開發(fā)的,工作中創(chuàng)建新濾鏡效果,離不開GPUImage,其原生的片元著色器一部分可以拿來直接使用,但是更多的都是要在其基礎(chǔ)上做修改的,使之符合自己的濾鏡需要,而在改造過程中,GLSL語法掌握就更為迫切了。
注:任何一種OpenGL程序本質(zhì)上都可以被分為兩部分:CPU端運(yùn)行的部分,采用C++、Java之類的語言編寫;以及GPU端運(yùn)行的部分,使用GLSL語言編寫。
二、上代碼,具體實(shí)現(xiàn)
(1)基本數(shù)據(jù)類型
GLSL中的數(shù)據(jù)類型主要分為標(biāo)量、向量、矩陣、采樣器、結(jié)構(gòu)體、數(shù)組、空類型七種類型:
1)標(biāo)量
標(biāo)量表示的是只有大小沒有方向的量,在GLSL中標(biāo)量只有bool、int和float三種。對于int,和C一樣,可以寫為十進(jìn)制、八進(jìn)制或者十六進(jìn)制。對于標(biāo)量的運(yùn)算,我們最需要注意的是精度,防止溢出問題。
2)向量
向量我們可以看做是數(shù)組,在GLSL通常用于儲(chǔ)存顏色、坐標(biāo)等數(shù)據(jù),針對維數(shù),可分為二維、三維和四位向量。針對存儲(chǔ)的標(biāo)量類型,可以分為bool、int和float。共有vec2、vec3、vec4,ivec2、ivec3、ivec4、bvec2、bvec3和bvec4九種類型,數(shù)組代表維數(shù)、i表示int類型、b表示bool類型。需要注意的是,GLSL中的向量表示豎向量,所以與矩陣相乘進(jìn)行變換時(shí),矩陣在前,向量在后(與DirectX正好相反)。
作為顏色向量時(shí),用rgba表示分量,就如同取數(shù)組的中具體數(shù)據(jù)的索引值。三維顏色向量就用rgb表示分量。比如對于顏色向量vec4 color,color[0]和color.r都表示color向量的第一個(gè)值,也就是紅色的分量。
作為位置向量時(shí),用xyzw表示分量,xyz分別表示xyz坐標(biāo),w表示向量的模。三維坐標(biāo)向量為xyz表示分量,二維向量為xy表示分量。
作為紋理向量時(shí),用stpq表示分量,三維用stp表示分量,二維用st表示分量。
3)矩陣
在GLSL中矩陣擁有22、33、4*4三種類型的矩陣,分別用mat2、mat3、mat4表示。我們可以把矩陣看做是一個(gè)二維數(shù)組,也可以用二維數(shù)組下表的方式取里面具體位置的值。
4)采樣器
采樣器是專門用來對紋理進(jìn)行采樣工作的,在GLSL中一般來說,一個(gè)采樣器變量表示一副或者一套紋理貼圖。所謂的紋理貼圖可以理解為我們看到的物體上的皮膚。紋理查找需要指定哪個(gè)紋理或者紋理單元將指定查找。
sampler1D // 訪問一個(gè)一維紋理
sampler2D // 訪問一個(gè)二維紋理
sampler3D // 訪問一個(gè)三維紋理
samplerCube // 訪問一個(gè)立方貼圖紋理
sampler1DShadow // 訪問一個(gè)帶對比的一維深度紋理
sampler2DShadow // 訪問一個(gè)帶對比的二維深度紋理
uniform sampler2D grass;
vcc2 coord = vec2(100, 100);
vec4 color = texture2D(grass, coord);
5)結(jié)構(gòu)體
和C語言中的結(jié)構(gòu)體相同,用struct來定義結(jié)構(gòu)體,這是唯一的用戶定義類型。
struct light
{
vec3 position;
vec3 color;
};
light ceiling_light;
6)數(shù)組
數(shù)組知識也和C中相同,不同的是數(shù)組聲明時(shí)可以不指定大小,但是建議在不必要的情況下,還是指定大小的好。
// 創(chuàng)建一個(gè)10個(gè)元素的數(shù)組
vec4 points[10];
// 創(chuàng)建一個(gè)不指定大小的數(shù)組
vec4 points[];
points[2] = vec4(1.0); // points現(xiàn)在大小為3
points[7] = vec4(2.0); // points現(xiàn)在大小為8
7)空類型
空類型用void表示,僅用來聲明不返回任何值得函數(shù)。
數(shù)據(jù)聲明示例:
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); //相當(dāng)于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); //相當(dāng)于mat2(1.8,1.8,1.8,1.8)
mat3 k=mat3(e,e,1.2,1.4,1.6);
(2)運(yùn)算符
GLSL中的運(yùn)算符包括(越靠前,運(yùn)算優(yōu)先級越高):
索引:[ ]
前綴自加和自減:++,–-
一元非和邏輯非:~,!
加法和減法:+,-
等于和不等于:==,!=
邏輯異或:^^
三元運(yùn)算符號,選擇:? :
成員選擇與混合:.
后綴自加和自減:++,–-
乘法和除法:*,/
關(guān)系運(yùn)算符:>,<,=,>=,<=,<>
邏輯與:&&
邏輯或:||
賦值預(yù)算:=,+=,-=,*=,/=
(3)類型轉(zhuǎn)換
GLSL的類型轉(zhuǎn)換與C不同。在GLSL中類型不可以自動(dòng)提升,比如float a=1;就是一種錯(cuò)誤的寫法,必須嚴(yán)格的寫成float a=1.0,也不可以強(qiáng)制轉(zhuǎn)換,即float a=(float)1; 也是錯(cuò)誤的寫法,但是可以用內(nèi)置函數(shù)來進(jìn)行轉(zhuǎn)換,如float a=float(1);還有float a=float(true);(true為1.0,false為0.0)等,值得注意的是,低精度的int不能轉(zhuǎn)換為高精度的float。
(4)限定符
觀察文首的兩段著色器,可以看到對于變量使用了不同的修飾符,以下是GLSL修飾符的主要類型:
attritude:一般用于各個(gè)頂點(diǎn)各不相同的量。如頂點(diǎn)顏色、坐標(biāo)等。
uniform:一般用于對于3D物體中所有頂點(diǎn)都相同的量。比如光源位置,統(tǒng)一變換矩陣等。
varying:表示易變量,一般用于頂點(diǎn)著色器傳遞到片元著色器的量。
const:常量。 限定符與java限定符類似,放在變量類型之前,并且只能用于全局變量。在GLSL中,沒有默認(rèn)限定符一說。
(5)流程控制
GLSL中的流程控制與C中基本相同,主要有條件判斷和各種循環(huán):
if( )、if( )else、if( )else if( )else
while()和dowhile( )
for( ; ; )
break和continue
(6)函數(shù)修飾符
GLSL中也可以定義函數(shù),定義函數(shù)的方式也與C語言基本相同。函數(shù)的返回值可以是GLSL中的除了采樣器的任意類型。對于GLSL中函數(shù)的參數(shù),可以用參數(shù)用途修飾符來進(jìn)行修飾,常用函數(shù)修飾符如下:
in:輸入?yún)?shù),無修飾符時(shí)默認(rèn)為此修飾符。
out:輸出參數(shù)。
inout:既可以作為輸入?yún)?shù),又可以作為輸出參數(shù)。
(6)浮點(diǎn)精度
與頂點(diǎn)著色器不同的是,在片元著色器中使用浮點(diǎn)型時(shí),必須指定浮點(diǎn)類型的精度,否則編譯會(huì)報(bào)錯(cuò)。精度有三種,分別為:
lowp:低精度。8位。
mediump:中精度。10位。
highp:高精度。16位。
當(dāng)然,也可以在片元著色器中設(shè)置默認(rèn)精度,只需要在片元著色器最上面加上precision <精度> <類型>即可制定某種類型的默認(rèn)精度。其他情況相同的話,精度越高,畫質(zhì)越好,使用的資源也越多。
(7)著色器代碼結(jié)構(gòu)
GLSL程序的結(jié)構(gòu)和C語言差不多,main()方法表示入口函數(shù),可以在其上定義函數(shù)和變量,在main中可以引用這些變量和函數(shù)。定義在函數(shù)體以外的叫做全局變量,定義在函數(shù)體內(nèi)的叫做局部變量。與高級語言不同的是,變量和函數(shù)在使用前必須聲明,不能在使用的后面聲明變量或者函數(shù)。
三、GLSL內(nèi)建變量和內(nèi)置函數(shù)
在上面,我們了解了GLSL的基礎(chǔ)語法:數(shù)據(jù)類型、運(yùn)算符、控制流,接下來,我們要了解另外一些關(guān)鍵的內(nèi)容:GLSL為方便調(diào)用,內(nèi)置了一些變量和函數(shù),在著色器構(gòu)造中,少不了這些知識的運(yùn)用。在上面glsl提供了非常豐富的函數(shù)庫,供我們使用,這些功能都是非常有用且會(huì)經(jīng)常用到的. 這些函數(shù)按功能區(qū)分大改可以分成7類:
(1)頂點(diǎn)著色器的內(nèi)建變量
gl_Position:頂點(diǎn)坐標(biāo)
gl_PointSize:點(diǎn)的大小,沒有賦值則為默認(rèn)值1,通常設(shè)置繪圖為點(diǎn)繪制才有意義。
(2)片元著色器的內(nèi)建變量
gl_FragCoord:當(dāng)前片元相對窗口位置所處的坐標(biāo)。
gl_FragFacing:bool型,表示是否為屬于光柵化生成此片元的對應(yīng)圖元的正面。 輸出變量:
gl_FragColor:當(dāng)前片元顏色
gl_FragData:vec4類型的數(shù)組。向其寫入的信息,供渲染管線的后繼過程使用。
GLSL提供了非常豐富的函數(shù)庫,供我們使用,這些功能都是非常有用而且經(jīng)常會(huì)使用到的,這些函數(shù)按照功能區(qū)分大致可以分為7類:
(1)通用函數(shù)庫
(2)三角函數(shù)&角度
(3)指數(shù)函數(shù)
(4)幾何函數(shù)
(5)矩陣函數(shù)
(6)向量函數(shù)
(7)紋理查詢函數(shù)
紋理采樣函數(shù)中,3D在OpenGLES2.0并不是絕對支持。我們再次暫時(shí)不管3D紋理采樣函數(shù)。重點(diǎn)只對texture2D函數(shù)進(jìn)行說明。texture2D擁有三個(gè)參數(shù),第一個(gè)參數(shù)表示紋理采樣器。第二個(gè)參數(shù)表示紋理坐標(biāo),可以是二維、三維、或者四維。第三個(gè)參數(shù)加入后只能在片元著色器中調(diào)用,且只對采樣器為mipmap類型紋理時(shí)有效。
在看完以上的OpenGL內(nèi)建變量和內(nèi)置函數(shù)之后,在返回前面,查看黑白濾鏡的片元著色器,就會(huì)發(fā)現(xiàn),各個(gè)變量的聲明和使用都可以看得懂了,GPUImage中每種不同類型的濾鏡實(shí)現(xiàn)都是采用了類似的方式,感興趣的讀者可以查看GPUImage的Android庫:android-gpuimage
四、延伸知識,JSON語法
(1)JSON簡介
? JSON指的是JavaScript對象表示法(JavaScript Object Notation)
? JSON是輕量級的文本交換格式,類似于XML,是純文本
? JSON獨(dú)立于語言,具有層級結(jié)構(gòu)(值中存在值)
? JSON具有自我描述性,更易理解(人類可讀)
JSON使用JavaScript語法來描述數(shù)據(jù)對象,但是JSON仍然獨(dú)立于語言和平臺。
(2)相比于XML的不同之處
JSON比XML更小、更快、更易解析
沒有結(jié)束標(biāo)簽
更短,讀寫速度更快
使用數(shù)組
不使用保留字
(3)JSON語法規(guī)則
JSON語法是JavaScript對象表示語法的子集
數(shù)據(jù)在 名稱/值 對中
數(shù)據(jù)由逗號隔開
花括號保存對象
方括號保存數(shù)組
(4)JSON 名稱/值 對
JSON 數(shù)據(jù)的書寫格式是:名稱/值對。
名稱/值對包括:字段名稱(在雙引號中),后面寫一個(gè)冒號,然后是值:
“firstName” : “John”
(5)JSON值
JSON 值可以是:
數(shù)字(整數(shù)或浮點(diǎn)數(shù))
字符串(在雙引號中)
邏輯值(true 或 false)
數(shù)組(在方括號中)
對象(在花括號中)
Null
(6)JSON對象
JSON 對象在花括號中書寫:對象可以包含多個(gè)名稱/值對:
{ “firstName”:”John” , “l(fā)astName”:”Doe” }
(7)JSON 數(shù)組
JSON 數(shù)組在方括號中書寫:數(shù)組可包含多個(gè)對象:
{
“employees”: [
{ “firstName”:”John” , “l(fā)astName”:”Doe” },
{ “firstName”:”Anna” , “l(fā)astName”:”Smith” },
{ “firstName”:”Peter” , “l(fā)astName”:”Jones” }
]
}
相關(guān)語法可以搜索 GLSL 中文手冊 獲取更多的語法知識
轉(zhuǎn)載于:https://www.cnblogs.com/laughingQing/p/11058557.html
總結(jié)
以上是生活随笔為你收集整理的在Android中使用OpenGL ES开发第(五)节:GLSL基础语法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是软件测试架构师?
- 下一篇: 04 Websocket和Websock