演算子
式は、一連の変数とリテラルが演算子によって区切られたものです。 演算子を使って、変数とリテラルがどのように結合、比較、選択されるかなどが決定されます。 演算子には次のものがあります。
オペレーター名 | 演算子 |
---|---|
加法および乗算演算子 | +、-、*、/、% |
配列演算子 | [i] |
代入演算子 | =、+=、-=、*=、/=、%= |
バイナリ キャスト | float および int の C 規則、bool の C 規則または HLSL 組み込み |
ビット処理演算子 &、^、| | ~、<<、>>、&、|、^、<<=、>>=、&=、|=、^= |
ブール値算術演算子 | &&、|、?: |
キャスト演算子 | (型) |
コンマ演算子 | 、 |
比較演算子 | <、>、==、!=、<=、>= |
前置または後置演算子 | ++、-- |
構造体演算子 | 。 |
単項演算子 | !、-、+ |
演算子の多くはコンポーネント単位です。つまり、演算は各変数のコンポーネントごとに個別に実行されます。 たとえば、1 つのコンポーネント変数では 1 つの演算が実行されます。 それに対し、4 つのコンポーネントを持つ変数では、コンポーネントごとに 1 つずつ、4 つの演算が実行されます。
+ や * など、値に対して何らかを行うすべての演算子は、コンポーネントごとに機能します。
複数コンポーネント変数で all または any 組み込み関数を使用するのでない限り、比較演算子では 1 つのコンポーネントが機能することが求められます。 次の演算は失敗します。if ステートメントでは 1 つの bool が必要ですが、bool4 を受け取っているためです。
if (A4 < B4)
次の演算は成功します。
if ( any(A4 < B4) )
if ( all(A4 < B4) )
二項キャスト演算子 asfloat、asint などはコンポーネントごとに機能しますが、特殊な規則が文書化されている asdouble は除きます。
ピリオド、コンマ、配列角かっこなどの選択演算子は、コンポーネントごとに機能しません。
キャスト演算子を使うと、コンポーネントの数が変更されます。 次のキャスト演算では、これらが同等であることを示しています。
(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]" では、配列内の 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
最初のインデックスは、ゼロから始まる行インデックスです。 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 と同じ優先順位に従う、次のビット演算子がサポートされます。 次の表で演算子について説明します。
Note
ビット演算子を使用するには、Direct3D 10 以降のハードウェアを備えたシェーダー モデル 4_0 が必要です。
演算子 | 関数 |
---|---|
~ | 論理 Not |
<< | 左シフト |
>> | 右シフト |
& | 論理 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 型になります。
キャスト演算子
かっこで囲まれた型名が前に付いた式は、明示的な型キャストです。 型キャストを使って、元の式がキャストのデータ型に変換されます。 一般に、単純なデータ型はより複雑なデータ型にキャストできます (上位変換キャストを使用) が、単純なデータ型にキャストできるのは一部の複雑なデータ型のみです (下位変換キャストを使用)。
右辺型キャストのみが有効です。 たとえば、(int)myFloat = myInt;
のような式は無効です。 代わりに myFloat = (float)myInt;
を使用してください
コンパイラでは、暗黙的な型キャストも実行します。 たとえば、次の 2 つの式は同等です。
int2 b = int2(1,2) + 2;
int2 b = int2(1,2) + int2(2,2);
コンマ演算子
コンマ演算子 (,) を使って、順番に評価される 1 つ以上の式を区切ります。 シーケンス内の最後の式の値が、そのシーケンスの値として使用されます。
注意を払う必要がある事例を次に示します。 コンストラクター型が誤って等号の右辺から漏れた場合、右辺には 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
または、両方を結合し、任意のスカラー値以上 (またはそれ以下) の値を比較します。
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 または any 組み込み関数を使用します。
次の演算は失敗します。if ステートメントでは 1 つの 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;
}
後置インクリメント演算子 (++) が使用されるため、i がインクリメントされる前に arrayOfFloats[i] に 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
単項演算子
単項演算子は !、-、+ です
単項演算子は、1 つのオペランドで動作します。
bool b = false;
bool b2 = !b; // b2 = true
int i = 2;
int i2 = -i; // i2 = -2
int j = +i2; // j = +2
演算子の優先順位
式に複数の演算子が含まれている場合、演算子の優先順位によって評価の順序が決まります。 HLSL の演算子の優先順位は、C と同じ優先順位に従います。
解説
中かっこ ({,}) で、ステートメント ブロックを開始および終了します。 ステートメント ブロックで 1 つのステートメントを使用する場合、中かっこは省略できます。
関連トピック