摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人”
第三章从 GPU 运行原理和数据流程的角度阐述了顶点着色程序和片段着色程序的输入输出,即,应用程序(宿主程序)将图元信息(顶点位置、法向量、纹理坐标等)传递给顶点着色程序;顶点着色程序基于图元信息进行坐标空间转换,运算得到的数据传递到片段着色程序中;片段着色程序还可以接受从应用程序中传递的纹理信息,将这些信息综合起来计算每个片段的颜色值,最后将这些颜色值输送到帧缓冲区(或颜色缓冲区)中。
这些是顶点着色程序和片段着色程序的基本功能和数据输入输出,实际上现在的着色程序已经可以接受多种数据类型,并灵活的进行各种算法的处理,如,可以接受光源信息(光源位置、强度等)、材质信息(反射系数、折射系数等)、运动控制信息(纹理投影矩阵、顶点运动矩阵等),可以在顶点程序中计算光线的折射方向,并传递到片段程序中进行光照计算。
这一章节中,我们将讲解Cg 语言通过何种机制确定数据类型和传递形式。读者要抱着如下几个问题阅读本章节:
1. 从应用程序传递到 GPU 的数据,分为图元信息数据(在GPU 处理的基本数据如顶点位置信息等)和其他的离散数据(在GPU 运行流程中不会发生变化,如材质对光的反射、折射信息),这两种输入数据如何区分?
2. 从应用程序传递到GPU 中的图元信息如何区分类型,即,顶点程序怎么知道一个数据是位置数据,而不是法向量数据?
3. 顶点着色程序与片段着色程序之间的数据传递如何进行?
7.1 Cg 关键字
关键字是语言本身所保留的一个字符串集合,用于代表特定的含义,如前面所讲到的数据类型关键字 int 、 float 等,以及结构体关键字 struct 。 Cg 中的关键字很多都是照搬 C/C++ 中的关键字,不过 Cg 中也创造了一系列独特的关键字,这些关键字不但用于指定输入图元的数据含义(是位置信息,还是法向量信息),本质也则对应着这些图元数据存放的硬件资源(寄存器或者纹理),称之为语义词( Semantics ),通常也根据其用法称之为绑定语义词( binding semantics )。
除语义词外, Cg 中还提供了三个关键字, in 、 out 、 inout ,用于表示函数的输入参数的传递方式,称为输入 / 输出关键字,这组关键字可以和语义词合用表达硬件上不同的存储位置,即同一个语义词,使用 in 关键字修辞和 out 关键词修辞,表示的图形硬件上不同的寄存器。
Cg 语言还提供两个修辞符: uniform ,用于指定变量的数据初始化方式; const 关键字的含义与 C/C++ 中相同,表示被修辞变量为常量变量。
下面将分别对上述的关键字进行详细阐述。这一章非常关键,尤其是语义词的使用方法和含义,再小的 Cg 程序都需要使用到语义词。
7.2 uniform
Cg 语言将输入数据流分为两类(参见文献[3] Program inputs and Outputs ):
1. Varying inputs , 即数据流输入图元信息的各种组成要素。从应用程序输入到 GPU 的数据除了顶点位置数据,还有顶点的法向量数据,纹理坐标数据等。 Cg 语言提供了一组语义词,用以表明参数是由顶点的哪些数据初始化的。
2. Uniform inputs ,表示一些与三维渲染有关的离散信息数据,这些数据通常由应用程序传入,并通常不会随着图元信息的变化而变化,如材质对光的反射信息、运动矩阵等。 Uniform 修辞一个参数,表示该参数的值由外部应用程序初始化并传入;例如 在参数列表中写:
uniform float brightness,
uniform float4x4 modleWorldProject
表示从“外部”传入一个 float 类型数据,和一个4 阶矩阵。“外部”的含义通常是用 OpenGL 或者 DirectX 所编写的应用程序。
使用 Uniform 修辞的变量,除了数据来源不同外,与其他变量是完全一样的。
需要注意的一点是: uniform 修辞的变量的值是从外部传入的,所以在Cg 程序(顶点程序和片段程序)中通常使用 uniform 参数修辞函数形参,不容许声明一个用 uniform 修辞的局部变量!否则编译时会出现错误提示信息:
Error C5056:’uniform’not allowed on local variable
7.3 const
Cg 语言也提供 const 修辞符,与 C/C++ 中含义一样,被 const 所修辞的变量在初始化之后不能再去改变它的值。下面的例子程序中有一个声明为 const 的变量被赋值修改:
const float a = 1.0;
a = 2.0; // 错误
float b = a++; // 错误
编译时会出现错误提示信息: error C1026: assignment to const variable 。
const 修辞符与 uniform 修辞符是相互独立的,对一个变量既可以单独使用 const 或者 uniform ,也可以同时使用。
7.4 输入/ 输出修辞符( in/out/inout )
参数传递是指:函数调用实参值初始化函数形参的过程。在 C/C++ 中,根据形参值的改变是否会导致实参值的改变,参数传递分为 “ 值传递( pass-by-value ) ” 和 “ 引用传递( pass-by-reference ) ” 。按值传递时,函数不会访问当前调用的实参,函数体处理的是实参的拷贝,也就是形参,所以形参值的改变不会影响实参值;引用传递时,函数接收的是实参的存放地址,函数体中改变的是实参的值。 C/C++ 采取指针机制构建引用传递,所以通常引用传递也称为 “ 指针传递 ” 。
Cg 语言中参数传递方式同样分为 “ 值传递 ” 和 “ 引用传递 ” ,但指针机制并不被 GPU 硬件所支持,所以 Cg 语言采用不同的语法修辞符来区别 “ 值传递 ” 和 “ 引用传递 ” 。这些修辞符分别为:
1. in : 修辞一个形参只是用于输入,进入函数体时被初始化,且该形参值的改变不会影响实参值,这是典型的值传递方式。
2. out : 修辞一个形参只是用于输出的,进入函数体时并没有被初始化,这种类型的形参一般是一个函数的运行结果;
3. inout : 修辞一个形参既用于输入也用于输出,这是典型的引用传递。
举例如下:
void myFunction(out float x); // 形参 x ,只是用于输出
void myFunction(inout float x); // 形参 x ,即用于输入时初始化,也用于输出数据
void myFunction(in float x); // 形参 x ,只是用于输入
void myFunction(float x); / 等价与 in float x ,这种用法和 C/C++ 完全一致
也可以使用 return 语句来代替 out 修辞符的使用。输入 / 输出修辞符通常和语义词一起使用,表示顶点着色程序和片段着色程序的输入输出。