使用包组[包装组织]

笼统的讲,显示器上每一个像素点都至少会执行一次。这也是为什么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