#Unity shader学前准备之CG语言(3)

##CG数据类型

       本节会介绍Cg语言中预定义的内置(built in)的,或称为基本(primitive)的数据类型

###1.基本数据类型 Cg支持7种基本的数据类型:

  1. float,32位浮点数据,一个符号位,浮点数据类型被所有的 profile 支持 (但是 DirectX8 pixel profiles 在一些操作中降低了浮点数的精度和范围);
  2. half,16 为浮点数据;
  3. int,32 位整形数据,有些 profile 会将 int 类型作为 float 类型使用;
  4. fixed,12 位定点数,被所有的 fragment profiles 所支持;
  5. bool,布尔数据,通常用于 if 和条件操作符(?:),布尔数据类型被所有的 profiles 支持;
  6. sampler*,纹理对象的句柄(the handle to a texture object),分为 6 类: sampler, sampler1D, sampler2D, sampler3D, samplerCUBE,和 samplerRECT。 DirectX profiles 不支持 samplerRECT 类型,除此之外这些类型被所有的 pixel profiles 和 NV40 vertex program profile 所支持。由 此可见,在不远的未来,顶点程序也将广泛支持纹理操作;(PS:表面渲染器出现之后顶点和偏大渲染器都会越来越少)
  7. string,该类型不被当前存在的 profile 所支持,实际上也没有 必要在 Cg 程序中用到字符类型,但是你可以通过 Cg runtime API 声明该类 型变量,并赋值;因此,该类型变量可以保存 Cg 文件的信息。(PS:现在的profile依旧不支持,毕竟渲染器处理字符串没啥实际意义)

profiles对于dx,openGL的支持情况和硬件需求可以从这里看到。(2012年Cg语言停止开发,开普勒显卡发售,一般情况下只要开普勒架构之后的N卡都可以较好才支持Cg,A卡开发Cg建议使用DX,openGL也许会有不确定的问题,不过用U3D开发请无视,因为Cg只是中间语言,最终会编译成GLSL或者HLSL)

       前6种类型经常用到,而第7种,完全可以没有,事实上wiki有关Cg语言的部分也没有。除了上述基本数据类型外,Cg还提供了内置向量数据类型(built-in vector data types),内置向量数据类型的基于基础数据类型。例如:float4表示float类型的4元向量。

       注意:向量最长不能超过 4 元,即在 Cg 程序中可以声明 float1float2float3float4 类型的数组变量,但是不能声明超过 4 元向量,例如:


float5 array; // 编译报错

向量初始化的一般形式为:


float4 array = float4(1.0,2.0,3.0,4.0);

较长的向量还可以由较短的向量进行构建:


float2 a = float2(1.0,1.0);
float4 b = float4(a,0.0,0.0);

此外,Cg还提供矩阵数据类型,但最大维数不超过4*4阶。例如:


float1x1 matrix1; //等价于 float matirx1; x 是字符,并不是乘号! 
float2x3 matrix2; // 表示 2*3 阶矩阵,包含 6 个 float 类型数据 
float4x2 matrix3; // 表示 4*2 阶矩阵,包含 8 个 float 类型数据 
float4x4 matrix4; //表示4*4阶矩阵,这是最大的维数

矩阵的初始化方程式:


float2x3 matrix5 = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}; //一维的,注意

       注意:Cg中向量、矩阵与数组是完全不同的,向量和矩阵是内置的数据类型(矩阵基于向量,向量基于6种基本类型),而数组则是一种数据结构,不是内置数据类型!(在C/C++中,这三者都是数据结构,向量和矩阵可以由数组创建)。

###2.数组(arrays)类型

“General-purpose arrays can only be used as uniform parameters to a vertex program. The intent is to allow an application to pass arrays of skinning matrices and arrays of light parameters to a vertex program”

       在着色程序中,数组通常的使用目的是:作为从外部应用程序传入大量参数 到 Cg 的顶点程序中的形参接口,例如与皮肤形变相关的矩阵数组,或者光照参 数数组等。

       简而言之,数组数据类型在 Cg 程序中的作用是:作为函数的形参,用于大 量数据的转递。

Cg中声明数组变量的方式和C语言类似,例如:


float a[10];  //声明了一个数组,包含 10 个 float 类型数据
float4 b[10]; //声明了一个数组,包含 10 个 float4 类型向量数据

对数组初始化的方式


float a[4] = {1.0, 2.0, 3.0, 4.0}; //初始化一个数组

