運算子
表達式是由運算符標點符號的變數和常值序列。 運算符會決定變數和常值如何結合、比較、選取等等。 運算子包括:
操作員名稱 | 操作員 |
---|---|
加法和乘法運算符 | +, -, *, /, % |
陣列運算子 | [i] |
指派運算子 | =, +=, -=, *=, /=, %= |
二進位轉換 | 適用於 float 和 int 的 C 規則、C 規則或 bool 的 HLSL 內部函數 |
位元運算子 | ~、、 <<、、 >>|、^、 <<=、 >>=、|=、|=、^= |
布爾數學運算符 | & &, ||, ?: |
轉換運算元 | (類型) |
逗號運算子 | , |
比較運算子 | <、 >、==、!=、=、 <=、 >= |
前置詞或後置運算符 | ++, -- |
結構運算元 | . |
一元運算子 | !, -, + |
許多運算子都是個別元件,這表示作業會針對每個變數的每個元件獨立執行。 例如,單一元件變數已執行一個作業。 另一方面,四個元件變數有四個作業執行,每個元件各執行一個作業。
對值執行某些動作的所有運算符,例如 + 和 *,每個元件都會運作。
比較運算子需要單一元件才能運作,除非您使用all或任何內建函式搭配多元件變數。 下列作業失敗,因為 if 語句需要單一 bool,但會收到 bool4:
if (A4 < B4)
下列作業成功:
if ( any(A4 < B4) )
if ( all(A4 < B4) )
二進位轉換運算符 asfloat、asint 等每個元件的工作,除了記錄特殊規則的雙倍。
選取運算子,例如句號、逗號和數位括弧,無法依每個元件運作。
轉換運算子會變更元件數目。 下列轉換作業會顯示其等價:
(float) i4 -> float(i4.x)
(float4)i -> float4(i, i, i, i)
加法和乘法運算符
加法和乘法運算子包括:+、-、*、/、%
int i1 = 1;
int i2 = 2;
int i3 = i1 + i2; // i3 = 3
i3 = i1 * i2; // i3 = 1 * 2 = 2
i3 = i1/i2; // i3 = 1/3 = 0.333. Truncated to 0 because i3 is an integer.
i3 = i2/i1; // i3 = 2/1 = 2
float f1 = 1.0;
float f2 = 2.0f;
float f3 = f1 - f2; // f3 = 1.0 - 2.0 = -1.0
f3 = f1 * f2; // f3 = 1.0 * 2.0 = 2.0
f3 = f1/f2; // f3 = 1.0/2.0 = 0.5
f3 = f2/f1; // f3 = 2.0/1.0 = 2.0
模數運算符會傳回除法的餘數。 使用整數和浮點數時,這會產生不同的結果。 小數的整數餘數將會被截斷。
int i1 = 1;
int i2 = 2;
i3 = i1 % i2; // i3 = remainder of 1/2, which is 1
i3 = i2 % i1; // i3 = remainder of 2/1, which is 0
i3 = 5 % 2; // i3 = remainder of 5/2, which is 1
i3 = 9 % 2; // i3 = remainder of 9/2, which is 1
使用整數時,模數運算符會截斷小數餘數。
f3 = f1 % f2; // f3 = remainder of 1.0/2.0, which is 0.5
f3 = f2 % f1; // f3 = remainder of 2.0/1.0, which is 0.0
只有在兩端都是正數或兩端為負數的情況下,才會定義 % 運算子。 不同於 C,它也會在浮點數據類型以及整數上運作。
陣列運算子
數位成員選取運算子 「[i]」 會選取數位中的一或多個元件。 它是一組方括弧,其中包含以零起始的索引。
int arrayOfInts[4] = { 0,1,2,3 };
arrayOfInts[0] = 2;
arrayOfInts[1] = arrayOfInts[0];
數位運算子也可以用來存取向量。
float4 4D_Vector = { 0.0f, 1.0f, 2.0f, 3.0f };
float 1DFloat = 4D_Vector[1]; // 1.0f
藉由新增額外的索引,數位運算元也可以存取矩陣。
float4x4 mat4x4 = {{0,0,0,0}, {1,1,1,1}, {2,2,2,2}, {3,3,3,3} };
mat4x4[0][1] = 1.1f;
float 1DFloat = mat4x4[0][1]; // 0.0f
第一個索引是以零起始的數據列索引。 第二個索引是以零起始的數據行索引。
指派運算子
指派運算子包括:=、+=、-=、*=、/=
變數可以指派常值:
int i = 1;
float f2 = 3.1f;
bool b = false;
string str = "string";
變數也可以指派數學運算的結果:
int i1 = 1;
i1 += 2; // i1 = 1 + 2 = 3
變數可以在等號的任一端使用:
float f3 = 0.5f;
f3 *= f3; // f3 = 0.5 * 0.5 = 0.25
浮點變數的除法如預期般,因為小數餘數不是問題。
float f1 = 1.0;
f1 /= 3.0f; // f1 = 1.0/3.0 = 0.333
如果您使用可能會分割的整數,請小心,尤其是在截斷影響結果時。 這個範例與上一個範例相同,但數據類型除外。 截斷會導致非常不同的結果。
int i1 = 1;
i1 /= 3; // i1 = 1/3 = 0.333, which gets truncated to 0
二進位轉換
int 和 float 之間的轉換作業會將數值轉換成適當的表示法,以遵循 C 規則來截斷 int 類型。 將值從 float 轉換成 int,然後轉換回 float 會導致根據目標的精確度進行遺失轉換。
二進位轉換也可以使用內部函數 (DirectX HLSL)來執行,其會將數位的位表示重新解譯成目標數據類型。
asfloat() // Cast to float
asint() // Cast to int
asuint() // Cast to uint
位元運算子
HLSL 支援下列位運算元,其與其他運算元遵循與 C 相同的優先順序。 下表描述運算符。
注意
位運算子需要 具有 Direct3D 10 和更高硬體的著色器模型 4_0 。
運算子 | 函式 |
---|---|
~ | 邏輯 Not |
<< | 左移 |
>> | 右移 |
& | 邏輯和 |
| | 邏輯或 |
^ | 邏輯 Xor |
<<= | 左移等於 |
>>= | 右移等於 |
&= | 和等於 |
|= | 或等於 |
^= | Xor Equal |
位運算子會定義為只在int和 uint 資料類型上運作。 嘗試在 float 上使用位運算元,或結構數據類型會導致錯誤。
布爾數學運算符
布林數學運算符是: &&&, ||, ?:
bool b1 = true;
bool b2 = false;
bool b3 = b1 && b2 // b3 = true AND false = false
b3 = b1 || b2 // b3 = true OR false = true
不同於 &&、||和 ?:在 C 中,HLSL 表達式絕對不會縮短評估,因為它們是向量作業。 一律會評估表達式的所有部分。
布爾運算子會根據每個元件運作。 這表示,如果您比較兩個向量,則結果會是一個向量,其中包含每個元件的比較布爾值結果。
對於使用布爾運算子的表達式,每個變數的大小和元件類型都會在作業發生之前升階為相同。 升階類型會決定作業的發生解析度,以及表達式的結果類型。 例如,int3 + float 運算式會升階為 float3 + float3 以進行評估,而其結果會是 float3 類型。
轉換運算子
前面加上括弧的類型名稱的表達式是明確的型別轉換。 類型轉換會將原始表示式轉換成轉換的數據類型。 一般而言,簡單數據類型可以轉換成更複雜的數據類型(使用升級轉換),但只有一些複雜的數據類型可以轉換成簡單數據類型(使用降級轉換)。
只有右側類型鑄造是合法的。 例如,這類 (int)myFloat = myInt;
表達式是非法的。 請改用 myFloat = (float)myInt;
。
編譯程式也會執行隱含型別轉換。 例如,下列兩個運算式相等:
int2 b = int2(1,2) + 2;
int2 b = int2(1,2) + int2(2,2);
逗號運算子
逗號運算子 (,) 會依序分隔一或多個要評估的表達式。 序列中最後一個表達式的值會當做序列的值使用。
以下是值得注意的一個案例。 如果建構函式類型不小心離開等號右邊,則右側現在包含四個運算式,並以三個逗號分隔。
// Instead of using a constructor
float4 x = float4(0,0,0,1);
// The type on the right side is accidentally left off
float4 x = (0,0,0,1);
逗號運算子會從左至右評估表達式。 這會將右側減少為:
float4 x = 1;
在此案例中,HLSL 會使用純量升級,因此結果會如同下列方式撰寫:
float4 x = float4(1,1,1,1);
在此實例中,離開右側的 float4 類型可能是編譯程式無法偵測的錯誤,因為這是有效的語句。
比較運算子
比較運算子為:<、、>==、!=、<=、=。 >
比較大於 (或小於) 任何純量值的值:
if( dot(lightDirection, normalVector) > 0 )
// Do something; the face is lit
if( dot(lightDirection, normalVector) < 0 )
// Do nothing; the face is backwards
或者,比較等於 (或不等於) 任何純量值的值:
if(color.a == 0)
// Skip processing because the face is invisible
if(color.a != 0)
// Blend two colors together using the alpha value
或合併並比較大於或等於(或小於或等於)任何純量值的值:
if( position.z >= oldPosition.z )
// Skip the new face because it is behind the existing face
if( currentValue <= someInitialCondition )
// Reset the current value to its initial condition
每個比較都可以使用任何純量數據類型來完成。
若要搭配向量和矩陣類型使用比較運算子,請使用all或任何內部函數。
此作業失敗,因為 if 語句需要單一 bool,但會收到 bool4:
if (A4 < B4)
這些作業成功:
if ( any(A4 < B4) )
if ( all(A4 < B4) )
前置詞或後置運算符
前置詞和後置運算符為:++、--. 前置詞運算子會在評估表達式之前變更變數的內容。 後置運算子會在評估表達式之後變更變數的內容。
在此情況下,迴圈會使用 i 的內容來追蹤循環計數。
float4 arrayOfFloats[4] = { 1.0f, 2.0f, 3.0f, 4.4f };
for (int i = 0; i<4; )
{
arrayOfFloats[i++] *= 2;
}
因為使用後置遞增運算符 (++) ,因此 arrayOfFloats[i] 會乘以 2,然後再遞增 i。 這可能會稍微重新排列,以使用前置遞增運算符。 雖然這兩個範例都相等,但這一個比較難閱讀。
float4 arrayOfFloats[4] = { 1.0f, 2.0f, 3.0f, 4.4f };
for (int i = 0; i<4; )
{
arrayOfFloats[++i - 1] *= 2;
}
因為使用前置詞運算符 (++) ,因此 arrayOfFloats[i+1 - 1] 會在 i 遞增之後乘以 2。
前置詞遞減和後置遞減運算子 (--) 會套用與遞增運算符相同的序列。 差異在於遞減減 1,而不是新增 1。
結構運算元
結構成員選取運算子 (.) 是句點。 鑒於此結構:
struct position
{
float4 x;
float4 y;
float4 z;
};
它可以像這樣讀取:
struct position pos = { 1,2,3 };
float 1D_Float = pos.x
1D_Float = pos.y
每個成員都可以使用結構運算符讀取或寫入:
struct position pos = { 1,2,3 };
pos.x = 2.0f;
pos.z = 1.0f; // z = 1.0f
pos.z = pos.x // z = 2.0f
一元運算子
一元運算子為:, -, +
一元運算子會在單一操作數上運作。
bool b = false;
bool b2 = !b; // b2 = true
int i = 2;
int i2 = -i; // i2 = -2
int j = +i2; // j = +2
運算子優先順序
當表達式包含一個以上的運算符時,運算符優先順序會決定評估的順序。 HLSL 的運算符優先順序遵循與 C 相同的優先順序。
備註
大括弧 ({,}) 開始和結束語句區塊。 當語句區塊使用單一語句時,大括弧是選擇性的。
相關主題