HLSL、Cg及RenderMonkey 简介

简介
简介

所有的事情都是在那一瞬间发生的。数年前第一次发生的时候,情况显得nVidia对个人计算机显卡市场的影响力似乎即将下滑。当时ATI(冶天)所推出全新的显卡比nVidia最高端的产品还要快、功能更多,而nVidia新的产品似乎也还要好几个月才能推出。

同时,冶天与nVidia均推出软件研发套件,以借此来掌握之后显示软件开发的主控权。冶天推出的产品称为RenderMonkey,名字十分炫,而nVidia所推出的叫做Cg,代表的是绘图显示用的C语言。两者之间的差异相当大。RenderMonkey的本质是用以发展新材质用的研发环境,而Cg则是种全新的程序设计语言。

在本文中,我将介绍各种的HLSL–high level shading languages,高端着色语言。它们的基本介绍、它们的优点、以及为什么绘图程序设计师必须学习它们。之后,我将简短地分析材质的概念以及RenderMonkey除了名字之外引人入胜之处。

HLSL

计算机绘图的应用程序接口在过去数年之间变动极为快速。往往各显卡支持其中的某些功能,而程序设计师的工作便是自其中压榨出最大的功能。在最近,我们则可发现市场已逐渐转型为可程序化显示硬件的应用。

目前所推出的可程序化显卡包括有nVidia的Geforce 3及Geforce 4钛系列的产品(不含GF4MX系列)、Xbox主机(GF3.5)、Matrox(迈拓) Parhelia、3Dlabs的P10及冶天所推出的Radeon 8500、9000、9700镭系列显卡。然而,这些显卡的可程序化特性均被列为机密,并仅能通过DirectX 8(DX8)内的顶点着色(VS)及像素着色(PS)接口来使用。

两者间共通较多的部份是vertex shader(顶点着色)。这项功能让程序设计师可以指定最多128项指令给显卡对单一顶点进行处理。若显卡本身并不支持顶点着色功能,这些指令仍可以交由中央处理器运作,只是在旧款光影与形变功能的显卡(GF1/2、Radeon等)上,往往无法利用固定功能的像素管线让简单的处理程序加快(即不使用顶点着色功能)。

在像素着色接口则较复杂,使用上的限制亦较多。最重要的是,像素着色功能仅能在支持此项功能的显卡上运作;因此在较旧的光影变形显卡上,研发人员被迫必须要研发pixel shader(像素着色功能)及non-pixel shader(非像素着色)技术。在实际操作上,这将让游戏设计者将像素着色功能视为额外的指令集,因此在核心显示功能设计时,并不会在这方面投资太多的时间。

时至今日,研发套件已成为基本工具,大部份的程序设计师仅需要以文字编辑器来发展其绘图效果即可。

因此,又有一些新的事情发生了。首先,我们需要一套全新的研发环境,让绘图设计师可以实时看到他们所创造的

效果如何。其次,我们需要一些特殊的程序设计语言让绘图设计工作对一般人而言更为容易。

汇编程序设计
汇编程序设计

先让我们花些时间回忆一下当初处理器萌芽期的情形。当时的程序设计师所擅长的是一般人所不懂的汇编语言。对大部份不曾体验过这种痛楚的人可以想象汇编语言就像是设计师直接向计算机处理器下指令以执行动作,唯一的差别只是你并不是真的用二进制的语言对处理器下指令。

所有计算机在最低端的部份,都是以0和1进行运作。这一连串告诉处理器该执行什么任务的数据称之为机械码,实际上看起就像是000100111010010110110101之类的数据串。很明显,这不是设计处理器程序最直觉的方式,这亦是汇编语言推出的原因。开发者依然直接设计指令串流,但使用的是人类更容易阅读的接口。例如,将内存内某位置的数值搬移至处理器内的缓存器的指令变成像是这样:

MOV AX, [BX],

若将其翻译,则其机械码为01110001、八进位码1010、八进位1001或0111000110101001。

使用这一小段的汇编语言,开发者可以设计出相当复杂的程序。但在一段时间之后,人们开始厌烦不断重复这些工作。而时势进展至此时,人们开始发展高端程序语言。基本上,这些语言在汇编语言上加上额外一层间接的指令,让人类可以更容易读懂这些程序代码。例如,请想象下面的这段汇编语言判断某一个数值,并依此数值是否大于5写入第二个数值:

