观前提示:本文章是个人笔记,只是给自己看的,我自己看得懂就行。
目录
一、Cg数据类型
1、基本数据类型
Cg支持7种基本的数据类型:
float:32 位浮点数据,一个符号位
half:16 位浮点数据
fixed:12 位浮点数
int:32 位整形数据,有些 profile 会将 int 类型作为 float 类型使用
bool:布尔数据
sampler*:纹理对象的句柄,分为 6 类: sampler, sampler1D, sampler2D, sampler3D, samplerCUBE和 samplerRECT
string:字符类型
2、Cg向量数据类型
Cg提供了内置的向量数据类型(built-in vector data types),内置的向量数据类型基 于基础数据类型。
例如:float4,表示float类型的4元向量; bool4,表示bool 类型 4 元向量。
注意:向量最长不能超过 4 元,即在 Cg 程序中可以声明 float1、float2、float3、 float4 类型的数组变量,但是不能声明超过 4 元的向量。
向量初始化方式一般为: float4 array = float4(1.0, 2.0, 3.0, 4.0)。
3、Cg矩阵数据类型
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};
4、Cg数组类型
在着色程序中,数组通常的使用目的是:作为从外部应用程序传入大量参数输出到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}; //初始化一个数组
//获取数组长度
float a[2] = {1.0, 2.0}; int nLen = 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阶矩阵数据。
注意:进行数组变量声明时,一定要指定数组长度,除非是作为函数参数而声明的 形参数组。
5、Cg结构类型
Cg语言支持结构体(structure),实际上Cg中的结构体的声明、使用和C++ 非常类似(只是类似,不是相同)。
声明一个该结构体类型的变量,代码如下:
struct myAdd
{
float val;
float add(float x)
{
return val + x;
}
};
myAdd s;
注意:在当前的所有的profile版本下,如果结构体的一个成员函数使用了成员变量,则该成员变量要声明在前。此外,成员函数是否可以重载依赖于使用的profile版本。
6、Cg数据类型转换
Cg中的类型转换和C语言中的类型转换很类似。C 语言中类型转换可以是强制类型转换,也可以是隐式转换,如果是后者,则数据类型从低精度向高精度转换。在 Cg 语言中也是如此。例如:
float a = 1.0;
half b = 2.0;
float c = a+b; //等价于 float c = a + (float)b
Cg 语言中对于常量数据可以加上类型后缀,表示该数据的类型,例如:
float a = 1.0;
float b = a + 2.0h; //2.0h 为 half 类型常量数据,运算是需要做类型转换
//常量的类型后缀(type suffix)有 3 种
1、f:表示 float;
2、h:表示 half;
3、x:表示 fixed;
二、Cg表达式与控制语句
1、Cg关系运算符
关系操作符,用于比较同类型数据(不同类型的基础数据需要进行类型转换,不同长度的向量,不能进行比较)之间的大小关系或者等价关系。
2、Cg向量bool逻辑运算
Cg语言表达式允许对向量使用所有的boolean operator,如果是二元操作符,则被操作的两个向量的长度必须一致。表达式中向量的每个分量都进行一对一的运算,最后返回的结果是一个bool类型的向量,长度和操作数向量一致。例如:
float3 a = float3(0.5, 0.0, 1.0);
float3 b = float3(0.6, -0.1, 0.9);
bool3 c = a < b;
//运算后向量c的结果为
bool3(true, false, false);
3、Cg逻辑操作符
Cg语言中有3种逻辑操作符(也被称为boolean Operators),逻辑操作符运算后的返回类型均为bool类型。
逻辑操作符也可以对向量使用,返回的变量类型是同样长度的内置bool向量。
注意:Cg中的逻辑与(&&)和逻辑或(||)不存在C中的短路现象(short-circuiting,即只用计算一个操作数的bool值即可),而是参与运算的操作数据都进行bool分析。
4、Cg数学操作符
Cg语言对向量的数学操作提供了内置的支持,Cg中的数学操作符有:*乘法; /除法; -取反; +加法; -减法; %求余; ++; --; *=; /=; +=; -=;
后面四种运算符有时被归纳入赋值操作符,不过它们实际上进行数学计算,然后进行赋值,所以这里也放入数学操作符中进行说明。
需要注意的是:求余操作符%。只能在int类型数据间进行,否则编译器会提示错误信息:
error C1021: operands to “%” must be integral.
5、Cg位移操作符
Cg语言中的移位操作符,功能和C语言中的一样,也可以作用在向量上,但向量类型必须是int类型。
例如:
int2 a = int2(0.0,0.0);
int2 b = a>>1;
6、Cg 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
注意:Swizzle操作符只能对结构体和向量使用,不能对数组使用。
7、Cg条件运算符
expr1 ? expr2 : expr3;
if(a < 0){b = a;}
else{c = a;}
8、Cg操作运算符优先级
9、Cg控制流语句
Cg中的控制流语句和循环语句与C语言类似。
条件语句有:if、if-else、if-else if-else;
循环语句有:while、for;
break、continue 语句可以在for和while语句中使用。
10、Cg关键字
Cg 中的关键字很多都是照搬C\C++中的关键字,不过 Cg 中也创造了一系列独特的关键字, 这些关键字不但用于指定输入图元的数据含义(是位置信息,还是法向量信息),本质也则对应着这些图元数据存放的硬件资源(寄存器或者纹理),称之为语义词(Semantics),通常也根据其用法称之为绑定语义词(binding semantics)。
除语义词外,Cg 中还提供了三个关键字:in、out、inout。用于表示函数的输入参数的传递方式,称为输入\输出关键字,这组关键字可以和语义词合用表达硬件上不同的存储位置,即同一个语义词,使用 in 关键字修辞和 out 关键词修辞,表示的图形硬件上不同的寄存器。
Cg 语言还提供两个修辞符:uniform 用于指定变量的数据初始化方式;const 关键字的含义与 C\C++中相同,表示被修辞变量为常量变量。
三、输入、输出与语义控制
1、Cg uniform
Cg 语言将输入数据流分为两类:
(1)Varying inputs,即数据流输入图元信息的各种组成要素。
(2)Uniform inputs,表示一些与三维渲染有关的离散信息数据,这些数据通常由应用程序传入,并通常不会随着图元信息的变化而变化,如材质对光的反射信息、运动矩阵等。
需要注意的一点是:uniform修辞的变量的值是从外部传入的,所以在Cg程序(顶点程序和片元程序)中通常使用uniform参数修辞函数形参,不容许声明一个用uniform修辞的局部变量!
2、Cg输入/输出修饰符
参数传递是指:函数调用实参值初始化函数形参的过程。
in:修辞一个形参只是用于输入,进入函数体时被初始化,且该形参值的改变不会影响实参值,这是典型的值传递方式。
out:修辞一个形参只是用于输出的,进入函数体时并没有被初始化,这种类型的形参一般是一个函数的运行结果。
inout:修辞一个形参既用于输入也用于输出,这是典型的引用传递。
3、Cg输入语义和输出语义的区别
语义,是两个处理阶段(顶点程序、片元程序)之间的输入/输出数据和寄存器之间的桥梁,同时语义通常也表示数据的含义,如 POSITION 一般表示参数种存放的数据是顶点位置。
语义,只对两个处理阶段的输入/输出数据有意义,也就是说语义只有在入口函数中才有效,在内部函数(一个阶段的内部处理函数,和下一个阶段没有数据传递关系)的无效,被忽略。
语义,分为输入语义和输入语义,输入语义和输出语义是有区别的。
4、顶点着色器的输入语句
参数使用语义使用举例:
in float4 modelPos: POSITION
表示该参数中的数据是的顶点位置坐标(通常位于模型空间),属于输入参数,语义词 POSITION 是输入语义,如果在 OpenGL 中则对应为接受应用程序传递的顶点数据的寄存器(图形硬件上)。
5、顶点着色器的输出语句
顶点程序的输出数据被传入到片断程序中,所以顶点着色程序的输出语义词,通常也是片元程序的输入语义词,不过语义词POSITION除外。
这些语义词适用于所有的Cg vertex profiles作为输出语义和Cg fragment profiles的输入语义:POSITION、PSIZE、FOG、COLOR0-COLOR1、TEXCOORD0-TEXCOORD7。
顶点着色程序必须声明一个输出变量,并绑定POSITION语义词,该变量中的数据将被用于光栅化,且只被用于光栅化!如果没有声明一个绑定POSITION语义词的输出变量就会报错。
示例:
void main_v(float4 position: POSITION,
out float4 oposition : POSITION,
uniform float4x4 modelViewProj)
{
//oposition = mul(modelViewProj,position);
}
为了保持顶点程序输出语义和片元程序输入语义的一致性,通常使用相同的 struct类型数据作为两者之间的传递,这是一种非常方便的写法,推荐使用。
示例:
struct VertexIn {
float4 position : POSITION;
float4 normal : NORMAL;
};
struct VertexScreen {
float4 oPosition : POSITION;
float4 objectPos : TEXCOORD0;
float4 objectNormal : TEXCOORD1;
};
注意:当使用struct结构中的成员变量绑定语义时,需要注意到顶点着色程序中使用的POSITION语义词,是不会被片元程序所使用的。
如果需要从顶点着色程序向片元程序传递数据,例如顶点投影坐标、光照信息等,则可以声明另外的参数,绑定到TEXCOORD系列的语义词进行数据传递,,实际上TEXCOORD系列的语义词通常都被用于从顶点程序向片段程序之间传递数据。
当然,你也可以选择不使用struct结构,而直接在函数形参中进行语义绑定。无论使用何种方式,都要记住vertex program中的绑定语义(POSITION除外)的输出形参中的数据会传递到fragment program中绑定相同语义的输入形参中。
6、片元着色器的输入语义
片元着色器的输出语义词较少,通常是COLOR。这是因为片元着色器运行完毕后,就基本到了GPU流水线的末端了。片元程序必须声明一个out向量(三元或四元),绑定语义词COLOR,这个值将被用作该片元的最终颜色值。 例如:
void main_f(out float4 color : COLOR)
{
color.xyz = float3(1.0,1.0,1.0);
color.w = 1.0;
}
7、语义绑定方法
入口函数输入\输出数据的绑定语义有四种方法:
//绑定语义放在函数的参数列表的参数声明后面中
[const] [in|out |inout]<type><identifier> [ : <binding-semantic>][=<initializer>]
//绑定语义可以放在结构体(struct)的成员变量后面
struct <struct-tag> {
<type><identifier> [:<binding-semantic >];
};
//绑定语义词可以放在函数声明的后面
<type> <identifier> (<parameter-list>) [:<binding-semantic] {
<body>
}
//最后一种语义绑定的方法是,将绑定语义词放在全局非静态变量的声明后面
<type> <identifier> [:<binding-semantic>][=<initializer>];
四、函数与程序设计
1、Cg函数
Cg 语言中的函数声明形式与 C\C++中相同,由返回类型(return type)、函数名、形参列表(parameter list, 位于括号中,并用逗号分隔的参数表)和函数体组成,函数体包含在花括号中。 如果没有返回值,则函数的返回类型是 void。
Cg 语言支持函数重载(Functon Overlaoding),其方式和 C++基本一致,通过形参列表的个数和类型来进行函数区分。
所谓入口函数,即一个程序执行的入口,例如 C\C++程序中的 main()函数。通常高级语言程序中只有一个入口函数,不过由于着色程序分为顶点程序和片断程序,两者对应着图形流水线上的不同阶段,所以这两个程序都各有一个入口函数。
2、Cg标准函数库
和 C 的标准函数库类似,Cg 提供了一系列内建的标准函数。这些函数用于执行数学上的通用计算或通用算法(纹理映射等)。
Cg 标准函数库主要分为五个部分:
数学函数(Mathematical Functions);
几何函数(Geometric Functions);
纹理映射函数(TextureMapFunctions);
偏导数函数(Derivative Functions);
调试函数(Debugging Function);