摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人”
在上一章中,我们已经介绍了 Cg 语言的基础数据类型( 7 种)、内置数据类型,以及数组、结构、接口等类型,本章将在此基础上讨论 Cg 中的表达式,表达式由操作符( operator )关联一个或多个操作数( operand )构成,我们首先阐述各种类型的操作符,并结合数据类型讲解操作符的具体使用方法。
Cg 中的操作符与 C 语言中的类似(操作符的功能和写法与 C 相同,但用法不尽相同),按照操作符的功能可以划分为:关系操作符、逻辑操作符、条件操作符。 Cg 中有一类较为独特的操作符,称为 Swizzle 操作符, 这个操作符用于取出向量类型变量中的分量。此外,与 C 语言不同的是, Cg 允许在向量类型变量上使用操作符,例如 > 操作符可以用来比较两个向量各个分量的大小关系。 Cg 中的表达式还有很多与 C 语言不同的细节之处,将在本章中一一分说。
6.1 关系操作符( Comparison Operators )
关系操作符,用于比较同类型数据(不同类型的基础数据需要进行类型转换,不同长度的向量,不能进行比较)之间的大小关系或者等价关系。 Cg 中有 6 种关系操作符,如 表 1 所示 , 关系操作符运算后的返回类型均为 bool 类型。
关系操作符
| 功能 | 用法 |
< | 小于 | expr < expr |
<= | 小于或等于 | expr <= expr |
!= | 不等于 | expr != expr |
==
| 等于 | expr == expr |
>= | 大于或等于 | expr >= expr |
> | 大于 | expr > expr |
表 1 关系操作符
在 Cg 中,由于关系操作符以及下节会讲到的逻辑操作符,都返回 bool 类型结果,所以这两种操作符有时也被统一称为 boolean operator 。
Cg 语言表达式允许对向量使用所有的 boolean operator ,如果是二元操作符,则被操作的两个向量的长度必须一致。表达式中向量的每个分量都进行一对一的运算,最后返回的结果是一个 bool 类型的向量,长度和操作数向量一致。例如:
float3 a = float4(0.5, 0.0, 1.0);
float3 b = float4(0.6, -0.1, 0.9);
bool3 c = a<b;
运算后向量 c 的结果为 float3(true, false, true);
6.2 逻辑操作符( Logical Operators )
Cg 语言中有 3 种逻辑操作符 ( 也被称为 boolean Operators) ,如 表 2 所示,逻辑操作符运算后的返回类型均为 bool 类型。
逻辑操作符
| 功能 | 用法 |
&& | 逻辑与 | expr && expr |
|| | 逻辑或 | expr || expr |
! | 逻辑非 | !expr |
表 2 逻辑操作符
正如上节所说,逻辑操作符也可以对向量使用,返回的变量类型是同样长度的内置 bool 向量。
有一点需要注意: Cg 中的逻辑与( && )和逻辑或( || )不存在 C 中的短路现象( short-circuiting ,即只用计算一个操作数的 bool 值即可),而是参与运算的操作数据都进行 bool 分析。
6.3 数学操作符( Math Operators )
Cg 语言对向量的数学操作提供了内置的支持, Cg 中的数学操作符有: * 乘法; / 除法; - 取反; + 加法;—减法; % 求余; ++ ;——; *= ; /= ; += ; -= ;后面四种运算符有时被归纳入赋值操作符,不过它们实际上进行数学计算,然后进行赋值,所以这里也放入数学操作符中进行说明。
在文献【 2 】第 3.3 节 Math Expressions 中,其行文意思容易让人觉得“好像只有加减乘除等运算可以对向量进行”,实际上经过我的测试, ++ 、——等数学运算符同样可以使用在向量上。所以“ Cg 语言对向量的数学操作提供内置支持”这句话是非常准确的。
需要注意的是:求余操作符 % 。只能在 int 类型数据间进行,否则编译器会提示错误信息: error C1021: operands to “ % ” must be integral.
当使用这些数学操作符对一个标量和一个向量进行运算时,标量首先被复制到一个长度相同的向量中,然后进行运算,例如下面的代码形式是正确的:
void function()
{
float2 a = float2(1.0, 1.0);
float b = 2.0;
f *= d;
f *= 2.0;
}
6.4 移位操作符
Cg 语言中的移位操作符,功能和 C 语言中的一样,也可以作用在向量上,但是向量类型必须是 int 类型。例如:
int2 a = int2(0.0,0.0);
int2 b = a>>1;
如果使用如下代码,会出现错误提示信息: error C1021:operands to “ shr ” must be integral.
float2 a = int2(0.0,0.0);
float2 b = a>>1;
6.5 Swizzle 操作符
可以使用 Cg 语言中的 swizzle 操作符( . )将一个向量的成员取出组成一个新的向量。 swizzle 操作符被 GPU 硬件高效支持。 swizzle 操作符后接 x 、 y 、 z 、 w ,分别表示原始向量的第一个、第二个、第三个、第四个元素; swizzle 操作符后接 r 、 g 、 b 和 a 的含义与前者等同。不过为了程序的易读性,建议对于表示颜色值的向量,使用 swizzle 操作符后接 r 、 g 、 b 和 a 的方式。
举例如下:
float4(a, b, c, d).xyz 等价于 float3(a, b, c)
float4(a, b, c, d).xyy 等价于 float3(a, b, b)
float4(a, b, c, d).wzyx 等价于 float4(d, c, b, a)
float4(a, b, c, d).w 等价于 float d
值得注意的是, Cg 语言中 float a 和 float1 a 是基本等价的,两者可以进行类型转换; float 、 bool 、 half 等基本类型声明的变量也可以使用 swizzle 操作符。例如:
float a = 1.0;
float4 b = a.xxxx;
注意: swizzle 操作符只能对结构体和向量使用,不能对数组使用,如果对数组使用 swizzle 操作符则会出现错误信息: error C1010: expr
要从数组中取值必须使用 [] 符号。例如:
float a[3] = {1.0,1.0,0.0};
float b = a[0]; // 正确
float c = a.x; // 编译会提示错误信息
6.6 条件操作符( Conditional Operators )
条件操作符的语法格式为:
expr1 ? expr2 : expr3;
expr1 的计算结果为 true 或者 flase ,如果是 true, 则 expr2 执行运算,否则 expr3 被计算。
条件操作符为简单的 if-else 语句提供了一种便利的替代方式,例如我们可以不必写:
if(a < 0){b = a}
else{c = a}
而改写为:
( a < 0 ) ?(b = a) :( c = a);
Cg 中的条件操作符一个独特的性能是:支持向量运算。即, expr1 的计算结果可以是 bool 型向量, expr2 和 expr3 必须是与 expr1 长度相同的向量。举例如下:
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) 做比较运算后结果为( true, false, false ) , 所以 i 的第一个数据赋值给 K 的第一个数据, g 的第二个和第三个数据赋值给 k 的第二个和第三个数据, K 的值为 (1.0, 1.0, 0.0) 。
6.7 操作符优先顺序
Cg 语言中操作符的优先顺序如 表 3 所示,从上到下表示从高级到低级的优先级;同一行的操作符具有同等优先级。该表参考了 Cg 教程 _ 可编程实时图形权威指南第 3.3.1 节。
操作符
| 结合律 | 功能 |
() [] -> . | 从左到右 | 函数调用、数组引用、结构引用、成员选择 |
! ~ ++ - + - * & (type) sizeof | 从右到左 | 一元操作符:取反、增加、减少、正号、负号、间接、地址、转换 |
* / % | 从左到右 | 乘法、除法、余数 |
+ - | 从左到右 | 加法、减法 |
<< >> | 从左到右 | 移位操作符 |
< >= > >= | 从左到右 | 关系操作符 |
== != | 从左到右 | 等于,不等 |
& | 从左到右 | 位操作符与 |
^ | 从左到右 | 位操作符异或 |
| | 从左到右 | 位操作符或 |
&& | 从左到右 | 逻辑与 |
|| | 从左到右 | 逻辑或 |
?: | 从右到左 | 条件表达式 |
= += -= *= /= %= &= ^= != <<= >>= | 从右到左 | 赋值、赋值表达式 |
, | 从左到右 | 逗号操作符 |
表 3 操作符优先级
6.8 控制流语句( Control Flow Statement )
程序最小的独立单元是语句( statement ),语句一般由分号结尾,缺省情况下,语句是顺序执行的,但是当涉及逻辑判断控制时,就要求有控制流程序语句。控制流程序语句分为条件语句和循环语句,在 C 语言中,条件语句有 if 、 if-else 、 switch 等,而循环过程则由 while 、 do-while 和 for 语句支持。 Cg 中的控制流语句和循环语句与 C 语言类似:条件语句有: if 、 if-else ;循环语句有: while 、 for 。 break 语句可以和在 for 语句中使用。
Cg 语言中的控制流语句要求其中的条件表达式返回值都是 bool 类型,这一点是与 C 语言不同之处( C 语言中,条件表达式返回值可以是 0 、 1 )
vs_2_x, vp30 和 vp40 这些 pro
“ In other profiles, for and while loops may on
这句话的意思是“在其他的 profiles 中, for 和 while 循环只有当确切的知道循环次数时才能被使用”。但经过试验,如果使用“在 fp40 和 ps_3_0 之前的” 片段 profiles 编译含义 for, while 语句时会出现错误提示信息: error c6003 : instruction limit of exceeded …… 。因此,如果没有确切的把握,不要在低级的 profiles 中使用循环控制语句。
同样, return 只能作为最后一条语句出现。函数的递归调用( recursion )在 Cg 语言中是被禁止的。 Switch 、 case 和 default 在 Cg 中作为保留关键字存在,但是它们目前不被任何 pro