MOV AX, number // just imagine
CMP AX, 5
JG true
MOV CX, 2
JMP end
true:
MOV CX, 1
end:

而在C语言中,它可以被写成这个样子:

if (number>5)
result = 1;
else
result = 2;

因此在这个例子中,C已经赋予开发者撰写更容易解读程序代码的能力,它仅需在比较数值后进行新值的设定。而在汇编语言中,若设计者没有好好的为其程序代码加上批注,其接续的程序修改人员将会有很大的问题,往往需要将程序重头分析才能了解该程序代码原本的意义。

C语言同时让程序设计人员只需撰写一次程序代码,之后便可在不同的作业平台上进行重新编译的工作。这种高端程序语言的概念将使得程序发展小组在不同平台(像是Xbox、GameCube、PS2等)上开发软件的工作简单了很多。


HLSL高端着色语言
HLSL高端着色语言

在了解这些之后,我们可以这样说,HLSL相对于顶点着色/像素着色引擎用的汇编语言,就像是C语言相对于中央处理器用的汇编语言一般。例如,我们先看一下下面这段程序代码,其目的是进行方向性打光的计算,并将其输出至漫射色彩缓存器。

在VS(顶点着色)中,其程序代码为:

mov r0, c0 //光线

方向
mov r1, v2 //法向
dp3 r2, r0, r1 //求内积值
mov r3, c1 //将0, 0, 0, 0存至r3
max r0, r2, r3 //将光线设为0
mov oD0, r0 //输出漫射色彩

或若将mov拿掉:

dp3 r0, c0, v2
max oD0, r0, c1

而在HLSL中,设计者仅需这样做:

float4 lightdirection = constant[0];
OUT.Diffuse = max( dp3(IN.Normal, lightdirection), 0 );

OK,HLSL到底好在哪里的问题已经有了答案。基本上,现在你已经知道HLSL的功能为何了。若你再阅读顶点着色引擎的程序代码一次,你要了解其程序的目的为何也并不简单。若方向性光线的等式并没有完整显示出来,读者仍无法了解为什么程序代码要如此的撰写,以及其运作的过程为何。在HLSL的范例之中,程序代码很清楚地表明我们正以法向及光线的方向进行算,将其设定为0,并将此值输出至漫射组件之中,以作为最终的显示。当我们使用指令超过50的着色引擎时,程序的可辨读性将会越显重要。因此,使用汇编语言的着色引擎将会变得极为复杂难懂;而HLSL版本将会易为人类所阅读,这项优势亦将带来更少的程序错误以及更容易维护及编辑。

HLSL的平台独立性则尚待测试。目前看来,每一新世代的产品总是与其前一代的产品有着极大的差异。虽然仍能撰写出同时能在DX8与DX9上运行的着色引擎,我仍无法想象这对于DX9的应用上不会造成损害。既然HLSL已经推出几年的时间,这兼容性问题理当有更好的支持才对。

HLSL并不改变硬件
HLSL并不改变硬件

虽然HLSL是设计VS/ PS效果极方便的方法,它仍无法克服在硬件上先天所具有的限制。更重要的是,使用HLSL的程序设计师必须要知道他们所将撰写软件的硬件平台为何,并要对于该系列产品在硬件上的限制先有通透的了解才行。

支持DX8的硬件而言,顶点着色引擎最基本的限制在于固定式内存限制以及指令限制。DX8的着色引擎亦对状况处理的数目有限制,同时并不支持指令预测及跳跃的功能。这代表人们在未来可能会撰写具判别分支的HLSL程序,但他们仍必须考虑如何在DX8的系统平台上把这些分支判断的程序移除。

当你正在使用HLSL时,这些限制并不会明白地告诉你。结果是设计者往往不知道他们已经超过了指令限制继续进行设计,直到编译器发出错误信息才发现。HLSL对于不完全了解硬件的设计者而言亦会带来许多复杂的问题。例如,以Cg的循环程序代码为例:DX8的顶点着色引擎并不允许循环结构,但Cg却能让设计者撰写重复固定次数的程序代码。实际上的结果是该循环在程序中被摊开成连续的程序段,而着色引擎则保持着同样这一段程序代码直到程序指定的次数已经达成。因此一个内含4个指令

