演算子 (DirectX HLSL)
式は、演算子で区切られた変数とリテラルのシーケンスです。演算子は、変数とリテラルの結合、比較、選択、その他の操作を行う方法を決定します。演算子には次のものがあります。
演算子名 | 演算子 |
---|---|
加算演算子と乗算演算子 | +, -, *, /, % |
配列演算子 | [i] |
代入演算子 | =, +=, -=, *=, /=, %= |
バイナリ キャスト | float および int には C 規則。bool には C 規則または HLSL 組み込み関数。 |
ビット演算子 | ~, <<, >>, &, |, ^, <<=, >>=, &=, |=, ^= |
ブール演算子 | &&, ||, ?: |
キャスト演算子 | (型) |
コンマ演算子 | , |
比較演算子 | <, >, ==, !=, <=, >= |
前置演算子と後置演算子 | ++, -- |
構造体演算子 | . |
単項演算子 | !, -, + |
演算子の多くは成分ごとに作用します。これは、それぞれの変数のそれぞれの成分に対して演算が別々に実行されることを意味します。たとえば、1 成分変数に対しては 1 つの演算が実行されます。4 成分変数に対しては、それぞれの成分に対して 1 つずつ、合計 4 つの演算が実行されます。
加算演算子と乗算演算子
加算演算子と乗算演算子は、+、-、*、/、% です。
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 or 1.0/2.0, which is 0.5
f3 = f2 % f1; // f3 = remainder of 2.0/1.0, which is 0.0
% 演算子は、両辺が正の値または両辺が負の値の場合にのみ定義します。C とは異なり、% 演算子は、整数だけでなく浮動小数点データ型に対しても作用します。
配列演算子
配列メンバー選択演算子 "[i]" を使うと、配列内の 1 つまたは複数の成分を選択できます。これは、ゼロで始まるインデックスを含む角かっこのセットです。
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
1 番目のインデックスは、ゼロから始まる行インデックスです。2 番目のインデックスは、ゼロから始まる列インデックスです。
代入演算子
代入演算子は、=、+=、-=、*=、/= です。
変数にはリテラル値を割り当てることができます。
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
浮動小数点変数の除算は、期待どおりの結果となります。これは、10 進数の剰余は問題とならないためです。
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 のキャスト演算では、int 型を切り捨てるための C 規則に従って、数値を適切な表現に変換します。値を float から int にキャストした後で、再度 float にキャストすると、ターゲットの精度の違いによって桁が切り捨てられ、元の値とは異なる結果が生じる場合があります。
バイナリ キャストも組み込み関数 (DirectX HLSL) を使用して実行できます。この場合、数値のビット表現が、変換対象のデータ型に合わせて解釈し直されます。
asfloat() // Cast to float
asint() // Cast to int
asuint() // Cast to uint
ビット演算子
HLSL では、以下に示すビット演算子がサポートされています。演算子の優先順位については、C と同じ規則が適用されます。次の表は、新しいを演算子を示しています。
演算子 | 関数 |
---|---|
~ | 論理 NOT |
<< | 左シフト |
>> | 右シフト |
& | 論理 AND |
| | 論理 OR |
^ | 論理 XOR |
<<= | 左シフト後代入 |
>>= | 右シフト後代入 |
&= | AND 後代入 |
|= | OR 後代入 |
^= | XOR 後代入 |
ビット演算子は、int および uint データ型に対してのみ使用できます。ビット演算子を float または struct データ型に対して使用すると、エラーが発生します。
ブール演算子
ブール演算子は、&&、||、?: です。
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 の式はベクトル演算であるため、短絡評価は使用されません。式のすべての側が必ず評価されます。
ブール演算子は、成分ごとに作用します。これは、2 つのベクトルを比較した場合、その結果は各成分を比較したブール値の結果を含むベクトルとなることを意味します。
ブール演算子を使用する式では、演算が行われる前に、各変数のサイズおよび成分型は、同じサイズおよび型を持つように昇格されます。格上げされた型に基づいて、演算が行われる精度と式の結果型が決定されます。たとえば、int3 + float の式は、評価のために float3 + float3 に昇格され、その結果は float3 型となります。
キャスト演算子
かっこで囲まれた型名に続く式は、明示的な型キャストを表します。型キャストは、元の式をキャスト先のデータ型に変換します。通常、単純なデータ型は、昇格キャストによって、より複雑なデータ型にキャストできます。これに対し、降格キャストによって単純なデータ型にキャストできる複雑なデータ型は限られています。
コンマ演算子
コンマ演算子 (,) は、順に評価する複数の式を区切るために使います。シーケンス内の最後の式の値がシーケンスの値として使用されます。
次の場合は注意が必要です。等号の右辺にコンストラクターの型を記述し忘れた状態で、右辺に 3 つのカンマで区切られた 4 つの式があるとします。
// 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
または、2 つを組み合わせて、スカラー値以上 (または以下) の値を比較します。
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
これらの比較は、任意のスカラー データ型を使用して実行できます。比較演算子は、ベクトル型、行列型、オブジェクト型などの複雑なデータ型をサポートしていません。
前置演算子と後置演算子
前置演算子と後置演算子は、++、-- です。前置演算子は、式が評価される前に変数の内容を変更します。後置演算子は、式が評価された後に変数の内容を変更します。
次のケースでは、i の内容を使ってループ カウントを追跡しています。
float4 arrayOfFloats[4] = { 1.0f, 2.0f, 3.0f, 4.4f };
for (int i = 0; i<4; )
{
arrayOfFloats[i++] *= 2;
}
後置インクリメント演算子 (++) が使用されているので、i がインクリメントされる前に arrayOfFloats[i] に 2 が掛けられます。上記の例を少し変えると、次のように前置インクリメント演算子を使用することができます。この 2 つの例は等価ですが、この例の方が読むのが困難です。
float4 arrayOfFloats[4] = { 1.0f, 2.0f, 3.0f, 4.4f };
for (int i = 0; i<4; )
{
arrayOfFloats[++i - 1] *= 2;
}
前置演算子 (++) が使用されているので、i がインクリメントされた後で arrayOfFloats[i+1 - 1] に 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
演算子の優先順位
式に複数の演算子が含まれている場合は、演算子の優先順位に基づいて評価順が決定されます。
解説
中かっこ ({,}) により、ステートメント ブロックの始まりと終わりが示されます。ステートメント ブロック内で使用されるステートメントが 1 つだけの場合は、中かっこを省略できます。