【引用】 Cg(C for Graphic)语言函数使用方法

来源:互联网 发布:java url编码 编辑:程序博客网 时间:2024/06/07 00:04

摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人”

通过第 5 章到第 7 章的阅读,我们已经知道了怎么声明变量(第 5 章),怎么写表达式和语句(第 6 章),怎么将输入 \ 输出参数绑定到语义词(第 7 章),本章将首先描述 Cg 语言中函数的写法,以及函数是否可以重载;然后阐述顶点 \ 片段着色程序中入口函数的概念(类似 C\C++ 中的 main() 函数);最后,以 Cg 标准函数库来结束本章。

8.1 函数

函数可以被看作一个由用户定义的操作。 Cg 语言中的函数声明形式与 C\C++ 中相同, 由返回类型( return type )、函数名、形参列表( parameter list ,位于括号中,并用逗号分隔的参数表)和函数体组成。函数体包含在花括号中。如果没有返回值,则函数的返回类型是 void 。下面是函数定义的例子:

 

void myFunc(inout float val )

{

 

       ………………

   val += 10.0;

}

 

 

float myFunc(float vals[])

{

   float sum = 0.0;

     ……………………..

   return sum;

}

注意:如果函数没有返回值,函数的返回类型一定要是 void ,否则编译会出现大量的错误,错误信息的大概形式是:

error C0000: syntax error, unexpected’(’at token “(”

error C0501: type name expected at token “(”

error C1110: function “main_v” has no return statement

参数传递机制是函数概念中的重中之重,请参阅 7.4 节 “ 输入 \ 输出修辞符 ” 中的论述。此外,有一个比较特殊的函数形参类型,不论在 C\C++ 中还是在 Cg 语言中,都是一个令人头疼的话题,它就是数组形参。

8.1.1 数组形参

在 C\C++ 中,当一个数组作为函数的形参时,实际上传入的只是指向首元素的指针,并且数组边界被忽略(参阅 stephen C.Dewhurst 所著的《 C++ 必知必会》)。而在 Cg 语言中不存在指针机制(图形硬件不支持),数组作为函数形参,传递的是数组的完整拷贝。关于 Cg 中形参数组的原始资料可以在文献 [3]Array 章节中查到:

 “The most important difference from C is that arrays are first-class types. That means array assignments actually copy the entire array, and arrays that are passed as parameters are passed by value, rather than by reference”.

这段英文中描述道, Cg 语言中数组是“ first-class types ”,中文翻译为 “ 第一类数据类型 ” ,所谓 “ 第一类( first-class ) ” 的含义是,强调该类型数据是 “ 不可分解的、最高级别的、不被重述的 ” ,即 “ 第一类数据类型 ” 和 “ 基础数据类型 ” 的概念是近同的。如有兴趣深入了解 “first-class” 概念,可参阅 Matthieu Sozeau and Nicolas Oury 所著的 “First-Class Type Classes” 一文。

数组类型变量作为函数形参,可以是一维的也可以是多维的,并且不必声明数组长度,即 Unsized Array 。例如:

 

float myFunc( float vals[])

{

float sum = 0.0;

   for(int i = 0; i< vals.length; i++)

   {

      sum += vals[i];

   }

      return sum;

}

myFunc 是一个函数,输入一个数组变量,计算数组中所有数据之和,然后返回 float 类型数据。请注意:数组形参不必指定长度。如果指定了函数中形参数组的长度,那么在调用该函数时实参数组的长度和形参数组的长度必须保持一致,如果没有保持一致,编译时会出现错误提示信息: error C1102: incompatible type for parameter… 。

 

float myFunc( float vals[3])

{

float sum = 0.0;

   for(int i = 0; i< vals.length; i++)

   {

      sum += vals[i];

   }

      return sum;

}

 

void main(…)

{

   float a[2] = {0.0, 1.0};

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

   myFunc(a); // 错误调用,会导致编译错误

   myFunc(b); // 正确调用

}

 

对于函数的形参数组最好不要指定长度,这样就可以片配任意长度的参数数组。

如果函数的形参数组是多维数组,其声明方式和上面是一样的,可以不指定长度;如果指定形参数组长度,则实参数组长度必须保持一致。

8.2 函数重载

Cg 语言支持函数重载( Functon Overlaoding ),其方式和 C++ 基本一致,通过形参列表的个数和类型来进行函数区分。例如:

 

bool function(float a, float b)   {return  ( a == b);}

bool function(boo a, bool b)   {return  ( a == b);}

 

Cg 语言标准函数库中绝大部分函数都被重载过。

8.3 入口函数

所谓入口函数,即一个程序执行的入口,例如 C\C++ 程序中的 main() 函数。

通常高级语言程序中只有一个入口函数,不过由于着色程序分为顶点程序和片断程序,两者对应着图形流水线上的不同阶段,所以这两个程序都各有一个入口函数。

顶点程序和片段程序有且只有一个入口函数,当程序进行编译时,需要指定入口函数名称(参阅 4.4 节 CG 编译),除非入口函数名为 main 。当我们编写或阅读 Cg 代码时,如何区分哪个函数是入口函数呢?或者哪个入口函数对应着顶点程序或片段程序?事实上,顶点程序和片段程序的入口函数形式,已经完全由它们在渲染管线中所处的阶段所决定。在前面已经阐述过,顶点程序接收应用程序传递的顶点数据(通常位于模型坐标空间),然后进行坐标空间转换和光照处理,最后输出投影坐标和计算得到的光照颜色;而片段程序接收从顶点程序输出的数据,并进行像素颜色计算。在片段程序中往往涉及到纹理颜色的处理,其输入参数中常有纹理形参的声明。所以通过观察程序的输入输出语义绑定(参阅 7.5 节语义词与语义绑定),就可以区分入口函数对应到顶点程序还是片段程序。而内部函数则忽略任何应用到形参上的语义,通常也没有人会在内部函数使用语义词,除非他 \ 她的目的是练习打字速度。

下面的代码展示了一个顶点程序的入口函数,名称为 C2E1v_green ,这个顶点着色程序接收二维顶点数据,然后转换为齐次坐标(请思考,顶点和向量的齐次坐标有什么不同?齐次坐标的本质是什么?),并将该顶点设置为绿色,最后使用 return 语句输出。如果电脑安装了 Cg ,该程序文件位于 “NVIDIA Corporation\Cg\examples\OpenGL\basic\ 01_vertex_program\C2E1v_green.cg” 目录下 。

 

struct C2E1v_Output {

  float4 position : POSITION;

  float3 color    : COLOR;

};

 

C2E1v_Output C2E1v_green(float2 position : POSITION)

{    

  C2E1v_Output OUT;

 

  OUT.position = float4(position,0,1);

  OUT.color = float3(0,1,0);

 

  return OUT;    

}

0 0
原创粉丝点击