第06章 CG 表達(dá)式與控制語(yǔ)句
讀書(shū)之法,在循序而漸進(jìn)心赶,熟讀而精思邮利。
------ 朱熹
在上一章中弥雹,我們已經(jīng)介紹了 Cg 語(yǔ)言的基礎(chǔ)數(shù)據(jù)類(lèi)型(7種)、內(nèi)置數(shù)據(jù)類(lèi)型延届,以及數(shù)組剪勿、結(jié)構(gòu)、接口等類(lèi)型方庭,本章將在此基礎(chǔ)上討論 Cg 中的表達(dá)式厕吉,表達(dá)式由操作符(operator)關(guān)聯(lián)一個(gè)或多個(gè)操作數(shù)(operand)構(gòu)成,我們首先闡述各種類(lèi)型的操作符械念,并結(jié)合數(shù)據(jù)類(lèi)型講解操作符的具體使用方法头朱。
Cg 中的操作符與 C 語(yǔ)言中的類(lèi)似(操作符的功能和寫(xiě)法與 C 相同,但用法不盡相同)龄减,按照操作符的功能可以劃分為:關(guān)系操作符髓窜、邏輯操作符、條件操作符欺殿。Cg 中有一類(lèi)較為獨(dú)特的操作符寄纵,稱(chēng)為 Swizzle 操作符,這個(gè)操作用于取出向量類(lèi)型變量中的分量脖苏。此外程拭,與 C 語(yǔ)言不同的是,Cg 允許在向量類(lèi)型變量上使用操作符棍潘,例如 > 操作符可以用來(lái)比較兩個(gè)向量各個(gè)分量的大小關(guān)系恃鞋。Cg 中的表達(dá)式還有狠多與 C 語(yǔ)言不通的細(xì)節(jié)之處,將在本章中一一分說(shuō)亦歉。
6.1 關(guān)系操作符(Comparison Operators)
關(guān)系操作符恤浪,用于比較同類(lèi)型數(shù)據(jù)(不同類(lèi)型的基礎(chǔ)數(shù)據(jù)需要進(jìn)行類(lèi)型轉(zhuǎn)換,不同長(zhǎng)度的向量肴楷,不能進(jìn)行比較)之間的大小關(guān)系或者等價(jià)關(guān)系水由。Cg 中有 6 種關(guān)系操作符。如下表所示赛蔫,關(guān)系操作符運(yùn)算后的返回類(lèi)型均為 bool 類(lèi)型砂客。
關(guān)系操作符 | 功能 | 用法 |
---|---|---|
< | 小于 | expr < expr |
<= | 小于或等于 | expr <= expr |
!= | 不等于 | expr != expr |
== | 等于 | expr == expr |
>= | 大于或等于 | expr >= expr |
> | 大于 | expr > expr |
在 Cg 中,由于關(guān)系操作符以及下節(jié)會(huì)講到的邏輯操作符呵恢,都返回 bool 類(lèi)型結(jié)果鞠值,所以這兩種操作符有時(shí)也被統(tǒng)一稱(chēng)為 boolean operator。
Cg 語(yǔ)言表達(dá)式允許對(duì)向量使用所有的 boolean operator渗钉,如果是二元操作符彤恶,則被操作的兩個(gè)向量的長(zhǎng)度必須一致。表達(dá)式中向量的每個(gè)分量都進(jìn)行一對(duì)一的運(yùn)算,最后返回的結(jié)果是一個(gè) bool 類(lèi)型的向量声离,長(zhǎng)度和操作符向量一致歇竟。例如:
float3 a = float4(0.5, 0.0, 1.0);
float3 b = float4(0.6, -0.1, 0.9);
float3 c = a < b;
運(yùn)算后向量 c 的結(jié)果為 float3(true, false, true);
6.2 邏輯操作符(Logical Operators)
Cg 語(yǔ)言中有 3 種邏輯操作符(也被稱(chēng)為 boolean Operators),如下表所示抵恋,邏輯操作符運(yùn)算后的返回類(lèi)型均為 bool 類(lèi)型焕议。
邏輯操作符 | 功能 | 用法 |
---|---|---|
&& | 邏輯與 | expr&&expr |
|| | 邏輯或 | expr||expr |
! | 邏輯非 | !expr |
正如上節(jié)所說(shuō),邏輯操作符也可以對(duì)向量使用弧关,返回的變量類(lèi)型是同樣長(zhǎng)度的內(nèi)置 bool 向量盅安。
有一點(diǎn)需要注意:Cg 中的邏輯與(&&)和邏輯或(||)不存在 C 中的短路現(xiàn)象(short-circuiting,即只用計(jì)算一個(gè)操作數(shù)的 bool 值即可)世囊,而是參與運(yùn)算的操作數(shù)據(jù)都進(jìn)行 bool 分析别瞭。
6.3 數(shù)學(xué)操作符(Math Operators)
Cg 語(yǔ)言對(duì)向量的數(shù)學(xué)操作提供了內(nèi)置的支持,Cg 中的數(shù)學(xué)操作符有:乘法株憾;/除法;-取反蝙寨;+加法;-減法嗤瞎;%求余墙歪;++;一一贝奇;=虹菲;/=;+=掉瞳;-=毕源;后面四中運(yùn)算符有時(shí)被歸納入復(fù)制操作符,不過(guò)他們實(shí)際上進(jìn)行數(shù)學(xué)計(jì)算陕习,然后進(jìn)行賦值霎褐,所以這里也放入數(shù)學(xué)操作符中進(jìn)行說(shuō)明。
在文獻(xiàn)【2】第 3.3 節(jié) Math Expressions 中该镣,其行文意思容易讓人覺(jué)得“好像只有加減法除等運(yùn)算可以對(duì)向量進(jìn)行操作”冻璃,實(shí)際上經(jīng)過(guò)我的測(cè)試,++拌牲、一一等數(shù)學(xué)運(yùn)算發(fā)符同樣可以使用在向量上俱饿。所以“Cg 語(yǔ)言對(duì)向量的數(shù)學(xué)操作提供內(nèi)置支持”這句話是非常準(zhǔn)確的歌粥。
需要注意的是:求余操作符%塌忽。只能在 int 類(lèi)型數(shù)據(jù)間進(jìn)行,否則編譯器會(huì)提示錯(cuò)誤信息:
error C1021:operands to “%” must be integral.
當(dāng)使用這些數(shù)學(xué)操作符對(duì)一個(gè)標(biāo)量和一個(gè)向量進(jìn)行運(yùn)算時(shí)失驶,標(biāo)量首先被復(fù)制到一個(gè)長(zhǎng)度相同的向量中土居,然后進(jìn)行運(yùn)算,例如下面的代碼形式是正確的:
void function()
{
float2 a = float2(1.0, 1.0);
float b = 2.0;
f* = d;
f* = 2.0;
}
6.4 移位操作符
Cg 語(yǔ)言中的移位操作符,功能和 C 語(yǔ)言中的一樣擦耀,也可以作用在向量上棉圈,但是向量類(lèi)型必須是 int 類(lèi)型。例如:
int2 a = int2(0.0, 0.0);
int2 b = a >> 1;
如果使用如下代碼眷蜓,會(huì)出現(xiàn)錯(cuò)誤提示信息:
error C1021:operands to "shr" must be integral.
float2 a = int2(0.0, 0.0);
float2 b = a >> 1;
6.5 Swizzle 操作符
可以使用 Cg 語(yǔ)言中的 Swizzle 操作符(.)將一個(gè)向量的成員取出組成一個(gè)新的向量分瘾。 Swizzle 操作符被 GPU 硬件高效支持。Swizzle 操作符后接x吁系、y德召、z、w汽纤,分別表示原始向量的第一個(gè)上岗、第二個(gè)、第三個(gè)蕴坪、第四個(gè)元素肴掷;Swizzle 操作符后接 r、g背传、b 和 a 的含義與前者等同呆瞻。不過(guò)為了程序的易讀性,建議對(duì)于表示顏色值的向量径玖,使用 Swizzle 操作符后接 r栋烤、g、b 和 a 的方式挺狰。
舉例如下:
float4(a, b, c, d).xyz 等價(jià)于 float3(a, b, c)
float4(a, b, c, d).xyy 等價(jià)于 float3(a, b, b)
float4(a, b, c, d).wzyx 等價(jià)于 float4(d, c, b, a)
float4(a, b, c, d).w 等價(jià)于 float d
值得注意的是明郭,Cg 語(yǔ)言中 float a 和 float1 a 是基本等價(jià)的,兩者可以進(jìn)行類(lèi)型轉(zhuǎn)換丰泊;float薯定、bool、half 等基本類(lèi)型聲明的變量也可以使用 Swizzle 操作符瞳购。例如:
float a = 1.0;
float4 b = a.XXXX;
注意:Swizzle 操作符只能對(duì)結(jié)構(gòu)體和向量使用话侄,不能對(duì)數(shù)組使用,如果對(duì)數(shù)組使用 Swizzle 操作符則會(huì)出現(xiàn)錯(cuò)誤信息:
// 其實(shí)個(gè)人覺(jué)得学赛,提示的錯(cuò)誤信息中 array 換成 vector 更加合適
error C1010:expression left of. "x" is not a struct or array.
要從數(shù)組中取值必須使用[]符號(hào)年堆。例如:
float a[3] = {1.0, 1.0, 0.0};
float b = a[0]; // 正確
float c = a.X; // 編譯會(huì)提示錯(cuò)誤信息
6.6 條件操作符(Conditional Operators)
條件操作符的語(yǔ)法格式為:
expr1?expr2?expr3;
expr1 的計(jì)算結(jié)果為 true 或者 false,如果是 true盏浇,則 expr2 執(zhí)行運(yùn)算变丧,否則 expr3 被計(jì)算。
條件操作符為簡(jiǎn)單的 if-else 語(yǔ)句提供了一種便利的替代方式绢掰,例如我們可以不必寫(xiě):
if(a < 0) {b = a}
else {c = a}
而改寫(xiě)為:
(a < 0)痒蓬?(a < 0):(a < 0)童擎;
Cg 中的條件操作符一個(gè)獨(dú)特的性能是:支持向量運(yùn)算。即, expr1 的計(jì)算結(jié)果可以是 bool 型向量攻晒,expr2 和 expr3 必須是與 expr1 長(zhǎng)度相同的向量顾复。舉例如下:
float3 h = float3(-1.0, 1.0, 1.0);
float3 i = float3(1.0, 0.0, 0.0)鲁捏;
float3 g = float3(1.0, 1.0, 0.0)芯砸;
float3 k;
k = (h < float3(0.0, 0.0, 0.0)) ? (i):(g)给梅;
三元向量 h 與 float3(0.0, 0.0, 0.0)做比較運(yùn)算后結(jié)果為(true,false,false),所以 i 的第一個(gè)數(shù)據(jù)賦值給 k 的第一個(gè)數(shù)據(jù)乙嘀,g 的第二個(gè)和第三個(gè)數(shù)據(jù)賦值給 k 的第二個(gè)和第三個(gè)數(shù)據(jù),k 的值為(1.0,1.0,1.0)破喻。
6.7 操作符優(yōu)先順序
Cg 語(yǔ)言中操作符的優(yōu)先順序如下表所示虎谢,從上到下表示從高級(jí)到低級(jí)的優(yōu)先級(jí);同一行的操作符具有同等優(yōu)先級(jí)曹质。該表參考了 Cg 教程可編程實(shí)時(shí)圖形權(quán)威指南第3.3.1節(jié)婴噩。
操作符 | 結(jié)合律 | 功能 |
---|---|---|
()[]->. | 從左到右 | 函數(shù)調(diào)用、數(shù)組引用羽德、結(jié)構(gòu)引用几莽、成員選擇 |
!~++-+-*&(type)sizeof | 從右到左 | 一元操作符:取反宅静、增加章蚣、減少、正號(hào)姨夹、負(fù)號(hào)纤垂、間接、地址磷账、轉(zhuǎn)換 |
*/% | 從左到右 | 乘法峭沦、除法、余數(shù) |
+- | 從左到右 | 加法逃糟、減法 |
<< >> | 從左到右 | 移位操作符 |
<>=>>= | 從左到右 | 關(guān)系操作符 |
== 吼鱼!= | 從左到右 | 等于、不等 |
& | 從左到右 | 位操作符 |
^ | 從左到右 | 位操作符異或 |
| | 從左到右 | 操作符或 |
&& | 從左到右 | 操作符與 |
|| | 從左到右 | 邏輯或 |
绰咽?: | 從右到左 | 條件表達(dá)式 |
=+=-=*=/=%=&=^=!<<=>>= | 從右到左 | 賦值菇肃、賦值表達(dá)式 |
, | 從左到右 | 逗號(hào)表達(dá)式 |
6.8 控制流語(yǔ)句(Control Flow Statement)
程序最小的獨(dú)立單元是語(yǔ)句(statement)取募,語(yǔ)句一般由分號(hào)結(jié)尾琐谤,缺省情況下,語(yǔ)句是順序執(zhí)行的矛辕,但是當(dāng)涉及邏輯判斷控制時(shí)笑跛,就要求有控制流程序語(yǔ)句付魔×钠罚控制流程序語(yǔ)句分為條件語(yǔ)句和循環(huán)語(yǔ)句飞蹂,在 C 語(yǔ)言中,條件語(yǔ)句有 if翻屈、if-else陈哑、switch 等,而循環(huán)過(guò)程則由 while伸眶、do-while 和 for 語(yǔ)句支持惊窖。Cg 中的控制流語(yǔ)句和循環(huán)語(yǔ)句與 C 語(yǔ)言類(lèi)似:條件語(yǔ)句有:if、if-else厘贼;循環(huán)語(yǔ)句有:while界酒、for。break 語(yǔ)句可以在 for 語(yǔ)句中使用嘴秸。
Cg 語(yǔ)言中的控制流語(yǔ)句要求其中的條件表達(dá)式返回值都是 bool 類(lèi)型毁欣,這一點(diǎn)是與 C 語(yǔ)言不同之處(C 語(yǔ)言中,條件表達(dá)式返回值可以是0岳掐、1)
vs_2_x凭疮,vp30 和 vp40 這些 profile 支持分之指令(又稱(chēng)轉(zhuǎn)移指令,branch instruction)串述,for 和 while 循環(huán)指令在這些 profile 中被完全支持执解。在文獻(xiàn)【3】中提到:
“In other profiles,for and while loops may only be used if the compiler can fully unroll them (that is纲酗,if the compiler can determine the iteration count at compile time)”衰腌。
這句話的意思是“在其他的 profiles 中,for 和 while 循環(huán)只有當(dāng)確切的知道循環(huán)次數(shù)時(shí)此才能被使用”觅赊。但經(jīng)過(guò)試驗(yàn)桶唐,如果使用“在 fp40 和 ps_3_0 之前的”片段 profiles 編譯含義 for,while 語(yǔ)句時(shí)會(huì)出現(xiàn)錯(cuò)誤提示信息:error c6003:instruction limit of exceeded .......茉兰。因此尤泽,如果沒(méi)有確切的把握,不要在低級(jí)的 profiles 中使用循環(huán)控制語(yǔ)句规脸。
同樣坯约,return 只能作為最后一條語(yǔ)句出現(xiàn)。函數(shù)的遞歸調(diào)用(recursion)在 Cg 語(yǔ)言中是被機(jī)制的莫鸭。Switch闹丐、case 和 default 在 Cg 中作為保留關(guān)鍵字存在,但是他們目前不被任何 profiles 所支持被因。