使用包组[包装组织]

笼统的讲,显示器上每一个像素点都至少会执行一次。这也是为什么GPU要设计高度优化的并行架构的原因。同样的在Cg语言的标准变量类型和操作符中,这种设计哲学也很明显。理解它们,不仅仅是为了正确的使用着色器,同时也是为了能够写出更高效的着色器。


  • 操作步骤

    在Cg语言中有两种类型的变量: 单精度值single包组packed arrays 。后者很容易辨别因为这种类型通常会以数字结尾,比如 float4int4 等等。正如它们的名字所表示的一样,这些类型的变量跟我们编程语言中的 结构体structs 类似,这也意味着每一个这样的变量包含了多个单精度值。在Cg语言中我们称之为 包组packed arrays ,尽管它们并非真的是传统意义上的数组。

    在包组中的元素能像常见的结构体那样访问。通常它们表示成 xyzw 。然而Cg语言还有另一种表示,就是 rgba 。尽管你使用 x 或者 r 去表示都是可以的,但是对于代码阅读者来说它们之间的区别就非常的大了。事实上,在着色器编程中,经常涉及到的就是位置和颜色的计算。你可能对下面的标准着色器代码中的代码片段还有印象吧:

    o.Alpha = _Color.a;
    

    在这里, o 是一个结构体而 _Color 就是一个包组。这也是为什么Cg要禁止上面提到的两种表示进行混用的原因:你不能使用 _Color.xgz

    这里还有一个很重要的包组的特性,这种特性在C#中没有: swizzling [这个不知道怎么翻译]。Cg允许仅通过简单的一行代码就对包组内的元素进行寻址和重新排序。又是下面在标准着色器中熟悉的代码片段:

    o.Albedo = _Color.rgb;
    

    Albedo 是一个 fixed3 类型,也就是说它里面包含了三个 fixed 类型的值。 然而 _Color 是一个 fixed4 类型的定义。由于 _Color 定义包含的元素比 Albedo 定义包含的元素要多,直接赋值的话,由于不匹配,肯定会产生一个编译错误。如果用C#代码来进行同样的操作,代码如下所示:

    o.Albedo.r = _Color.r;
    o.Albedo.g = _Color.g;
    o.Albedo.b = _Color.b;
    

    相比于C#代码,在Cg语言中,我们可以用如下代码简写:

    o.Albedo = _Color.rgb;
    

    Cg语言也允许对元素进行重新排序。比如,通过 _Color.bgr 这个代码去交换红色和蓝色通道的颜色。

    最后要讲一点,当一个单精度值赋值给包组时,这个值会被复制到包组的所有元素中去:

    o.Albedo = 0; // Black =(0,0,0)
    o.Albedo = 1; // White =(1,1,1)
    

    这个就是Cg语言中的 smearing 特性。

    Swizzling 还可以被用作表达式的左值,不过仅当包组的具体元素能被这样使用:

    o.Albedo.rg = _Color.rg;
    

    上面这种特性,叫做 masking .


    压缩矩阵

    真正发挥 swizzling 特性潜力的是在它应用于压缩矩阵的时候。Cg语言允许像 float4x4 这种类型,这是一个四行四列的矩阵。你可以使用 _mRC 标记访问矩阵中的单个元素,R 表示元素所在的行而 C 表示元素所在的列:

    float4x4 matrix; 
    // ... 
    float first = matrix._m00; 
    float last = matrix._m33;
    

    _mRC 标记还可以接连使用:

    float4 diagonal = matrix._m00_m11_m22_m33;
    

    如果要获取矩阵的整行,可以使用中括号:

    float4 firstRow = matrix[0]; 
    // Equivalent to 
    float4 firstRow = matrix._m00_m01_m02_m03;
    

  • 相关补充

    包组是Cg语言中最棒的特性之一,你可以从下面的连接获得关于包组的更多信息:

    http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter02.html