重复4次的回路,对顶点着色引擎则是变成16项指令。若设计师并不了解循环程序段分支的方式,将会造成很大的问题,尤其当设计师希望以8种光线进行循环、每种光线又需执行8种指令时。这将让指令产生许多的问题。

至于像素着色引擎部份,硬件的限制比对顶点着色引擎的影响来得大。在一开始,像素着色引擎的指令集便已被分隔为两大块。第一块是材质寻址指令,而第二块则是算术指令。这两指令永远都需被分开执行。所以设计师必须先加载所有他所需要的材质,并根据材质的数值进行运算,以算出最终输出的色彩。

因此,若你希望加载两种材质并利用其读入环境立体图,那么更好的作法应该是:

tex t0 //读取材质0
tex t1 //读取材质1
mad r0, t0, t1, c0 // c0 = 0.5
texload t2, r0

不幸地是,这将需要混用材质区块与算术区块的指令。事实上,亦无法直接加载在暂时缓存器内的中间结果作为材质(暂且不论PS1.4)。我已经可以听到部份读者说:「那关联性材质数据的读入该如何处理呢?」没错,在DX8的像素着色引擎内的确有着关联性材质的处理,但老实说,就这范围内的使用而言的确需付出不小的代价。

要加载1组凹凸平面反射材质,我们必须执行这些指令:

tex t0
texm3x3pad t1, t0
texm3x3pad t2, t0
texm3x3vspec t3, t0

这其中使用了4个材质指令,2种材质及4组材质坐标。材质坐标是用提供一般映对图旋转之后用以产生反射结果的矩阵数据。不幸地是,总共只有4组材质坐标,所以这个凹凸平面反射材质在一次处理的过程之中已经用上所有我们有的材质。任何更进一步的修改,像是加入漫射材质,将需要执行额外的处理程序。

这些限制当设计者仅需执行下面这种材质加载的程序时并不明显:

colour = tex_3d(normal, vec0, vec1, vec2, cubemap);

因此,像素着色引擎设计师总是需要了解他们撰写硬件平台的规格为何,而这样亦让HLSL当初希望与平台独立的目标有所折损。


但HLSL的确能让设计变得更容易
但HLSL的确能让设计变得更容易

我在上篇文章中已对于结合/交换的问题做过深入的介绍顶点着色引擎与像素着色引擎。这些问题对在DX8平台上使用HLSL仍是个大问题。然而,有许多方法让HLSL可以以较简单的方法克服这些问题。

最明显的例子便是当不同的光线必需加在几何对象之上。在过去,人们使用分段式处理器以实时重新编译程序代码。这代表顶点着色引擎是撰写成小小的区块。每个区块必须自特定暂时缓存器中读取数据,并将其输出数据写入输出缓存器或是暂时缓存器之中(视区块式系统的整体而定)。这个系统在使用HLSL

后将更有威力,因为每个区段都可以依区域变量名称运作。当区块被组合时,编译程序应可处理所有的缓存器位置问题,并消去mov指令。这亦是分段式系统最大的两个问题。

与分段式系统不同的是采用一组整体式着色引擎设计的系统,这种系统适用于一般光影环境下的应用。不同的光影环境并不会以不同的着色引擎进行处理,它仅将其一律以现成的整体式着色引擎进行处理。这种系统亦会因使用HLSL而得利,因为撰写每个整体着色引擎的时间将会大幅下降,让设计师能有更多的时间撰写更好的着色引擎以适用于更多不同的光影环境。因为HLSL的研发速度更快,这些不同的变量将可以很快的被撰写在程序之内。

老实说,我并不确定HLSL是否提供像素着色引擎够多的东西。正如我所说的,设计师在处理DX8像素着色引擎时仍需要时时注意。然而,我们仍需提到其程序可读性有所提升。而在未来,像素着色引擎将会更为复杂、功能更为全面,而若我们希望使用硬件上所有的功能,那HLSL将是更为重要。目前而言,使用HLSL作为像素着色引擎并不需要什么代价,但别以为初学者在学会汇编语言之前就能了解其中的运作原理。



相关文档
最新文档