[译]Unity3D Shader教程(二)HLSL
目錄
- 1 HLSL?
- 2 Builtin Types
- 3 Vector Values
- 4 Matrix Values
- 5 Textures
- 6 Math
- 7 Custom Types
- 8 Variables
- 9 Functions
- 10 Control Flow
- 10.1 if statements
- 10.2 Loops
原文地址
Shader Tutorials by Ronja。
1 HLSL?
HLSL(High Level Shading Language,高級著色器語言)是Unity用來編寫著色器的語言,其用來自定義邏輯并最終決定在屏幕上繪制什么內(nèi)容。 HLSL是微軟設(shè)計(jì)開發(fā)的,供Direct3D API使用的GPU語言。 嚴(yán)格來說,大多數(shù) Unity Shader都是用 CG 編寫。Cg是C for Graphics的縮寫,Cg與 HLSL 共享大部分語法和功能,并在 2012 年被棄用,因此錯誤地將Unity Shader中的語言稱為HLSL能幫助您在搜索引擎中找到更想要的結(jié)果。 理論上Unity 也支持編寫GLSL(OpenGL Shading Language)著色器,這是為 OpenGL 設(shè)計(jì)的語言。因?yàn)镠LSL有更多示例,并且Unity最終會將Shader將轉(zhuǎn)換為導(dǎo)出平臺的對應(yīng)語言,所以Unity中,我們堅(jiān)持使用HLSL來編寫Shader。
(其實(shí)上面這段沒怎么說清楚為什么咱們用的是HLSL語言來編寫Unity Shader。這里來解釋一下,編寫Shader的語言無非就三種,一是微軟平臺的HLSL語言,二是OpenGL平臺的GLSL語言,三是NVIDA的Cg語言。Cg語言是真正意義上的跨平臺著色器語言,即用Cg編寫的語言既可以在微軟的Direct3D上跑也可以在Open Gl上跑。但是由于Cg和DX9風(fēng)格的HLSL從寫法上說幾乎是同一種語言,所以在Unity中HLSL和Cg是等價的。我們一般在CGPROGRAM和ENDCG代碼片段中編寫。)
為了在 Unity 中學(xué)習(xí)著色器,我們建議您不要從這里開始您的編程之旅。著色器調(diào)試起來很麻煩,它們可以做的事情非常有限,而且在許多情況下,您必須朝著與大多數(shù)編程上下文略有不同的方向進(jìn)行處理。 所以我們假設(shè)你知道類型、變量、類、方法、循環(huán)、if 語句等等的基本編程知識。
2 Builtin Types
內(nèi)置類型。
fixed、half、float表示浮點(diǎn)數(shù),int表示有符號的整型,uint表示無符號的整型。
在移動端的GPU 上,fixed的范圍為-2~2之間的數(shù)字,精度為 1/256,half是 16 位浮點(diǎn)數(shù),float是 32 位浮點(diǎn)數(shù)。 在PC端GPU 上,所有這些值都是 32 位浮點(diǎn)數(shù),所以為了方便我們會到處使用float,但根據(jù)數(shù)據(jù)的大小選擇對應(yīng)的類型也是一種優(yōu)化。
整型是只能包含整數(shù)值的數(shù)字,int 可以包含正值和負(fù)值,uint 只能包含正值,使用uint可以帶來輕微的性能優(yōu)勢。
此外,還有一個簡單的數(shù)據(jù)類型 bool(布爾值),它保存的值為真或假,因此我們可以將檢查結(jié)果存儲在其中。 如果您將布爾值與其他數(shù)字一起使用(例如將它們相加或相乘),它們的行為就像數(shù)字 0(如果它為假)或 1(如果它為真)。
3 Vector Values
向量。
我們可以在第2節(jié)中的內(nèi)置類型后面添加一個數(shù)字(最大為4)來組成一個多維向量,如fixed4, float2 或 half3。我們向量來表示紋理坐標(biāo)、顏色和位置。
當(dāng)我們想訪問向量中的某一個值時,如果此向量表示位置,我們依次使用 vector.x、vector.y、vector.z 和 vector.w;當(dāng)向量表示顏色時 ,我們使用vector.r、vector.g、vector.b 和 vector .a?;蛘呶覀円部梢赃@樣:vector[2]([]中的2是索引值,是從0開始的,因此4維向量的 4 個值將是 0、1、2 和 3)。
如果我們想從一個向量中獲取多個值并將它們放入另一個向量中,我們不必自己構(gòu)造那個新向量,我們可以使用 Swizzling。 Swizzling 意味著在點(diǎn)之后寫入多個向量子值。 如:
vector.xy - 僅獲取向量的前 2 個值并將它們放入二維向量中。
vector.zyx - 獲取向量的前 3 個值并顛倒它們的順序。
vector.xxxx - 取向量的第一個值并構(gòu)建一個全部是該值的 4維向量。
(Swizzling能夠提高性能)
4 Matrix Values
矩陣。
像向量在一個方向上擴(kuò)展基本類型一樣,矩陣在兩個方向上擴(kuò)展它們。 它們的語法是將 number x number 附加到標(biāo)量類型上。 如 float4x4、half3x2 甚至 bool2x4??梢酝ㄟ^像 matrix[3][2] 這樣的方括號來訪問矩陣的成員,第一個數(shù)字表示行,第二個數(shù)字表示列。 或者通過像 _m32 這樣的訪問器。 這鐘訪問器也可以用于 swizzling,如 matrix._m03_m13_m23 以獲取最后一列的前 3 個值并將它們寫入 3維 向量。 如果我們使用方括號版本并且只使用一對括號,我們將得到定義該行的向量,如matrix[3]表示第三行。
幸運(yùn)的是,我們幾乎從不需要訪問矩陣中的值,所以沒必要完全掌握它,如果某天你需要使用到矩陣鐘的某個元素,再回過頭來看看即可。
5 Textures
紋理。
Hlsl 也有描述紋理的類型。我們后面再講。
6 Math
算數(shù)運(yùn)算。
除了簡單數(shù)學(xué)中的+ - * / 運(yùn)算符和 邏輯運(yùn)算符> < == != ! >= <= && 、|| ,hlsl 還內(nèi)置了許多數(shù)學(xué)函數(shù),如 abs、dot、lerp、pow、min、atan2 (支持的函數(shù)可以見這里)。
還有一些簡寫,如 += *= -= 和 /= ,它們修改一個變量,然后再次賦值給它自己,還有var++ 和 var- -,其從給定的變量中加或減 1。
標(biāo)量類型和向量類型的乘法返回一個向量類型,它們的維數(shù)都與數(shù)字相乘。 所以 float2(2, 7) * 3 等于 float2(6, 21)。
另外,我們我們使用mul函數(shù)來實(shí)現(xiàn)矩陣與向量相乘以變換向量。矩陣和向量是具體是怎么計(jì)算的,這里不細(xì)說。
7 Custom Types
自定義類型。
除了內(nèi)置類型,我們還可以添加我們自己的類型。 添加你自定義類型的語法是這樣的(最后的分號很重要!):
理論上我們也可以使用 class 關(guān)鍵字,使用繼承、成員函數(shù)甚至接口,但我從未在任何著色器中遇到過它們,所以我不會在這里解釋,等它們出現(xiàn)時再說。 如果您確實(shí)想使用這些功能,則語法類似于 C++/C#。
與向量類型一樣,我們使用點(diǎn)來訪問自定義類型的成員變量,例如 instance.variable 或 instance.otherVariable.x。
8 Variables
變量。
hlsl 中的所有數(shù)據(jù)類型都是值類型。 這意味著一旦我們擁有一個值,我們就可以更改它,而無需通過 new 或類似的方式創(chuàng)建它。
如果我們想創(chuàng)建向量類型,我們可以通過調(diào)用函數(shù)之類的類型來實(shí)現(xiàn)。 在這些情況下,參數(shù)類型中的子值數(shù)量之和必須與目標(biāo)類型中的值數(shù)量相等。比如我們可以通過傳入 4 個float或2 個float 2或一個float、一個float2 和另一個float或者僅一個float4來構(gòu)建一個新的float4。 如我們可以這樣構(gòu)建我們自定義類型的變量:
我們聲明的變量可以在函數(shù)內(nèi)部,在這種情況下,它們只能被該函數(shù)內(nèi)部的其他部分訪問,這些部分需晚于變量聲明(就像在大多數(shù)編程語言中一樣);也可以在函數(shù)之外,在這種情況下,它們可以被 從著色器中的所有函數(shù)訪問,無論變量聲明在哪兒(但通常在頂部聲明它們,在函數(shù)上方以輕松找到它們)。
9 Functions
函數(shù)。(C#中稱為方法)
hlsl 中的大多數(shù)函數(shù)都在全局范圍內(nèi)。 這意味著它們不屬于任何數(shù)據(jù)類型,我們可以從任何地方調(diào)用它們。 它們可以接受多個(或沒有)參數(shù)并返回一個值。 如果您的函數(shù)沒有返回值,則必須將返回類型聲明為 void。 典型的函數(shù)語法如下所示:
要調(diào)用一個函數(shù),我們只需寫下函數(shù)名,后跟方括號和后綴中的參數(shù)。 如果有多個具有相同名稱但參數(shù)類型不同的函數(shù),hlsl 將自動找到適合我們調(diào)用它的參數(shù)的函數(shù)。如functionName(arg1, arg2)。
10 Control Flow
流程控制。
對于許多著色器來說,一個接一個執(zhí)行一個命令就足夠了,而不會遺漏或重復(fù)任何一個命令。 對于他們中的很多人來說,在兩種代碼路徑之間進(jìn)行選擇也很重要。 眾所周知,在著色器中使用控制流是有害的,尤其是在移動 GPU 上會影響您的性能,您應(yīng)該使用諸如 step 之類的函數(shù)。 但是這種方法是完全錯誤的,因?yàn)閟tep這些函數(shù)在它們內(nèi)部其實(shí)也是使用了分支,使用它們反而會使你的代碼更復(fù)雜和閱讀。如果使用if能使你的代碼更整潔,那么就可以使用它。
(“step不能提高效率”這觀點(diǎn)我持保留意見,等考證后再說)
10.1 if statements
if語句。
如下面的例子,如果condition為真,則執(zhí)行 do thing 塊,否則執(zhí)行 do other thing 塊,兩個代碼塊永遠(yuǎn)不會同時執(zhí)行。
其中, else是可選的。 條件周圍的括號是強(qiáng)制性的。 如果你不使用花括號,該語句只會影響下一行(直到下一個分號,而不是下一個換行符),而不是之后的行。 條件可以是布爾值、數(shù)字(在這種情況下 0 為假,任何其他值包括負(fù)數(shù)為真)或返回兩者之一的操作。 如果你有一個與你想要的相反的值(當(dāng)你想執(zhí)行一些代碼時為假,否則為真),只需在它前面加上一個感嘆號,就會翻轉(zhuǎn):真值變?yōu)榧?#xff0c;假值變?yōu)檎妗H?flag。
10.2 Loops
循環(huán)。
控制哪些代碼被執(zhí)行,哪些不執(zhí)行的另一種方式是循環(huán)。 While 循環(huán)是一種更簡單的循環(huán)。 只要定義它們的條件不再為真,它們就會永久執(zhí)行。 如果一開始就不是真的,它們根本不會被執(zhí)行。 它們看起來像這樣:
重要的是,在 do things 中的某個地方您要講條件改為true,否則循環(huán)while將永遠(yuǎn)運(yùn)行,這很糟糕,甚至可能使您的整個編輯器崩潰。
另一種循環(huán)是 for 循環(huán),它添加了一些語法糖來迭代和計(jì)數(shù),它們是這樣定義的。
我們偶爾會遍歷某個數(shù)組,變量索引從 0 到 maxValue-1 ,不那么令人困惑的版本如下:
for(uint index=0;index<maxValue;index++){//do things }此代碼等效于使用 while 循環(huán)的代碼:
uint index = 0; while(index < maxValue){//do thingsindex++; }兩個循環(huán)也支持關(guān)鍵字 break 和 continue。
在您的代碼中使用 break 語句會使代碼跳轉(zhuǎn)到下一個循環(huán)的末尾。 這也將跳出無限的 while 循環(huán)。
使用 continue 語句會使循環(huán)跳轉(zhuǎn)到循環(huán)的下一次迭代的開始。
博主本文鏈接。
總結(jié)
以上是生活随笔為你收集整理的[译]Unity3D Shader教程(二)HLSL的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统框图整理
- 下一篇: Visual Studio开发环境下的中