要获取数组的长度,可以调用.length,例如:


float a[10];   //声明一个数组
int length = a.length; //获取数组长度,后面没有括号

声明多维数组及初始化方式如下所示:


float b[2][3] = { {0.0, 0.0, 0.0}, {1.0, 1.0, 1.0} };

对多维数组求长度的方式:


int length1 = b.length; // length1 值为 2
int length2 = b[0].length; // length2 值为3

       数组和矩阵有些类似,但是并不是相同。 例如 4*4阶数组的的声明方式为: float M[4][4];4 阶矩阵的声明方式为:float4x4 M。前者是一个数据结构,包含 16 个 float 类型数据,后者是一个 4 阶矩阵数据。float4x4 M[4],表示一个数组,包 含4个4阶矩阵数据。

       进行数组变量声明时,一定要指定数组长度,除非是作为函数参数而声明的 形参数组。并且在当前的 profiles 中,数组的长度和所引用的数组元素的地址必须在编译时就知道。

Unsized arrays may only be declared as function parameters-they may not be declared as variables. Furthermore, in all current profiles, the actual array length and address calculations implied by array indexing must be known at compile time

###3.结构(struct)类型

       Cg语言支持结构体(structure),实际上Cg中的结构体的声明、使用和C++ 非常类似(只是类似,不是相同)。一个结构体相当于一种数据类型,可以定义该类型的变量。引入结构体机制,赋予了 Cg 语言一丝面向对象的色彩。还记得 C++中,结构体与类的区别吗?没有区别,除了默认访问属性在结构体中为 public,类中为 private,所以结构体与类是非常近似的,由此可以看出 shader 语言的发展趋势还是向着具有面向对象特性的高级语言。不过目前的 Cg 语言中的结构体以展现“封装”功能为主,并不支持继承机制。

       结构体的声明以关键字 struct 开始,然后紧跟结构体的名字,接下来是一个大括号,并以分号结尾(不要忘了分号)。大括号中是结构体的定义,分为两大 类:成员变量和成员函数。例如,定义一个名为 myAdd 的结构体,包含一个成员变量,和一个执行相加功能的成员函数,然后声明一个该结构体类型的变量, 代码为:


struct myAdd {
    float val;
    float add(float x) {
        return val + x; 
    }
}; 
myAdd s;

使用符号.引用结构体中的成员变量和成员函数,例如:


float a = s.val;
float b = s.add(a);

       一般来说,Cg 的源代码都会在文件首部定义二个结构体,分别用于定义输人和输出的类型,这二个结构体定义与普通的 C 结构定义不同,除了定义结构体 成员的数据类型外,还定义了该成员的绑定语义类型( Binding Semantics),所谓 绑定语义类型是为了与宿主环境进行数据交换的时候识别不同数据类型的。目前 Cg 支持的绑定语义类型包括 POSTION 位置),COLOR(颜色),NORMAL(法向量),Texcoord(纹理坐标)等类型。

       当顶点着色程序向片段着色程序传递的数据类型较多的情况下,使用结构体 可以大大的方便代码的编写和维护。总而言之,使用结构体是一个好习惯,高智商的孩子都使用。

###4.接口(Interfaces)类型

       Cg 语言提供接口类型,实际上,很少见到使用接口类型的着色程序, 原因在于,Cg 语言中的接口类型还不完善,不能被扩展和包含。此外,目前的 GPU 编程大多只是针对独立的算法进行编码,规模较小,使用接口类型没有太大的优势。

###5.类型转换

       Cg 中的类型转换和 C 语言中的类型转换很类似。C 语言中类型转换可以是 强制类型转换,也可以是隐式转换,如果是后者,则数据类型从低精度向高精度 转换。在 Cg 语言中也是如此。例如:


float a = 1.0;
half b = 2.0;
float c = a+b; //等价于 float c = a + (float)b;

       当有类型变量和无类型常量数据进行运算时,该常量数据不做类型转换,举例如下:


float a = 1.0;
float b = a + 2.0; //2.0 为无类型常量数据,编译时作为 float 类型

Cg语言中常量数据后可以加上类型后缀,表示该数据的类型,例如:


float a = 1.0;
float b = a + 2.0h; //2.0h 为 half 类型常量数据,运算是需要做类型转换

常量的类型后缀(type suffix)有3种:

  • f:表示float
  • h:表示half
  • x:表示fixed