我们知道了进行Shader编程的Shader Lab语言有三种,分别是:OpenGL的GLSL、Direct3D的HLSL和NAVIDIA公司的Cg语言。
由于Cg编写的程序可以不作任何处理就能同时在OpenGL和Direct3D上运行,所以我选择了Cg作为开发Shader的编程语言,接下来就来了解Cg的一些基本特性。
一、Cg的编译方式:
1.编译:
编译程序:
计算机只能理解和执行由 0 、 1 序列(电压序列)构成的机器语言,所以汇编语言和高级语言程序都需要进行翻译才能被计算机所理解, 担负这一任务的程序称为语言处理程序,通常也被称为编译程序。
静态编译:
一旦编译后,除非改变程序代码,否则不需要重新编译,这种方式称为静态编译( static compilation ) 。静态编译最重要的特征是:一旦编译为可执行文件,在可执行文件运行期间不再需要源码信息。
动态编译:
编译程序和源码都要参与到程序的运行过程中。
Cg通常采用动态编译的方式(Cg也支持静态编译方式),即在宿主程序运行时,利用Cg运行库(Cg Runtimer Library)动态编译Cg代码。使用动态编译的方式,可以将Cg程序当做一个脚本,随时修改随时运行,节省时间,在OGRE图形引擎中就采用了这种方式。
2.Cg编译器:
Cg 编译器首先将 Cg 程序翻译成可被图形 API ( OpenGL 和 Direct3D )所接受的形式, 然后应用程序使用适当的 OpenGL 和 Direct3D 命令将翻译后的 Cg 程序传递给图形处理器, OpenGL 和 Direct3D 驱动程序最后把它翻译成图形处理器所需要的硬件可执行格式。NVIDIA 提供的 Cg 编译器为 cgc.exe。
安装Cg ToolKit之后,在NAVIDIA Corporation\Cg\bin文件夹下,就能找到cgc.exe文件。各种命令:
Cg程序编译的命令:cgc [options] file
—— [options]表示可选配置项,file 表示 Cg 程序文件名。
例如,比较典型的编译方式:cgc -profile glslv -entry main_v test.cg
—— -profile是profile配置项名; glslv是当前所使用的profile名称; -entry着色程序的入口函数名称配置项;main_v 是顶点着色程序的入口函数名;test.cg 是当前的着色程序文件名(必须带后缀名)。
将Cg语言所写的着色程序转换为使用GLSL或HLSL所编写的程序:
cgc –profile glslv –o direct.glsl –entry main_v test.cg
—— 表示编译文件 test.cg 中的顶点着色程序, 入口函数名为 main_v , 并将顶点着色程序转换为 glsl 程序,然后保存成文件 direct.glsl 。
备注:GPU编程,是无法跟踪调试着色程序的,一个着色程序,语法错误可以通过编译器发现,但是代码的逻辑错误只能认为查找。
3.Cg Profiles:
Cg 程序的编译不但依赖于宿主程序所使用的三维编程接口,而且依赖于图形硬件环境,因为图形硬件自身的限制,不一定支持某种 Cg 语句。
被特定的图形硬件环境或 AIP 所支持的 Cg 语言子集,被称为Cg Profiles 。profile分为:顶点程序的profile和片段程序的profile,所以编译顶点着色程序时必须选用当前图形硬件支持的顶点profile ,同理,编译片段着色程序时必须选用当前图形硬件支持的片段profile 。顶点 profile 和片段 profile 又基于 OpenGL 和 DirectX 的不同版本或扩展,划分为各种版本。
当前 Cg compiler 所支持的 profiles 有:
OpenGL ARB vertex programs
Runtime profile: CG_PROFILE_ARBVP1
Compiler option: _profile arbvp1
OpenGL ARB fragment programs
Runtime profile: CG_PROFILE_ARBFP1
Compiler option: _profile arbfp1
......
二、CG数据类型:
1.基本数据类型:Cg支持7种基本的数据类型,分别是:
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);
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};
float a[10];//声明了一个数组,包含 10 个 float 类型数据
float a[4] = {1.0, 2.0, 3.0, 4.0}; //初始化一个数组
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
struct myAdd
{
float val;
float add(float x)
{
return val + x;
}
};
myAdd s;
float a = s.value;
float b = s.add(a);
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 类型
float a = 1.0;
float b = a + 2.0h; //2.0h 为 half 类型常量数据,运算是需要做类型转换