运算符

表达式是由运算符添加标点的变量和文本序列。 运算符确定如何合并、比较、选择变量和文本。 运算符包括:

操作员名称 运算符
加法和乘法运算符 +, -, *, /, %
数组运算符 [i]
赋值运算符 =, +=, -=, *=, /=, %=
二进制强制转换 用于 float 和 int 的 C 规则、用于布尔的 C 规则或 HLSL 内部函数
位运算符 ~, <<, >>, &, |, ^, <<=, >>=, &=, |=, ^=
布尔数学运算符 &&, ||, ?:
强制转换运算符 (类型)
逗号运算符
比较运算符 <, >, ==, !=, <=, >=
前缀或后缀运算符 ++, --
结构运算符
一元运算符 !, -, +

 

许多运算符都针对每个组件,这意味着运算针对每个变量的每个组件独立执行。 例如,单个组件变量执行了一个运算。 另一方面,四组件变量执行了四个运算,每个组件各执行一个运算。

对值执行某些运算的所有运算符(例如 + 和 *)都适用于每个组件。

除非对多组件变量使用全部任何内部函数,否则比较运算符需要单个组件才能工作。 以下运算失败,因为 if 语句需要单个布尔值,但收到了 bool4:

if (A4 < B4)

以下运算成功:

if ( any(A4 < B4) )
if ( all(A4 < B4) )

二进制强制转换运算符asfloatasint等适用于每个组件,但记录了特殊规则的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]”选择数组中的一个或多个组件。 它是一组包含从零开始的索引的方括号。

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 之间的强制转换运算会按照截断 int 类型的 C 规则,将数值转换为相应的表示形式。 将值从 float 转换为 int 再转换回 float 将导致根据目标的精度进行有损转换。

也可以使用内部函数 (DirectX HLSL)执行二进制强制转换,这会将数字的位表示形式重新解释为目标数据类型。

asfloat() // Cast to float
asint()   // Cast to int 
asuint()  // Cast to uint

位运算符

HLSL 支持以下位运算符,这些运算符在其他运算符方面遵循与 C 相同的优先级。 下表描述了运算符。

注意

位运算符需要具有 Direct3D 10 及更高硬件的着色器模型 4_0

 

运算符 函数
~ 逻辑非
<< 左 Shift 键
>> 右移
& 逻辑与
| 逻辑或
^ 逻辑异或
<<= 左移等于
>>= 右移等于
&= 和等于
|= 或等于
^= 异或等于

 

位运算符定义为仅对 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

每个比较都可以使用任何标量数据类型完成。

要对向量和矩阵类型使用比较运算符,请使用所有任何内部函数。

此运算失败,因为 if 语句需要单个布尔值,但收到了 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

一元运算符

一元运算符为:, -, +

一元运算符对单个运算数进行运算。

bool b = false;
bool b2 = !b;      // b2 = true
int i = 2;
int i2 = -i;       // i2 = -2
int j = +i2;       // j = +2

运算符优先级

当表达式包含多个运算符时,运算符优先级确定计算顺序。 HLSL 的运算符优先级遵循与 C 相同的优先级。

备注

大括号 ({,}) 将语句块括起来。 当语句块使用单个语句时,大括号可选。

语句 (DirectX HLSL)