《GPU编程与CG语言之阳春白雪下里巴人》- 第四章(CG 语言概述)

第04章 CG 语言概述

人们常觉得准备的阶段是在浪费时间,只有当真正机会来临,而自己没有能力把握的时候,才能觉悟自己平时没有准备才是浪费了时间。
                                   ------ 罗曼.罗兰

Cg(C for Graphcis)语言,是 NVIDIA 于 Microsoft 合作研发,旨在为开发人员提供一套方便、跨平台(良好的兼容性),控制可编程图形硬件的高级语言。Cg 语言的语法结构与 C 语言非常类似,使用 Cg 编写的着色程序默认的文件后缀是 *.Cg。

4.1 开启 Cg 之旅

在 NVIDIA 的 http://develpoer.nvidia.com/object/cg_toolkit.html 网页上下在 Cg Toolkit,截止到 2009 年 10 月,Cg 语言的版本为 2.2。下载之后直接安装即可。在安装目录的 bin 目录下一个可执行程序:cgc.exe。这是 NVIDIA 提供的 Cg 程序编译器。
  目前还没有一个主流的专门为编写着色程序而开发的 IDE,很多人都是直接在文本写好程序后,然后将文件后缀改为 .cg。在网上有一个名为 NShader (http://nsshader.codeplex.com/) 的 Visual Studio 2008 插件,安装之后可以支持编写着色程序。图 11 展示了使用该插件之后的使用效果。

图11.png

4.2 CG 特性

Cg 同时被 OpenGL 与 Direct3D 两种编程 API 所支持。这一点不但对开发人员而言非常方便,而且也赋予了 Cg 程序良好的跨平台性。一个正确编写的 Cg 应用程序可以不做任何修改的同时工作在 OpenGL 和 Direct3D 之上。

以前编写 Shader 是非常痛苦的,尤其是作为跨平台渲染开发者,不同的渲染环境要写几套 Shader, GLSL、HLSL 等等,目前由于一些新兴的渲染环境增加导致 Shader 编写更加痛苦,所以聪明的开发这借鉴当下比较流行的中间语言方式,对 Shader 编程也做了 IL 化,日历目前大火且开源的 SPIRV,有兴趣的读者可以去 github 阅读一下源码。

4.3 CG 编译

编译(compilation , compile)
1、利用编译程序从源语言编写的源程序产生目标程序的过程。
2、用编译程序产生目标程序的动作。
通俗点理解其实编译就是把高级语言变成计算机可以识别的2进制语言,计算机只认识1和0,编译程序把人们熟悉的语言换成2进制的。

4.3.1 CG 编译原理

前面提到的编译概念讲到计算机只能理解和执行由 0、1 序列(电压序列)构成的机器语言,所以汇编语言和高级语言程序都需要进行翻译才能被计算机所理解,担负这一任务的程序称为语言处理程序,通常也被称为编译程序。例如 C 或者 C++ 编写的程序,需要首先编译成可执行文件(.exe 文件),然后才能在 GPU 上运行,且一旦编译后,除非改变程序代码,否则不需要重新编译,这种方式称为静态编译(static compilation)。静态编译最重要的特征是:一旦编译为可执行文件,在可执行文件运行期间不再需要源码信息。而动态编译(dynamic compilation)与之相反,编译程序和源码都要参与到程序的运行过程中。
  Cg 语言通常采用动态编译的方式,即,在宿主程序运行时利用 Cg 运行库(Cg Runtime Library)动态编译 Cg 代码,使用动态编译的方式,可以将 Cg 程序当做一个脚本,随时修改随时运行,节省大量的时间,在 OGRE 图形引擎中就是采用这样的方法。在文献【2】的 1.4.2 章节中提到 Cg 语言同样支持静态编译方式,即,Cg 源码编译成汇编代码后,这部分目标代码被链接到宿主程序最后的可执行程序中。使用静态编译的好处是只要发布可执行文件即可,源码不会被公开。
  Cg 编译器首先将 Cg 程序翻译成可被图形 API(OpenGL 和 Direct3D)所接受的形式,然后应用程序使用适当的 OpenGL 和 Direct3D 驱动程序最后把他翻译成图形处理器所需要的硬件可执行格式。NVIDIA 提供的 Cg 编译器为 cgc.exe。
  Cg 程序的编译不但依赖于宿主程序所使用的三维编程接口,而且依赖于图形硬件环境,因为图形硬件自身的限制,不一定支持某种 Cg 语句,例如,如果你所使用的 CPU 并不支持循环控制指令,那么在 Cg 程序中编写的循环控制语句将无法通过编译。被特定的图形硬件环境或 API 所支持的 Cg 语言子集,被称为 Cg Profiles。需要注意的是:profile 分为顶点程序的 profile 和片段程序的 profile,这是因为顶点着色器和片段着色器原本就不是工作在同一个硬件。
  Cg Profile 是 Cg 语言的重要组成部分,在使用 Cg 语言编写着色程序时,首先要考虑的一点就是“当前的图形硬件环境支持哪个 Cg Profile”,这直接关系到您所编写的着色程序时否可以在当前的图形硬件上运行。

4.3.2 CGC 编译命令

如果 Cg Toolkit 安装正确,在 NVIDIA Corporation\Cg\bin 文件夹下会看到 cgc.exe 文件。首先打开命令行窗口,输入 cgc -h,如果安装正确,则会出现图 12 所示的提示信息。

图12.png

  Cg 程序编译的命令形式为:cgc [options] file
  [options] 表示可选配置项,file 表示 Cg 程序文件名。可选配置项包括编译时选择使用 profile、着色程序的入口函数名称,以及着色程序文件名。比较典型的编译方式是:cgc -profile glslv -entry main_v test.cg
  -profile 是 profile 配置项名;glslv 是当前所使用的 profile 名称;-entry 着色程序的入口函数名称配置项;main_v 是顶点着色程序的入口函数名;test.cg 是当前的着色程序文件名。编译器指定的着色程序入口函数名默认为 main,通常为了将顶点\片段着色程序入口函数名区别开来,而并不适用默认名称。在下面所有的例子中,main_v 表示顶点着色程序入口函数名,main_f 表示片段着色程序入口函数名。
   需要强调如下几点:
  1. 着色程序分为顶点着色程序和片段着色程序,profile 也分为顶点 profile 和 片段 profile,所以编译顶点着色程序时必须选用当前图形硬件支持的顶点 profile,同理,编译片段着色程序时必须选用当前图形硬件支持的片段 profile。下面所示使用片段 profile fp20 编译顶点着色程序是不对的。

  cgc -profile fp20 glslv -entry main_v test.cg

所以,如果您的着色程序中同时窜在顶点着色程序和片段着色程序,在编译前切记分别选择各自的 profile。
  2. 选择 profile 如果不被当前图形硬件所支持,编译时会出现错误。被编译的着色程序中,如果存在被被所选择的 profile 所支持的语句,则编译时会出现错误。例如,tex2D(sampler2D tex,float3 sz,float2 dsdx,float2 dsdy)不被 fp20 所支持,如果泥的编译形式为:

  cgc -profile fp20 glslv -entry main_f test.cg

则会出现错误提示信息:

  error c3004:function "tex2D" not supportd in this profile.

改为 fp30,进行编译就会通过。

  cgc -profile fp30 glslv -entry main_f test.cg

尤其需要注意的是,循环语句 for, while 只被 vs_2_x, vp30, vp40, fp40 等少量的 profiles 所支持。在 CgUsersManual 中提到 “In other profiles, for and while loops may only be used if the compiler can fully unroll them (that is,if the compiler can determine the iteration count at compile time)”,这句话的意思是“在其他的 profiles 中,for 和 while 循环只有当确切的知道循环次数时才能被使用”。但经过试验,通常在其他 profile 编译含义 for, while 语句时会出现错误提示信息:

  error c6003:instruction limit of exceeded......

因此,如果没有确切的把握,不要在低级的 profiles 中使用循环控制语句。
  3. 被编译的着色程序文件名必须加上 .cg 后缀。如果没有加后缀,写成如下的形式:

  cgc -profile glslv -entry main_v

则会出现错误提示信息:

  fatal error C9999:Can't open file:test

4. 另外 cgc 还提供一种比较特殊的功能:就是将 Cg 语言所写的着色程序转换为使用 GLSL 或 HLSL 所编写的程序。例如,将代码写成如下形式,表示编译文件 test.cg 中的顶点着色程序,入口函数名为 main_v, 并将顶点着色程序转换为 glsl 程序,然后保存成文件 direct.glsl。

  cgc -profile glslv -o direct.glsl -entry main_v test.cg

5. 还有一个非常隐藏的编译情况是:如果着色程序中的某些变量并没有为最终的输出做出贡献,则编译时会将该部分代码忽略(会检查语法错误,但并不编译成汇编代码)。通常这一点不会造成太大的影响,但是如果这些变量刚好是从外部宿主程序中传入的变量,并且在着色程序中没有被使用,则宿主程序传入变量的接口函数可能会报错“找不到该变量”。这种情况比较少遇到,但并非不存在,且一旦遇上问题的原因难以查明,故而我在此写上,希望可以有所帮助。
  基于 GPU 编程,最令人崩溃的一点是:无法跟踪调试着色程序!这一点目前还没有解决方案出现(对于iOS开发者如果用Metal开发的话到不会有这个苦恼,苹果真香~)。这一点到目前为止还没有好的解决方式出现(苹果的Metal是个案)。对于一个着色程序,语法错误可以通过编译器发现,而代码逻辑错误只能是人为查找。常会遇到这种情况,一段代码编译通过,但是运行结果不在预期之中,如果是 C++\JAVA 程序就可以进行跟踪调试,但是着色程序不能被调试,只能一行代码一行代码的进行逻辑分析。
  所以,编译着色程序要非常注意逻辑的严密性,和代码的组织结构,这是为了更加容易的暴露错误和维护代码。一个良好的习惯是加入注释语句。

4.4 CG Profiles

Profile 在英文中意思是“侧面、轮廓”,文献【1】第三页写到:A Cg Profile defines a subset of the full Cg language that is supported on a particular hardware platform or API(CgUsersManual 21 页)。即一个Cg Profiles 定义了一个“被特定图形硬件或 API 所支持的 Cg 语言子集”,从前面的分析我们可以知道,任意一种 Shader Language 都是基于可编程图形硬件的(寄存器、指令集等),这也就意味着:不同的图形硬件对应着不同的功能子集。Profiles 按照功能可以划分为顶点 Profiles 和片段 Profiles,而顶点 Profiles 和片段 Profiles 又是基于 OpenGL 和 DirectX 的不同版本或扩展,划分为各种版本。从某种意义上而言,OpenGL 和 DirectX 的发展历程成就了 Cg 语言。
当前 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

  • OpenGL NV40 vertex programs
    Runtime Profile: CG_PROFILE_VP40
    Compiler option:_profile vp40

  • OpenGL NV40 fragment programs
    Runtime Profile: CG_PROFILE_FP40
    Compiler option:_profile fp40

  • OpenGL NV30 vertex programs
    Runtime Profile: CG_PROFILE_VP30
    Compiler option:_profile vp30

  • OpenGL NV30 fragment programs
    Runtime Profile: CG_PROFILE_FP30
    Compiler option:_profile fp30

  • OpenGL NV2X vertex programs
    Runtime Profile: CG_PROFILE_VP20
    Compiler option:_profile vp20

  • OpenGL NV2X fragment programs
    Runtime Profile: CG_PROFILE_FP20
    Compiler option:_profile fp20

  • DX9 vertex shaders
    Runtime Profile: CG_PROFILE_VS_2_X/CG_PROFILE_VS_2_0
    Compiler option:_profile vs_2_x/_profile vs_2_0

  • DX9 pixel shaders
    Runtime Profile: CG_PROFILE_PS_2_X/CG_PROFILE_PS_2_0
    Compiler option:_profile ps_2_x/_profile ps_2_0

  • DX8 vertex shaders
    Runtime Profile: CG_PROFILE_VS_1_1
    Compiler option:_profile vs_1_1

  • DX8 pixel shaders
    Runtime Profile: CG_PROFILE_PS_1_3/CG_PROFILE_PS_1_2/CG_PROFILE_PS_1_1
    Compiler option:_profile ps_1_3/_profile ps_1_2/_profile ps_1_1

附:截止到 2009 年 10 月,出现的 Profile 已经不止上面这些种类了,尤其是现在 DirectX 已经出到了 11 的版本。上面的 Profile 是可以在当前大多数机器上使用的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351