??xml version="1.0" encoding="utf-8" standalone="yes"?>
译:宋晓?br>
介绍Q?/span>
传统计算机图形应?-特别是的应用的需要一个实Ӟ交互的方法来现实--通过处理一个发送到昑֍的数据的最有效的图形数据子集的Ҏ来决定图形数据的昄Q而不是传送全部的数据Q四叉树Q八叉树QBsp树,背面剔出Qpvs集合很多其他Ҏ都是针对q个目的而提出的?br>
行的计机囑Ş卡近些年在处理能力和处理Ҏ上程指数增长Q当前的状态揭C出很多时候应该更好的和快速的扑ֈ一个好的数据集把它们送到昑֍里,而不是把_֊攑֜努力的找C个最好的数据集。这L数据集是一个近似的最好的数据集ƈ且能l常发现它都有十分有效的法Q因此手头上的Q务因此就变成了回ַl存在的技术和法q且试扑ֈ最快的选择Q这对于扑ֈ最好的解决问题的方法ƈ不是什么负担?br>
一、四叉树Q?/span>
四叉树作为数据处理表达技术的一个好Ҏ已经有很多年了,特别是地形渲染引擎都能利用他很有效的作ؓ剔出机制Q剩余的q个章节会l出一个小数量的说明四叉树q且传统的,高层的方法用四叉树Q在描述一个快速的Ҏ之前?br>
四叉树数据结构:
四叉树被描述通过对应每个父节点传递四个子节点Q在一个地形渲染上下文里,根节点将会表达ؓq个围绕地Ş的正方Ş区域集,自节点表CZؓ“左上”Q?#8220;右上”Q?#8220;左下”?#8220;右下”象限Q这些象限由根节点组成ƈ且每一个都是由四个字节炚w归的定义下面的描述会说明q些概念一个四元数的数据结构ƈ不负?下面的伪码演CZq个四叉树的节点Q?
x,y : float;
end;
PQuad = pointer to TQuadNode;
TQuad = record
p1,p2,p3,p4 : TPosition; // corners
c1,c2,c3,c4 : PQuadNode; // children
end;
一个简单的递归初始化一个深度ؓmax_depth的四叉树如下Q?br>
var
tmp : PQuad;
begin
inc(lev);
if lev > max_depth then return(nil);
new(tmp);
...initialize tmp node with corner data
w := w / 2;
tmp^.c1 := InitQuad(x - w, y - w, w, lev);
tmp^.c2 := InitQuad(x - w, y + w, w, lev);
tmp^.c3 := InitQuad(x + w, y + w, w, lev);
tmp^.c4 := InitQuad(x + w, y - w, w, lev);
return(tmp);
end;
Z四叉树的背面剔除
在很多应用里主要的四叉树函数是提供一个有效的的视截体数据剔除,下面的基于视点的高层视截体剔除已l够了:
begin
case Clip_Quad_against_View(q) of
inside : DrawQuad(q);
outside : ; // ignore quad
else begin
...CullQuad children of q
end;
end;
end;
Clip_Quad_against_View 是cullquad的关键函敎ͼq且当然q行的函数和Ҏ来解册个问题是否是一个四元(或者多边ŞQ交叉于q个视见体,通过交叉视见体金字塔作ؓq面集合^面作Z套光U,然后q箋试几何体的光线怎么和可视体q面怺的,光线怺试的方E在很多Qd几何的书上都可以扑ֈQ这里不做重复?br>
二、选择Z四叉树的视点剔除Q?/span>
上面描述的方法一般情况下会得出正的l果Q但是他没有l这个简单的静态的四叉树提供Q何东西,好多的优化可以应用到四叉树的剔除q程Q下面的两个阶段产生了一个很快的和很有效的基于四元数的剔除:
*Z点的剔除Q一个完全的剔除计算Q他通过记录相关的四个角的可视点?br>
很多q样的情况可以被考虑Q例如:如果一个单独的点在视见体内发现Q那么这个块在视见体内Q如果所有的炚w在视见体的一面,那么q个块就不在视见体内Q一个数量的可能性就存在Qƈ且需要通过一个完全的计算Q但是上面给出的两种情况得出了一个充的启发性来接受Q丢弃或者将来重新定义一个潜在的块?br>
*Qomoization:是一U储存计结果的技术ƈ且还需要相同的计算时查扑ւ存的l果?br>
四元CZ个静态结构,q种Ҏ角的点经帔R是相同的Q另外,很多块的角是四个块共享的Qƈ且在循环传递四元数的角一遍又一遍的Q通过军_一个角的相关位|关联这个可视体和方便的储存q个四元数的的计结果?br>
下面的伪代码声明了这个算法,对于一个四元数横跨的区域是(0,0)?256,256);
Memoized : array[0..256,0..256] of byte;
posx,posy : float; // origin of FOW
Lx,Ly,Lz : float; // normal of left FOV plane
Rx,Ry,Rz : float; // normal of right FOV plane
function CheckPos(x, y : int) : int;
// checks position x,y against FOV planes, memoize
// Results: bit 1 (inside), bit 2 (left), bit 3 (right)
var
res : int;
begin
res := 0;
if (x-posx)*Lx + (y-posy)*Ly > 0 then res := 2;
if (x-posx)*Rx + (y-posy)*Ry > 0 then res := res or 4;
if res = 0 then res := 1;
Memoized[x,y] := res;
return res;
end;
function TestQuad(x, y, w : int) : int;
// quad-midpoint: (x,y)
// quad-width: 2w
// test quad against FOV
// Results: 0 (out), 1 (partially in), 2 (completely in), -1 (unknown)
var
m1,m2,m3,m4 : int;
tmp : int;
begin
m1 := Memoized[x - w, y + w];
if m1 = 0 then CheckPos(x - w, y + w);
m2 := Memoized[x + w, y + w];
if m2 = 0 then CheckPos(x + w, y + w);
m3 := Memoized[x + w, y - w];
if m3 = 0 then CheckPos(x + w, y - w);
m4 := Memoized[x - w, y - w];
if m4 = 0 then CheckPos(x - w, y - w);
tmp := m1 and m2 and m3 and m4;
if tmp = 1 then
return 2; // quad completely inside FOV
if m1 or m2 or m3 or m4 = 1 then
return 1; // quad partially inside FOV
if tmp => 0 then
return 0; // quad completely outside FOV
return -1; // else quad state undetermined
end;
上面的函数应该被清除q且及早的需要整数的的四元块E序
begin
Clear_Memoized_Array_to_Zero;
CullQuad(Quadtree_Root);
end;
procedure CullQuad(q : PQuad);
begin
case Test(quadmidpoints and half-width) of
2 : ...handle complete quad
1 : ...handle partial quad
0 : ; // ignore quad
else begin
...CullQuad children of q
end;
end;
end;
三、另外需要考虑的:
很多在代码里和一般的法里都需要被考虑的是Q?br>
*四叉树算法的版本里表现出的只剔除?叻I不是q裁剪面Q不是远裁减面或者上下都没有考虑Q另外只有^面视角。因此这个算法只覆盖了四叉树的的高度剔除和视见面沿着q个四叉树的面?br>
我们扩展q些代码沿着3d的四叉树Q增加如Q应用四叉树的移出算?M可视的位|和方向会正确的进行的东西?br>
*另外很多附加的点Q视见体需要考虑执行的比如:如果两个点在视见体的前面q且对这FOV的一个面Q这个块部分在这个视见体里,对于很多的算法,q样的关卡满个结果?br>
*主要兛_q个法的需要是在记忆需求里Q尽一般的沉浸记忆对于每个可能的块某些炚w要一个附加的字节。因此如果正方Ş的区域有n个间隔,每个面都都需要n个字节来储存Q通过典型的只有一个碎片的内存被一个给定的遍历讉KQ这一部分p讉K了很多次?br>
q篇文章ȝ了算法的表达Q我发现q是一个有用积极的法Q如果你查询了相关的法q且有感触可以写信给我咨询?
]]>
实际游戏制作当中,狭义的灯光只是指E序实时Ҏ灯光影响的顶点色和动态阴?但实际应用当?影响玩家整体感觉的光影效?也就是广义上的光影效果包含的范围要更加广泛?br>E序的即时光?shader的定制固然对光媄效果臛_重要,术制作的lightmap,normalmap,VertexColor乃至Glow都可以构成一般玩家认知上的灯光效果?br>x?在游戏制作应该以游戏玩家的感觉ؓ基准整体的设|光q效果,而ƈ非仅以技术手D作为分?对于游戏制作来说,l合E序术的多U表现手D?最l呈现给玩家的光q感觉,才是游戏制作当中的灯光?br>
二、基本灯光系l介l及使用Ҏ
从画面渲染来?无论一般网游还是次世代游戏,q算的规则构建是大体怼?我们可以通过分析q个构架得出其中制作各种光媄效果的可能?下图是比较常规的各种囄l过如何的运达成了最l画面的pipeline?br>
在这Lpipeline?Ҏl光影有影响的环?br>
Dark Map
Dark Map 也被UCؓ LightmapQ这是我们目前常使用的方法之一?br>q个通道一般放|bakeq的灯光贴图,或者Ambient Occlusion Map, 常用的方式是赋予一个另外的UV ,q一张很的Dark Map可以在没有灯光的情况下获得良好的光影效?很多引擎当中是可以由灯光自动生成lightmap?它的效果一般用在静态物体上?br>
Project lights
Project lights是一般我们所说的g灯光,׃效果是即时运?有一定的g要求,推荐一般不要超q??否则Ҏ率会有一定的影响.除非完全必要Q不使用q种Ҏ?br>Project lights的运用一般是三个q光来构筑一个整体的天光环境,(另外 Ambient light在这个过E中也有重要的意?,׃Project lights是对资源比较耗费的部?应该q序尽量优化?br>
Base map
Base map是diffuse map是我们常说的:贴图?br>在脓图上我们可以在固有色之上q行光和阴媄的绘?从这个角度来说也是媄响光影效果的重要方式,q种做法几乎不会在光pC耗费Mpȝ资源。效果主要依赖美术h员绘制脓囄水^?br>同时Q这也是在美术h员能力许可的条g下,完成大部分光q最x式?br>
Project Shadow
Project Shadow和project light一般有相同的光源点产生,作用是生成阴?׃project Shadow是动态的阴媄,一般用于表现有动画的物?实际q用中Project Shadow和lightmap往往互补使用,分别解决静态和动画物体的阴影。同Pq种Ҏ非常耗费pȝ资源Q除非完全必要,不徏议用这U方法?br>
Normal Map
Normal Map是次世代游戏常用的技?他解决了寚w数限制的情况下如何体现物体细节的问题,通过RGB三色代替XYX三个Normal轴向值的方式获得了更多物体细节?br>Noraml Map机理的主要是在各U灯光位|会对物体的diffuse specular和reflectq行扭曲来达到近似真?但是他不会对base map产生影响,既是说normal map影响的仅仅是材质本n,而非贴图?br>׃Normal Map的这一盲点,prallax map(或displacemap)方式被创造出来I补normal map不能扭曲basemap的缺?实际应用?常常在运用Normal Map之后配合prallax map来达到更好效果?br>
Vertex Color
Material of vertex map是游戏中重要的运?点色可以适用的范围很?透明柔体{都可运?但是最主要的应用在色彩?通过刷取点色可以方便迅速的改变物体的色?对系l资源的要求也较?但是他与灯光色彩有一定冲H?通常情况下灯光色彩与点色不会用于同一物体以避免冲H?点色可以手动绘制或者灯光烘?在面数较低的物体上能发挥更多的媄响?br>点色是Ҏ戏光影最佳的解决Ҏ之一Q也是最常用的解x法?br>
Gloss map
Gloss mapx们所说高光脓?一般灯光对物体高光是基于顶点色q行影响,如角色毛?皮衣{物体胦政很难表现出质感,如果q用Gloss map卛_借助贴图的肌理界定高光的外Ş,也是可以通过较少资源占用辑ֈ影响光媄效果的手D?另外很重要一?Gloss map与我们一般常?D软g中的界定不同,q仅仅影响高光,对反脓图也有媄响?br>
Gloss map其实q不一定需要是灰度?用彩色脓图一样可以?
Environment Map
Environment Map可以理解为我们一般所谓的反射贴图,有球形和盒体{多U方?表现金属物体时常?可以通过 gloss mapq行限制?br>Enivironment Map在max下的贴图槽ؓReflection slot,默认的脓图放|进M用球形坐标方式进行反?如果对脓图有更高要求,也用box贴图,六张贴图l成反射?如果是dds box贴图可以直接调用,但是坐标会有不同?br>
MixGlow Map
Glow Map一般用以体现各U发光物?如灯Q火焰等{,是比较常用的ҎcchannelQ在Glow 贴图栏里面脓上所用glow map Q会和其他脓囄最l效果生相乘效果?br>
Post Effects
除去上述效果Q在Final效果输出之后Q有时我们会通过各种post effect效果对画面加强,如Glow{,对画面光影感觉也会有巨大的媄响?br>
Channel Mix
灯光pȝ分Ҏ及制作要?/strong>
在实际游戏制作当?一般不会将所有的channel都开?一斚w其实要做ZƑև色的游戏画面q取决于用多新技?令一斚w也要考虑E序的负载?br>Ҏ游戏的种cd需求选择正确的方法合理的节省资源能够让游戏拥有更好的效果?br>
技术是手段Q不是诉?/strong>
《Doom III?虽然在当时是技术绝寚w先的游戏,但是术q度的依赖先q的技?D在美术风g的切入不?本应该更优秀的游?未能更上一步?br>
与之相对,几年后推出的《Lost Plant》则成功扑֛了游戏美术最本质的东西,使技术成ZU手Dc?br>
哪些地方Q该使用哪些手段?
我们首先要明,在一个成熟的术团队中。对于一ƾ游戏画面中的各U光影,是如何去表现Q以辑ֈ画面效果和系l负担最佳的l合点的Q?br>我们需要找刎ͼ最优化的手Dc?br>
From http://www.wz-online.net/web2/home/Detail.asp?id=47&FromType=1
]]>
一个模型(有一大堆点跟烦引数据组成)描画的方式,跟材质有很大的关pR?br>Gamebryo提供了一个很强大的材质系l?br>
首先gamebryo使用了一个自定义的PipelineQ这个也是在之前的文章中介绍q的。其实这个Pipeline是大家最常用的一些Shader。GB帮我们ȝ出来了,q做成了一个标准的材质。这在GB里叫StandardMaterial?br>
标准材质跟Pipeline是相对应的。但是标准材质的实现是非常困隄Q可以查阅NiStandartMaterialQ大U有5000多行代码。GB会首先查找一下Shader文g夹下的那些Shader?br>q些Shader的文件名是由一Ҏ?字母l成的。这些文仉是不重复的,因ؓGB内部通过Hash码得些倹{如果在Shader文g夹下没有Q那么GB会把当前的渲染方式记录到q个Shader中去Q作为缓存?br>
当然你也可以构徏自己的MaterialpȝQ比如GB的NiCommonMaterial里也l出了一些构己材质系l的例子Q不q这是非常复杂,基本思想都是需要维护一个Shader树?br>
不过自己写Shader是非常方便的Q你可以用RenderMonkey或者ShaderFXQ把做好?.fx文g攑ֈShader文g夹中QMAX再次打开的时候就会找到这些Shader。让工使用h非常的方ѝ?br>
GameBryo提供了一太基本的模板容器c,q些容器在整个库内用?/p>
Lists
NiTPointerList对象可以包含和管理指针,指针Q以及其他Q何大小于等于指针的元素Q该链表可以有效的插入和删除所有元素,以及正向遍历和反向遍历所有元素,同样可以通过l定值查扑օ素的实体和所在位|,NiTPointerList的元素的内存是从一个共享内存中分配的,从而提高类的执行速度和内存效率,如果链表元素大于指针Q程序可以用NiTObjectList.
Array
NiTArray对象实现勒几乎可以包含所有对象的动态数l,该数l可以羃放,q且可以压羃(通过转移元素来移除空I间)。内|类?char*, float, int{?使用NiTPrimitiveArray。NiMemObjectz出的cd使用NiTobjectArray。注意NiTArray的元素上限ؓ65535Q如果大于该限制Q用NiTLargeArrayz出的c,比如NiTLargePrimitiveArray或NiTLargeObjectArray?/font>
Map
NiTPointMap对象实现勒哈希表的功能,允许Mcd的键值来影射到指针,指针Q以及其他Q何大小于等于指针的元素Qƈ能快速的储存和查N值对Q不q不能用字W串键哈希表Q而NiTStringPointerMap对象是专为此设计的,NiTPointMap和NiTStringPointerMap的元素内存也是从一个共享内存中分配的,从而提高执行速度和内存效率,如果map元素大于指针Q程序可以用NiTMap和NiTStringMap
StringMap
NiTStringMap和NiTStringPointerMap对象的函数和NiTMap和NiTPointerMap风格cMQ但是允许字W串作ؓ键,q且通过字符串比较来q行键散?/font>
FixedStringMap
NiTFixedStringMap对象函数和NiTMap对象风格怼Q但是允许NiFixedString对象作ؓ?/font>
Queue
NiTQueue实现勒基本所有类型对象的先进先出队列Q但不提供智能指针,需要注意链表可以当做队列来使用
Set
NiTSet实现了基本所有类型的无序集合Q也没提供智能指针,内置cd(char*, float, int{?使用NiTPrimitiveSet。NiMemObjectz出的cd使用NiTobjectSetQ智能指针则使用NiTObject或者NiTPrimitivePtrSetQ这正的处理引用计数?/font>
Pool
NiTPool实现了小型对象的池,使得E序能通过一个池来分配小型对象,q能重复使用Q而不是单独的d配和释放一个小型对?/font>
一个程序会在需要的时候改变一个节点的转换Q计该节点的时间{换,以及该节点子节点的其他{换将被gq,直到应用E序调用例行的update?/p>
update是高效用深度优先来遍历子图计算世界转换和世界包围球Q从而最大限度的减少节点的访问,当向下递归Ӟ转换被更斎ͼ包括所有的自节点,当矩阉|新后Q世界包围球通过递归调用q回
MQ{换在递归中被更新Q而包围球在递归q回时得?/p>
通常大多数的对象都不会移动的Q所有只更新只限于小部分可以Ud的对象。在场景囄数据处理初始化中Q当应用E序使用到场景图前,臛_要对场景囄根节点进行一ơ例行updateQ这样保证所有节点的世界信息及本C息都是最新的?/p>
在应用程序到的运行中Q当W合下面M一条时Q应用程序必调用对?#8220;O”的update?/p>
·O被绑定到父亲节点或者从父亲节点解除l定
·Ol定了一个新子节Ҏ者解除了一个子节点的绑?/p>
·OM一个{换被改变
注意一下,调用当前发父亲节Ҏ者Q何祖先节点的update可以代替当前节点update的调用。例如,如果对象Al定了子节点B和CQ只需要调用A的update够了。没有必要调用三个对象的updateQ应用程序会以批处理的方式更新?/p>
例如Q当应承需要改变了一个活动角色的所有的兌矩阵Q他应该推迟update直到所有的改变都完成,q且只需要调用一ơ角色根节点的update?/p>
但是Q注意update量在场景图的更下层调用Q如果一个场景图每只有一个叶子被改变Q那么调用根节点的update太q分了,q将降低性能?/p>
GameBryo ---- |格数据׃n
Z世界中对象只需要少量的点Q一|可以用来代表场景图,每个对象被单独表Cؓ树中的节炏V但是,在绝大多数场景图中,会多ơ出C个需要大量内存来存储的复杂对象,l大多数的内存是消耗在U理和顶Ҏ据上Q比如纹理坐标和法线?/font>
如果一个应用程序需要多份这样一个对象,是有可能通过׃n的NiDataStream来分享模型空间的几何信息Q颜ԌU理和其他颜艌Ӏ换句话_若干|格对象可能分nNiDataStream对象Q在q种情况下,场景图是有向非@环图Q而不是一|?/font>
在几何数据共享的情况下,叶网格对象共享NiDataStream对象的模型空间网根{但?两个模型数据的实例是在世界的不同位置,因ؓ他们代表的多个网格对?q且每个副本自己单独的{换?/font>
q些理是在应用E序的内部透明处理的——应用程序只需要徏立两个网格对象用同一个NiDataStream对象。网格对象甚臌比一套最的|格数据的多,因ؓ|格对象不像数据,不存在每个顶点的数据
下面的图象是一个典型的情况Q?/font>
(两个|格对象使用同一个NiDataStream)
两个叶网格对象分享一个轮胎NiDataStream对象的顶点位|和法线。一个网格对象对应到自行车的前轮Q另一个对应到自行车的后轮。NiDataStream对象自n存储的网格顶点的位置和法U被׃nQ两个网格对象都保存表现自己|格的{换?/font>
注意以下QGamebryo不ؓMcd的NiAVObject提供多父亲的功能。绑定一个已l拥有父节点的对象CQ会DC自动q原来的父节点
Gamebryo通过一套相互独立的渲染性质为每个能渲染的叶子对象定义了渲染属性,每一个渲染属性都渲染的对象定义了某一斚w的渲染状态,q且都是NiProperty的子cR对个可渲染的对象可以共享渲染属性。针对一个对象的完整渲染状态是所有属性的完整l合。当属性状态对象存在与NiRenderObject叶子节点Ӟq个个体渲染属性就被绑定在场景囄MNiAVObject上。这正式用于每个可渲染叶子对象生属性状态的每个NiAVObject的属性。一个属性被l定C个NiAVObject媄响所有子C的子对象(包括它自?Q除非在子树中同L属性类型被其他属性所替代。如果没有Q何属性被讄到场景图的对象上Q该对象会通过合适的默认属性来l制。每个属性类型的默认相当于ؓ该类型设|一个默认构造,每个NiAVObject都包含了一个绑定与它的所有属性的链表Q一个NiAVObject可能没有M属性绑定,也有可能l定一个或多个属性。所有的方式辑ֈ一个NiAVObject能绑定的每样属性的最大倹{注意确保应用程序Q何时候都不能l单一的NiAVObjectl定一个以上已l类型的属性。一个单一的NiAVObjectl定已经cd的一个以上的属性会D奇怪的视觉效果和未知的问题?/font>
l制属性类?/font>
NiProperty对象在Gamebryo中的数据层次如下Q?/font>
NiObject
NiProperty
NiAlphaProperty
NiDitherProperty
NiFogProperty
NiMaterialProperty
NiShadeProperty
NiSpecularProperty
NiStencilProperty
NiTexturingProperty
NiVertwxColorProperty如上面讨论的,属性设|从根到叶层ơ。一个被l定到NiAVObject对象的属性会影响该对象及它的子对象。除非在更低的子树中l定该类型的其他属性。因此,一个可渲染叶节点的当前状态是由场景图中它先的链所军_?/p>
NiWireFramProperty
NiZBufferProperty
更新属性到集合物体?/font>
Gamebryo会让整个场景图保持绑定属性,l定在每个可渲染叶对象的属性状态是包含所有提供的cd的属性的数组Q这P每个可渲染的对象包含一个直接指向用来绘制的渲染属性的指针。这很重要,因ؓ渲染只涉及可渲染的叶对象Q而不是整个场景图。于是每个可渲染对象的属性状态都是承其他可渲染对象的所有属性的副本?/p>
Gamebryo使用一个系l类g用NiAVObject::Update函数来更新这些属性状态对象。这个类似的渲染属性函数是NiAVObject::UpdateProperties。当出现下面的情况UpdateProperties必须在obejct"O"或Q何一个他的祖先调用下一ơ渲?/p>
·一个以O个根节点的树刚被创徏
·一个属性被l定到O或从OU除
·O被绑定到节点P或从P上被U除
注意Q当只改变了一个已有属性,应用E序不需要调用UpdateProperties?/p>
要实现最x能Q这些UpdateProperties的调用可以以相同的方式进行批处理来执行批处理更新,如果应用E序在子树上绑定或解除l定许多属性,它必调用所有的l定或解除绑定函敎ͼ然后在子树的栚w调用一ơUpdatePropertiesQ通常的,因ؓ属性和子节点的l定和解l没有每一帧这样频J,所以UpdateProperties要比每U篏属性快的多Q但是对于程序员。将多出一个额外的负担?/p>
GameBryo ---- SoftParticles
软粒子主要是Z解决_子q告牌和场景几何怺Ӟ产生的生边~,如下囄雾与地面怺时的?img class=blogimg border=0 src="http://hiphotos.baidu.com/%BA%DA%B5%C4%B7%A2%D7%CF/pic/item/b117d366e9752f0faa184c1c.jpg" small="0">
Z解决上面的情况,我们需要用到场景的深度信息Q如下图Q?/p>
在一般的渲染线中,点P3是产生生硬边的点,Z改善q种情况QSoftParticles通过改变_子的alpha值来处理_子后面的场景,q里使用了自定义的shader帔R来决定距d以便我们调整alpha|d为world place中)QQ何距d场景深度大于d的粒子相素我们将不处理他的alpha?对应上图P1的公?Q?/p>
上图中点P1正好辑ֈ该距,点P1到P3的alpha混合E度会递增Q距d讄的越,那效果就接q于粒子的效果Q因为P1的条件很Ҏ满Q对alpha值的修改会减,但是如果距离讄的过大,那P2很Ҏ满Q这样导致Len/d产生的值很,让粒子变的很透明Q造成的粒子很E疏,具体的效果要自己手动调节?/p>
q种边缘软化的方式只是近似的Q当场景的法U于摄像机方向锤子时会失效,当出现这U情冉|Q随便粒子与怺面很接近了,但因为摄像机与相交面q乎垂直Q而粒子相素的深度是沿与摄像机方向的Q从而生一个很大len|D了本来应该成为P3效果的点Q成为P1?/p>
DEMO5个类Q?span class=NDLSystemFont>SoftParticlesQ?span class=NDLSystemFont>MRT_ColorDepthMaterialQ?span class=NDLSystemFOnt>SoftParticlesMaterialQ?span class=NDLSystemFont>SoftParticlesManagerQMRT_ColorBlackMaterial
SoftParticles::CreateScene()
负责创徏场景Q从Nif文g中获取场景,摄像机及_子pȝQ设|alpha排序Q剔除,及默认材质MRT_ColorDepthMaterial, MRT_ColorDepthMateriall承于NiStandardMaterialQ重载了函数bool HandleFinalVertexOutputs()和函数bool HandleFinalPixelOutputs()Q这两个函数分别在vertex shader和pixel shader的最后执行,通过HandleFinalVertexOutputs函数Qؓvertex shader的outputl构增加成员NiMaterialResource* pkVertOutViewTexCoord = Context.m_spOutputs->AddInputResource("float4", "TexCoord", "World",
"PosViewPassThrough");作ؓU理坐标的格式输?/span>
NiMaterialNode* pkSplitterNode = GetAttachableNodeFromLibrary(
"PositionToDepthNormal");
kContext.m_spConfigurator->AddNode(pkSplitterNode);
kContext.m_spConfigurator->AddBinding(pkViewPos,
pkSplitterNode->GetInputResourceByVariableName("Input"));
kContext.m_spConfigurator->AddBinding(
pkSplitterNode->GetOutputResourceByVariableName("Output"),
pkVertOutViewTexCoord);
获得自定义函数PositionToDepthNormalQ将pkViewPos作ؓ传入参数Q把新增的pkVertOutViewTexCoord作ؓ输出参数
而HandleFinalPixelOutputs则在输入l构中增加float4 WorldPosProjected : TEXCOORD6;代码如下
NiMaterialNode * pkInputResource;
pkInputResource = kContext.m_spConfigurator->GetNodeByName("PixelIn");
NiMaterialResource* pkDepthFromVP = pkInputResource->AddOutputResource(
"float4", "TexCoord", "World", "WorldPosProjected");
然后在输出结构中增加深度颜色Q通过WorldPosProjected来赋|CreateScene()q将场景中所有的_子pȝdq?span class=NDLSystemFont>SoftParticlesManagerQ添加完所有的ParticlesSystem后,SoftParticlesManager通过Initialize()函数Q创建默认的_子材质Q以?span class=NDLSystemFont>SoftParticlesManager自己的RnderView及click,然后通过InitializeScene()为每个粒子系l设|材质,q添加进RnderView,q里要注意执行顺?/span>
SoftParticles::CreateFrame()该函CQ通过获取后备~冲的属性,来创建相同一张textureQ用来渲染深度,不过格式要设|ؓNiTexture::FormatPrefs::SINGLE_COLOR_32Q表C?2位的单通道颜色Q用R通道表示Q然后新Z个RenderGroupQ要包含原来的后备缓冲以及先建的位图~冲Q让场景同时渲染到这两个~冲上,q将U理~冲作ؓSoftParticlesManager中创建的click的一个输?/span>
MSAA(MultiSampling Anti-Aliasing)
GB中MSAA实现的关键代码基本是直接用的D3DQ除了自q渲染ҎpȝQDX9不能直接Ҏ染到U理启用MSAAQ但是提供了渲染表面surface,可以对surface启用MSAA?/p>
实现的关键步骤大致如?/p>
1。NiRenderedTexture::Create创徏一个和背景~冲一样大的RenderTextureQNiRenderTargetGroup::Create利用RenderTexture创徏RenderTargetQ屏q最l渲染的目标是该RenderTargetQ我们需要的也是MSAA后的数据lRenderTexture
2。通过D3D的CreateRenderTarget按照自定的MSAAU别来创建surfaceQ利用surface的buffer创徏另外一个MSAARenderTarget
3。新Z个RenderViewQGB中用的NiScreenFillingRenderViewQ就是D3D中四个顶点组成的矩ŞQ该RenderViewl定一个baseTexture为步?所创徏的RenderTexture的NiTexturingPropertyQ创建RenderClick挂接该RenderViewQƈ讄一个CallBackFuncQ然后将该click插到mainClick后面Q这L两pass来完成MSAA
4。主click画面渲染到开启MSAA的MSAARenderTargetQ然后进入新click的CallBackFuncQ获取MSAARenderTarget的bufferQ用D3D的StretchRect复制数据到RenderTextureQ这hclick׃渲染行MSAA后的texture
GameByro作ؓ一ƾ次世代引擎Q用了复杂的材质系l,用来满各种各样的需求。材质代表了物体受到光照后所呈现出的质感Q而这U质感在计算机图形学中需要着色代码来完成Q所以当前流行的囑Ş引擎设计是用被渲染对象的材质与shader相关联,GameByro也不例外。GameByro的材质系l可以通过shade tree生成shaderE序Q增Z应用E序层对可编E渲染管U的控制能力?/p>
在GameByro中,对象表面的色彩、纹理、光滑度、透明度、反率、折率、发光度{可视属性与传统的材质系l分,独立的成Z对象的渲染属性(NiPropertyQ,而材质(NiMaterialQ仅用来对着色程序的装Q这样就实现了渲染数据和渲染Ҏ的分,降低了耦合性。如上所说的q些可视属性在Gamebyro中会装成一个属性对象,在应用程序中如果对对象挂载这个属性对象,在GPUE序中就可以讉Kq个属性对象的倹{渲染属性对象可以在创徏时指定其cdQ如U理、Q炏V矩c向量或数组Q此外一些全局性的对象也可以通过在Shader中用语意声明为全局object对象Q如灯光和摄影机{,q样可以以同样的方式来讉Kq些对象上的属性?/p>
GameByro每一帧的渲染QNiRenderFrameQ划分ؓ多个步骤QNiRenderStepQ,每个步骤又包含很多个批(NiRenderClickQ,NiRenderFrame装了上层对渲染pȝ调用的接口,而NiRenderClick则代表了囑Şg的一ơ绘制操作(Ҏ染队列中所有的对象的顶点缓存调用DrawPrimitiveQ,当应用程序调用NiRenderFrame的Display接口? NiRenderFrame会依ơ调用每一个NiRenderStep的Render()接口QNiRenderStep׃执行所有的NiRenderClick操作?/p>
对于每个NiRenderClick来说Q首先要讄视口和渲染目标,也就是渲染数据流的入口和出口。视口徏立以后就可以通过兌的摄影机对场景图中的对象q行裁剪Q默认的有视口裁剪和遮挡裁剪Q此外还可以通过回调函数加入自己的裁剪方式)Q将未被裁剪的对象放入渲染队列。然后Gambyro会根据材质来Ҏ染队列中的对象进行排序,让材质相同的对象处于盔R位置Q这样可以减切换shader的开销?/p>
如图所CZؓ帧渲染系l的l构图(化版Q?/p>
GameByro中的材质代表渲染对象所采用的方法。前面说q。纹理属性包含了着色所需的原料,那么材质指定了对这些原料的加工Ҏ。基于当前可~程渲染线设计Q材质就成ؓq接对象与GPUE序的中间层Q应用程序可以通过材质shader应用于几何体?/p>
NiMaterialcL所有材质的基类Q这个类通过一个Map来保存当前应用程序中所有NiMaterial的指针,当然q个Map是静态也是说相当于全局变量Q通过static NiMaterial* NiMaterial:: GetMaterial(const NiFixedString& kName)接口对这个全局的Mapq行讉K。也是_当前环境中所有的NiMaterial对象是通过NiMaterialcL理的。此外NiMaterialc还通过静态成员变量保存了一个工作\径(即shader文g路径Q,通过q个路径加蝲shader文g。NiMaterial像是一个中介,全权代理对对象的渲染工作。用户可以通过重蝲NiMaterial来实现自q渲染机制。每个NiMaterial都是全局性的Q可以作用于多个甚至是所有的渲染对象Q但一个渲染对象也可以拥有多个NiMaterialQ但只能有一个处于激zȝ态的NiMaterial?/p>
NiMaterial的派生类NiFragmentMaterial提供了对可编E渲染管U完整的控制机制Q内部保存了NiShader的哈希表、一个NiGPUProgramCache数组。ƈ且NiFragmentMaterial会生成一个用来编译GPUE序的shade treeQ后面会有解释)。这L话,每个NiFragmentMaterial可以对应多个shaderE序Q这样就提供了一U机Ӟ在运行时Ҏ不同的运行环境和渲染对象不同的状态,来选择合适的shaderE序。在NiRenderClick依次渲染可见集中的每个对象时Q首先会判断其是否需要被渲染的标讎ͼflagQ,如果需要被渲染Q则使用NiMaterial::IsShaderCurrent接口判断当前shaderQ上一ơ渲染所使用的shaderQ是否有效,所谓有效就是仍然存在ƈ且可以应用于本次的渲染对象,如果无效Q则会调用NiMaterial::GetCurrentShader获得shaderQ用于本ơ渲染。NiMaterial::GetCurrentShader会根据渲染对象的属性和当前环境g条g来选择合适的shaderE序。当Ӟ可以通过重蝲IsShaderCurrent和GetCurrentShader接口来指定自q有效性判断规则和如何选择shaderE序的方案?NiFragmentMaterial提供了一套搭建shade tree的框Ӟ用户可以通过重蝲来搭qshade treeQ当Ӟ如果不想通过shade tree的Ş式生成shaderE序也可以,使用NiSingleShaderMaterial可以从文件生成shaderE序?/p>
Shade TreeQ?/strong>
什么是shade tree呢?我们通常~写的shader代码是线性执行的Q即每个pass程执行的是文本上定义好的shader程Q每一Dshader功能模块是按一定顺序依ơ执行的。如果需要修ҎE中的某一部分需要更改相关的shader代码q新编译。而shade treeshader代码以树形结构组lv来,每一个shader代码块(一般是一个函敎ͼ都会被编译成一个节点,通过定义输入变量和输出变量来提供数据的入口和出口,q些节点的插入和删除可以通过应用E序来控Ӟ从而灵zȝ控制整个渲染q程。这样shaderE序中的一些核心模块可以由术通过工具生成Q然后插入到shade tree中,只要输入和输出的接口不变Q就无须修改其他代码Q从而降低了术开发shader的门槛?/p>
GameByro通过以下几个cL建shade tree:
l NiMaterialConfiguratorQshade tree被封装在q个对象中,Uniform constants被封装在NiMaterialResource中,而NiMaterialNode装了相关的shader代码Q所有的资源和节炚w过NiMaterialResourceBindingq接h。当所有的q接都确立以后,NiMaterialConfigurator会调用Evaluate接口生成GPUE序和一个输入Uniform资源的集合?/p>
l NiMaterialFragmentNodesQ这个类包含了一个shader代码片段的集合,q些代码片段Z同的q_和编E语a所~写。这׃ؓshaderE序员提供了更大的灵zL,用以控制他们的代码在不同q_和图形硬件上的表现。例如:在高端^台可以采用高U的shader model提供更好的效果,而在低端q_上可以关闭一些特效来加快速度?/p>
l NiMaterialNodeLibrariesQ这个类是一个NiMaterialNode的集合,其实也就是一个shader库。它允许shade tree节点完全Z数据驱动。shader库的生成可以通过两种方式Q解析XML文g或用XML文g生成C++代码。GameByro提供了相关的解析器和代码生成器?/p>
l NiMaterialResourcesQshade tree中的Uniform constantsQ支持多U数据类型,包括Constant、Predefined、Attribute、Global、Object?/p>
固定线的渲染:
GameByro支持固定线的渲染,其纹理合过E如下?/p>
固定线的着色处理流E?/p>
上图很清楚的昄Z每个stage的操作,q的表CZ张纹理的采样是同时进行的Q特定情况下双的纹理可能被忽略?/p>
大部分情况下Q应用程序不会用上面所有的stageQ开启或者关闭那个stage可以由应用程序来指定?/p>
以下为多重采L原理?
固定线的纹理多重采?/p>
GameByro提供了一个默认的着色处理流E,装在NiMaterial的派生类NiStandardMaterial中,q个cL行类g固定线的流E,在不同阶D将U理采样、ƈ采样到的数据合到最l的l果中去?/p>
GameByro默认的材质系l的Ҏ如下:
下图昄Z同的U理、灯光、材质属性的l合q程Q不q需要注意的是,视差贴图和凹凸脓囑ֱ于特D的情况Q它们仅仅媄响到U理采样的UV坐标Q而ƈ非直接对最后的颜色g生A献。视差脓图会改变所有脓NLUV坐标Q而凹凸脓图仅对环境脓囄UV产生影响?/p>
以上程完全由shade tree构徏QNiStandardMaterial提供了大量的函数接口用于Ҏ个流E的控制Q用户可以通过重蝲相关的接口,插入自己的shade tree节点Q修Ҏ一步的操作或处理过E。例如:
virtual bool HandleBaseMap(Context& kContext, NiMaterialResource* pkUVSet,
NiMaterialResource*& pkDiffuseColorAccum,
NiMaterialResource*& pkOpacity, bool bOpacityOnly);
当然Q整个流E的序和结构修改v来比较困难,如果有需要可以定制自q材质pȝQ搭qshade tree?/p>
NiStandardMaterial提供了若q回调函敎ͼq些函数可以动态的修改程Q分割PASSQ对shaderq行p|q行定w?/p>
l SplitPerPixelLights/SplitPerVertexLights:q两个函数分别作用于逐顶点光照和逐像素光 照,当物体所受的光源数量太多Q超q了点或像素着色器的能力时Q通过q些函数可以失败的pass分割成两个,如果分割出的pass仍然不能执行Q那么函C被递归调用Q直到每个pass只有一个光源ؓ止?/p>
l SplitTextureMapsQ这个函C把对U理采样的passq行分割Q当U理查询q多Ӟ点或像素着色器׃q于复杂Q这时就可能Dshaderq行p|。此函数只能q代一ơ,生成一个额外的pass?/p>
l DropParallaxMapQ这个函数用来从几何体上U除视差贴图Q且不生额外的pass?/p>
l DropParallaxMapThenSplitLightsQ这个函数首先调用DropParallaxMapU除视差贴图Q然后一直调用SplitPerPixelLights直到p|为止?/p>
NiMaterialq不是直接与NiRenderObject相关联,而是l过了NiMaterialIstanceq个中间层,由它来代理将NiMaterial兌到几何体Q它负责调用NiMaterial为NiRenderObject生成NiShaderQ通过改变NiMaterialIstance上的接口SetMaterialNeedsUpdateQ可以决定每一帧NiRenderObject所使用的材质是否需要被更换Q通过接口SetDefaultMaterialNeedsUpdateFlagQ可以决定当前材质所需的数据(卛_前渲染流E所需的数据)是否要被更新。这h个NiMaterialIstance只能被一个NiRenderObject所拥有Q而多个NiMaterialIstance可以׃n一个NiMaterialQ这样就减少了重复创建NiMaterial的时间和I间上的开销Q同旉低了渲染对象个材质之间的耦合度?/p>
如下为材质系l的cȝ构简化图Q?/p>
前面提到q,GameByro渲染所需要加工的数据全部装在了NiProperty中,只要在shader中用指定的语法进行声明,可以访问这些属性的倹{?/p>
目前引擎中已l定义了12U属性,均派生自NiPropertyQ分别代表渲染数据的12U不同类型:
用户也可以自定义属性类型,但所对应的数据类型要被shader语言所支持?/p>
光照与阴影密不可分,因ؓ阴媄是由光照生的Q前面在材质pȝ中已l提到过光照对着色的影响Q这里重炚wqͼGameByro是怎样Ҏ光源产生阴媄的。由GameByro提供的阴影均ZShadowMap技术,
但也提供了ShaowVolume的示例代码?/p>
Shadowing System是完全徏立在帧渲染系l上的, 通过一个RenderClick生成ShadowMapQ然后在正常渲染程开始之前将ShadowMap更新到可见集内每一个渲染对象上。这样当渲染对象使用NiStandardMaterial时就会根据光源的阴媄技术来对ShadowMapq行采样Qƈ结果与最l的输出颜色按一定比例合?/p>
阴媄pȝ׃下几个类构成Q?/p>
GameByro提供了三U类型的Shadow Write MaterialsQ分别ؓNiPointShadowWriteMaterial?/p>
NiDirectionalShadowWriteMaterial、NiSpotShadowWriteMaterialQ适用于三U不同的光源cd?/p>
阴媄pȝ静态结构如下:
1. 在应用程序初始化阶段Q通过调用NiShadowManager的Initialize()接口实现Ҏ个阴ql的初始化,此时应用E序会注册所有的ShadowTechniqueQƈ初始化NiShadowClickGenerator?/p>
2. 当我们创Z个NiLight以后Q我们可以通过NiShadowManager个NiLight新徏一个NiShadowGeneratorQNiShadowGenerator会通过NiLight的类型来选择合适的NiShadowTechniqueQ此时NiShadowManager会ؓ新的NiShadowGenerator创徏一个NiShadowRenderClick?/p>
3. 当渲染pȝ启动后,NiShadowRenderClick的PerformRendering()接口会被调用Q此时NiShadowRenderClick会通过引用的NiGenerator获得阴媄生成的着色程序和所需的数据(例如深度偏移Q,同时通过NiGenerator引用的Camera获得场景图中的可见集。下一步就是对可见集中的渲染对象添加ShadowWriteMaterialq设为激zȝ态,而ShadowWriteMaterial的类型是ҎNiDyamicEffect的类型指定的。最后NiRenderClick׃启动渲染水U,可见集中的对象的深度全部渲染到ShadowMap中?/p>
4. 在第一个RenderClick中生成了ShadowMapQ下面就要用这些ShadowMap投射阴媄。在每一帧开始之前,用户q可以自己指定不接受阴媄的节点,手动其插入NiShadowGeneratorQ:m_kUnaffectedReceiverList中。在渲染BackBuffer的RenderClick中,首先会对场景图中的节点进行一ơ遍历,不受阴q节点攑օNiShadowGeneratorQ: m_kUnaffectedCasterList的列表。在Ҏ个节点进行渲染时Q会遍历NiShadowManager中所有的NiShadowGeneratorQ判断这些NiShadowGenerator是否对这个节Ҏ影响Q判断的规则是此节点是否存在于UnaffectedReceiverList和UnaffectedCasterListq两个链表中Q如果存在于M一个链表,则节点不受此NiShadowGenerator影响Q如果受此NiShadowGenerator影响Q那么就该NiShadowGenerator上的ShadowMap和数据更新到节点上的渲染属性中QNiStandardMaterial会根据这些数据选择对ShadowMap采样的方式,q将l果混合到最l的输出颜色中?/p>
值得注意的是Q点光源的shadowMap默认的是采用CubeMap实现Q用户可以通过接口选择不用CubeMap实现Q当采用CubeMap实现Ӟ光源无法产生软阴影?/p>
Z验证GameByro渲染pȝ的扩展性,W者尝试着加入了一个后期处理特效Screen Space Ambient OcclusionQSSAOQ?卛_q空间的遮蔽Q由于仅仅ؓ了熟悉GameByro的渲染流E,所以笔者ƈ未对SSAO法做深IӞ仅仅用了自己化的法?/p>
在渲染过E中先单独用一个RanderClick场景中的深度渲染到一张纹理上Q然后在渲染到后台缓冲区的RenderClick中对深度U理q行采样Q执行SSAO法Q将l果混合到最l的l果中。采L的偏Ud标是通过对一l随机向量进行归一化再乘以0~1之间的随机数而生成的Q即长度?~1之间的随机向量?/p>
PS代码如下Q?/p>
float VerticalRange:GLOBAL; //控制XY方向采样范围变量Q可以在应用E序层对其进行调?/p>
float HorizontalRange:GLOBAL; //控制在Z方向采样范围的变?/p>
float calAO(float2 texCoord,float dw, float dh ) //通过当前像素所标和偏移量计AO
{
float2 coord = float2(texCoord.x + dw, texCoord.y + dh);
float4 CenterPos = tex2D(DepthSampler,texCoord);
float4 CurPos = tex2D(DepthSampler,coord);
float depthDiff = clamp(CenterPos.z - CurPos.z,0,VerticalRange);
float ao = depthDiff/length(CurPos.xyz - CenterPos.xyz);
return ao;
}
// Pixel shader
float4 PS_SSAO(VS_OUTPUT In) : COLOR
{
float2 texCoord = In.BaseTex;
float depth = tex2D(DepthSampler,texCoord).z;
float ao = 0.0;
float scale = HorizontalRange/depth; //因ؓ采样范围会受深度影响Q故除以此系数?/p>
for(int i=0; i<32; ++i)
{
float2 offset = arrRandomPt[i].xy* scale;
ao += calAO(texCoord, offset.x, offset.y);
}
ao/=32;
float4 color = tex2D(BaseSampler,texCoord);
color.xyz *= (1.0 - ao);
return color;
}
最l实现效果如下:
SSAO生成的明暗图 无SSAO材质
以下两图上图为无SSAO效果Q下图ؓ开启SSAO后的效果?/p>
GameByro的渲染pȝ是比较灵z,惛_入自q渲染程是比较容易的Q此外由于RenderTarget和RenderView都可以由用户指定Q所以想实现自己的shader效果不是很难。然而,NiStanderMaterial的shade tree比较复杂Qd高达6000行代码以上,q程非常复杂Q这是_如果惛_现自q材质处理程也要付出相当大的工作量,阴媄pȝ虽然实现了较低的耦合度,但是实现q于复杂Q不够简z高效?/p>
作者:叶v涟漪
GameByro是一ƾ成熟的商业引擎Q已l被许多成功的商业项目所使用Q不仅包括文?、上古卷?Q辐?{跨q_知名单机游戏Q还有EA的大?DMMORPG战锤online。GameByro完全Z面对对象设计Q结构清晎ͼ便于使用和扩展。灵zȝ可编E渲染架构它比较容易支持最新的囑Ş技术。另一斚wQ由于强调通用性,GameByroq没有对上层应用做太多的支持Q其工具和插件大多仅h一些通用功能Q所以它更像是一个游戏开发套件而不是一个完整的FrameWork?
引擎Ҏ概览:
· 跨^収ͼPC,XBOX360,PS3Q?
· 场景图表Cؓ层次化的l构
· 同目前主要的建模工具集成Q插件支持)
· 高效的可视性裁?
· 在所有的q_上支持高U?Dg加?
· 高U理和着色效?
· 动态碰撞检?
· l节分表现QLODQ?
· 灉|的渲染,排序和裁剪算?
对象pȝQ?
GameByro的对象系l的使用遍及整个引擎Q其主要实现如对象管理,引用计数Q对象生存期{功能。可以通过对几个基cȝl承来用对象系?
其主要特性主要有Q?
1. 指针Q引用计敎ͼ
2. q行时类识别QRTTIQ?
3. 场景对象复制
4. 序列化(对象文档化)
5. 对象命名
6. 对象扩展数据的管?
7. 定时器(主要用于动画Q?
8. 多线E操?
驱动?/strong>Q?
GameByro对图形接口进行了装Q将囑ŞAPI从引擎的渲染pȝ中剥d来,在PCpȝ上,GameByro同时支持DX9和DX10两种接口?
DX9渲染器特?/strong>Q?
l GameByro不允许多个DX9Renderer对象同时存在Q且不能与DX10Renderer共存。但在同一帧内QGameByro的DX9Renderer可以同时渲染多个H口?
l GameByro的DX9渲染器支持对资源的多U程讉KQ同时也支持对资源的预蝲Q但是多U程的操作仍然受C格的限制?
l DX9Renderer 的固定管U蒙皮操作最支?块骨|矩阵Q,同时Zshader的蒙皮操作支?0块骨骹{?
l DX9Renderer通过对后台缓冲的多重采样支持全屏抗锯齿,范围?倍到4倍之间?
l 大多数硬件在DX9下对几何体的一个单一的piece所能接受的光照数量都有一定的限制Q指渲染的一个批Q,如果过q个限制QGameByro会根据距L者是影响因子攑ּ一些光照。正因如此,在应用程序中Q当8个以上的逐顶点光照作用于同一对象Ӟ׃有一些被抛弃?
l DX9Renderer提供了视口的左右反向功能Q用于实现后视镜、立方体贴图表面l制{功能?
l DX9Renderer支持点雾,可以通过相关接口讄雄最大倹{?
l DX9Renderer完全支持DX的纹理格式。应用程序可以将自己的Direct3D textures提供lGameByroQ引擎会自动其装到自q材质对象中ƈ允许d属性和Ҏ。通过提供自己的DX的纹理格式,GameByro能够正确的用自q天然支持的纹理格式?
l U理相关Ҏ和限制QDX9Renderer需要显卡支持在同一pass中至用两个纹理,否则渲染器则不会创徏成功。此外受限于g的功能还有:
1. Cube MappingQ立方体U理Q?
2. Specular Bump MappingQ凹凔R面脓图)
3. Diffuse Bump MappingQ凹凸反脓图)
4. Non-Power-of-Two Texture SizeQ编镉K二整数幂的纹理)Q用这U纹理有如下限制Q纹理寻址模式必须讄为clampQMipmapping不能使用?
5. Texture DownsizingQ纹理精度羃减)
6. Texture CompressionQ压~纹理)QDX9Renderer支持载入l过压羃的纹理格式,q提供了U理x压羃的功能。如果硬件支持压~纹理,DX9Renderer可以经q压~的U理数据直接交给g处理Q否则DX9Renderer会自动将数据解压后再交给g?
7. Texture Apply Modes SupportedQDX9Renderer不支持APPLY_DECALU理应用模式?
8. Texture Format RequestsQ纹理格式要求)Q精的U理像素映射依赖于目标硬件和昄器像素位宽?
9. Palettized Texture FormatsQ调色板U理格式的支持)?
10. Sphere MappingQ球面映)QGameByro中所有的渲染器都支持世界I间中的球面映射?
11. DX9Renderer支持加蝲包括DDS、TGA、BMP格式的文件。Cube textures和volume textures都能被正的加蝲Q但是只能通过三维坐标d?
12. GameByro的编辑器支持导出DX原格式的U理Qƈ在加载的时候不q行格式转换。将DX格式的纹理直接交lDX处理?
引擎渲染pȝ分析
GameByro的渲染是由渲染pȝ(Frame Rendering System) 控制的,帧渲染系l封装了每一个批ơ的l制q程Q这样可以方便的控制整个渲染程?
阴媄pȝQ?/strong>
GameByro的阴ql是使用帧渲染系l的典范Q通过对已有的cMl承的方式进行功能扩展,实现了灵zȝ阴媄渲染功能。GameByro自n支持三种光源的阴影图生成Q分别ؓ点光源(全方向光Q、^行光、聚光灯?
阴媄pȝl一׃个管理器负责控制。阴q法被封装成了一个个cd象中。系l中同时存在着几种阴媄技术,卛_以选择使用Q也可以同时使用几种阴媄技术作用于同一几何体。阴ql会自动裁剪掉那些没有阴影接受体的光源,同时在计每个光源所产生的阴影时Q会裁剪掉那些不受此光源影响的几何体Q达到性能优化的目的。用户很Ҏ寚wql进行扩展,实现自己的阴q法?
GameByro的阴׃要是ZshadowMap技术,点光源用了立方体阴影图QY阴媄则用了方差阴媄图技术(VSMQ?
GameByro的阴ql集成到了场景编辑器中,可以在编辑器中直接ؓ光源指定阴媄效果Q所见即所得,提高了开发效率?
Q如图所C,在编辑器中同时用点光源阴媄和方向光产生的Y阴媄Q?
地ŞpȝQ?/strong>
GameByro的terrain是简单易用的Qƈ且方便ؓ其扩展,用以满目的其他功能需求?
地Şpȝ׃下几部分l成Q?
1. NiTerrainComponentQ是整个地Şpȝ的入口,通过Terrain Interactor可以像访问容器一栯问NiTerrainComponent所有的sub-component?
2. Terrain Interactor 地Şq代器,通过Terrain Interactor可以启动所有的数据查询Q比如简单的撞,meta-data查询Q高度图导入和文件加载。几乎所有基于terrainpȝ的操作都是通过Terrain Interactor完成的?
3. NiTerrainSectorComponent 的内部主要包含一个负责组l地形数据块的四叉树。此对象负责从磁盘加载执行物体与地Ş的射U相交查询,转换地Ş几何体,理内部资源Q包括共享的点和shaderQ。同时NiTerrainSectorComponentq负责进行四叉树的遮挡查询和生成可见集?
4. Data Leaves and Blocks 四叉树的切分{是通过NiTerrainComponent讄的,四叉树的每个节点都包含一个地形块Q块的边长必L2的整数幂。四叉树的根节点{为NQ叶节点?Q由高到底细节依ơ增加,{?的叶节点所包含的地形块拥有最高的l节?
5. Material LayersQ地形系l通过掩码图来为地形指定材质。掩码的数据指定了地表的每个部分所对应的材质。同一地表最多支持四U材质,Ҏ各自的权重进行合,权重数据也是通过掩码来获得?
材质在地形的渲染pȝ中扮演着很特D的角色Q可以通过Ҏ质指定meta-data来附载其他的信息Q比如在雪地行走的声韻IQ同时也可以通过Ҏ质的U理贴图为地表增加顶Ҏ像素着色程序。此外每U材质可以包含如下指定类型的U理贴图Q?
l Diffuse/Base Map 反射贴图Q通过3?个通道军_反射光的颜色?
l Detail Map l节贴图Qؓ反射贴图提供Alpha倹{?
l Normal Map 法线贴图?
l Parallax Map 视差贴图Q用来保存表面像素的高度偏移信息?
此外GameByroq提供了单的地Ş线q踪和水面渲染。地形射U追t目前的实现很简单,是用一个射U查询地表的三角形,以后会与Physicsq行集成Q支持通过物理引擎l实现物体与地表的撞。水面渲染主要是用两张纹理动L合而成Q通过凹凸贴图来表现水波。引擎ؓ水面所在的地表生成一个灰度图Q保存地表距L面的高度。这里预先定义了两种颜色Q深水颜色和水颜色Q如果水面以下没有地表就直接使用深水颜色。再像素着色器中可以对高度图进行采P从而得出当前像素的水深Q然后根据这个深度在p和浅色之间进行差|得到最l水的颜艌Ӏ不q这个效果ƈ不是很理惻I如需更好的效果,q需要对其进行扩展,比如_增加反射和折等?
材质Q?
GameByro的材质主要是通过3DMAX的插件进行编辑的Qƈ提供了标准的材质节点?
Q在GameByro中,材质节点的主要作用是负责Ҏ着色过E生成相应的shader代码Q这个节点一共有9个通道Qƈ提供了大量可供调整的参数Q这样就可以让美术方便的使用逐像素光照、视差脓图、环境体贴图q些效果Q在游戏中,GameByro通过不同的材质属性和不同的光照环境编译出不同的shader代码Q以二进制的形式保存在磁盘上Q下ơ启动时载入内存Q这样就不用重复~译Q但在第一ơ进入游戏中时会有些延迟。灵zd大的材质是次世代引擎的重要特征,GameByro在这斚w做到了灵z,但是术仍然不能通过可视化工h制材质的处理q程Q只能编辑资源和修改参数Q编辑性还不够强大?
ShaderQ?
GameByro提供了一个极h展性的shaderpȝQƈ它与艺术创作工L密结合。GameByro定义了自qshader格式Q基于文本的.NSF文g和基于二q制?NSB文gQ?NSF文g~译后生?NSB文gQ这个过E即可以在开发时完成Q也可以在运行时q行?.NSF文g使用的是GameByro自定义的语法Qƈ使用自己的语法分析器其~译成相兛_^台的代码。实际上QGameByro的shaderpȝ在应用程序和GPUE序之间做了一层封装,屏蔽了不同^C间的差异Q得应用程序可以用相同的接口来与GPU之间q行交互Q而GPUE序最l还是通过cg或hlsl来实现的Q这样做的好处是Q当q_发生改变Ӟ只需要根据具体的q_来编写相应的shaderE序Q而不需要去修改应用E序。当Ӟshaderpȝ也直接支持cg和hlsl格式的文Ӟ使用Ҏ同NSF、NSB文g怼。GameByro的Shaderpȝ提供了一套完整的查错机制Q方便用者对E序q行debug?
GameByro的shaderpȝ集成C所有的配套开发工具中Q这样就实现了所见即所得,术在工具中可以直接看到资源在游戏中的效果Q从而提高了开发效率?
GameByro通过引入自己的shader文g格式q_提供了方便,同时也增加了pȝ的复杂性,使用hE微有点J琐Q开发h员又不得不去熟悉它的语法Q如果开发中不考虑跨^台的话,惌自己开发特效,也可以不使用NSF文gQ直接用fx格式的文件?
引擎自带的特D材?/strong>Q?
AdditiveLightMapsMaterialQ附加光照图材质,使用额外的光照图U理来合当前场景光照计的l果。下面的插图是用投光照图U理的渲染效果。)
AlphaTextureBlenderQ纹理Alpha混合Q?
AnisoQ各向异性光照,左下的飞机)?strong>Halo ASMSkinningLightingQ参与光照计的骨骼动画蒙皮Q?
ASMSkinningNoLightingQ不参与光照计算的骨骼动画蒙皮,效率l过了优化)
BaseBumpWithSpatialGlowQ带有辉光效果的凹凸贴图Q?strong> ChromeQ金属外表,使用基本U理、O反射cube map、镜面反cube map、法U脓囄效果实现cM于金属表面的光泽和质感,q支持基于此效果的蒙皮操作)
ColorizeQ用一个自定义的属性来动态的讄或者修攚wԌ
FXSkinning and HLSLSkinningQ用可~程渲染线q行骨骼动画的蒙皮)
GeneralGlowQ普通辉光效果)
GeneralDiffuseSpecularGlow(普通镜面反高光辉?
GeneralDiffuseSpecularQ普通镜面反高光,支持蒙皮操作Q?
Dot3BumpMapQ凹凸脓图,叛_皮效果)
Glass (ȝ效果)
OilyFilmQa状薄膜)
LuminanceTransferQ类g热源侦测仪的效果Q?strong> OutliningQ蓝图或草图Q?
ParallaxMappingQ视差脓图,下面叛_是用了ParallaxMapping的效果)
PerPixelLightingQ逐像素光照,同时支持固定线和可~程渲染线Q?
SkinnedToonShadingWithOutlineQ卡通渲染,支持骨骼蒙皮、描边)
U理Q?
GameByro的纹理系l支持多重纹理、投纹理、动态纹理、特效纹理,q且使用U理的一些高U特性是q_无关的。当然GameByro中纹理的功能仍然受限于硬件特征。主要特性和限制见渲染器?
工具及插Ӟ
GameByro提供了场景编辑器Q资源浏览器Q资源的物理模型览器、动ȝ辑工P字体创徏工具Q以?DMAX、MAYA插gQ材质编辑是在插件中q行的,q套工具包含了完整的3D资源开发流E?strong> 物理pȝ
GameByro通过PhysX提供物理仿真模拟Q只需几行单的代码可以将GameByro的场景和PhysX建立赯p,不过需要在昑ּ的调用相x口来q行物理仿真对象和场景对象间的同步,卛_物理仿真的结果作用于场景中的对象。同Ӟq可以通过参数来决定是否需要等待物理仿真运的l果。这׃证了一定的灉|性,例如Q用户可以不寚w态物体进行同步,辑ֈ优化的目的。还可以通过把更新的接口攑֜下一ơ@环开始的时候调用,q样可以减少׃{待物体仿真模拟造成U程的阻塞,充分的利用了CPU资源?
_子pȝQ?
GameByro的粒子系lؓ3ds max ?Maya{粒子生成工h供了完整的插件系l,q完全支持Y_子Q即_子会受到力的媄响?
处理引?/strong>Q?
GameByro实现了一个完全跨q_的流处理引擎。用者可以方便的利用它完成多U程dQ如资源的加载等Q完全发挥多处理器^台的威力Q而不用关注底层细节?
效率分析Q?/strong>
Z对GameByroq行全面的评伎ͼW者试着对GameByro的渲染效率进行了单的试Q本人的机器配置如下Q?
l CPUQE2180
l GPUQ?400PRO
l 内存Q?GB
基本可以代表当前中低端玩家的配置?
场景一Q?
下图Z个大的室外地形场景,包括建筑物、水面,地表使用了法U脓图,除了植被以外Q基本包含MMORPG的大部分室外场景要素。最后测试的l果q_帧数?1帧。数偏低的原因是整个场景用了逐像素光照,同时没有采用LODq行优化?
场景?/strong>Q?
如图所C场景中?00个角色在做不同的动画Q每个角色有51个骨|整个场景共有287900个多边Ş?56600个顶点,q且场景中有一个点光源和^行光源,在没有阴q情况下数大U在27~28帧左叟뀂这些骨骼动M用了GameByro提供的骨骼动M化方法,性能有一定提升?
׃上结果可以看出,GameByro的骨骼动L染效率尚可,而室外地形渲染系l的性能E显不Q再没有角色和植被的情况下,仅能辑ֈ22帧左叻I如果在实际的游戏环境q要上其他游戏pȝQ网l、逻辑、UI{)的性能消耗,q是不以让玩家畅q行游戏的,q需q一步手动的优化?
ȝQ?
GameByro拥有一套灵zȝ架构Q便于用和扩展Q易于上手;配套的开发工h较齐全,拥有完整的美术开发流E;完全支持跨^収ͼ便于游戏的移植;同时׃Z保证灉|性,在设计上面比较偏重于功能和扩展,而不是性能?
高度的可定制性带来的是较低的整合QGameByro没有提供游戏框架Q地囄辑器也仅仅实C最通用的功能,卛_表和物g的摆放。这p明,如果要用GameByro开发品,需要自己整合GameByroQƈ对其q行一定的修改和扩展。这也是它与UnrealEngine3区别最大的地方?
作者:叶v涟漪
]]>
GameBryo拥有一套复杂的材质pȝQ这套材质系l可以根据渲染对象的状态和属性生成不同的shader代码Q提高了渲染程的适应性,可以使你定义一套材质能适应多种渲染对象。同ӞGameByroshader的初始化和用插件化Q方便与术工具集成Qƈ且实Cq_无关性。ؓ了实现这些目的,GameByro使用了一套复杂的机制Q本文主要解析GameByro如何生成、编译ƈ使用shader代码?
GameBryo的shader的接口封装在NiShader中,点数据声明,帔R表的讉KQ渲染状态的讄都是通过q个c(有点cM于D3DeffectQ。在E序q行NiShader是由NiShaderFactory负责理的,NiShaderFactory通过NiShaderLibrary从文件中创徏shaderQ用全局性的map理h。NiShaderLibrary通过解析shader文本创徏NiShader对象Qƈ调用3D囑Ş接口~译shader代码Q将q个cMdll的Ş式封装,可以作为插件来使用。NiShadercȝ创徏可以通过解析文g来进行,也可以通过C++的类来定Ӟ只需从NiShader上承即可。GameByro为PCq_提供了一个NiD3DXEffectShaderLib库,q个库提供了解析shader文g和初始化shader对象的功能。用户只需按GameByro定义的格式编写shader代码的语意和注释QNiD3DXEffectShaderLibrary׃Ҏ文本来创建NiD3Dshader对象Q在应用E序中就可以通过Techinqe的名U来讉Kq个对象。通过q种机制Q我们将shader文本文g攑֜相关术工具指定的目录下Q在工具中就可以使用q些shaderQƈ且能够通过shader的语意和注释为相兛_数和变量生成UIQ方便美术调试?
WINq_上的整个程如下Q?
1. 应用E序在启动时会先初始化整个shaderpȝQ接下来导入Shader解析库和加蝲库(dll的Ş式)?
2. 接下来应用程序将NiD3DShader的初始化工作委托lNiShaderLibrary来处理,NiShaderLibrary首先通过NiD3DXEffectLoader载入所有的shader文本文gQƈ通过NiD3DXEffectParser解析文本生成NiD3DXEffectFile对象Q同时NiD3DXEffectLoaderq负责将shader代码~译成二q制形式的GPUE序?
3. 最后由NiD3DXEffectTechnique负责通过NiD3DXEffectFile上的信息生成NiD3Dshader对象?
4. 所有的shader对象创徏后,NiShaderLibrary的初始化q束了Q最后由NiShaderFactory负责l一理?
NiMaterial为渲染对象生成和定义Shader,NiMaterialInstance为渲染对象分?和Cach Shader。NiFragmentMaterial提供了一个Shader Tree框架Q在它的l承cM可以使用q个框架搭徏shader tree。这个机制允许NiFragmentMaterialҎ对象不同的渲染状态生成不同的shader代码QCach在内存中Qƈ保存到磁盘文件。GameByro描述W的概念大量使用Q包括前面提到的Shader解析q程也是通过描述W来传递信息。在材质pȝ中主要用了NiMaterialDescriptor和NiGPUProgramDescriptorq个两个cd描述W,q两个类中保存的信息是兼容的Q都是ؓ了描q某U材质在渲染对象的某一特定渲染状态下所对应的GPUE序的特征。NiFragmentMaterial通过渲染目标的状态和属性生成NiMaterialDescriptorQƈ通过NiMaterialDescriptor查找匚w的shaderQ如果找不到Q则通过shader tree生成相应的shaderE序Qƈ保存到磁盘文件中。当下一ơ应用程序启动时可以通过q个文g直接创徏NiShader对象。可以说通过NiFragmentMaterial生成的shader代码是ؓ特定的渲染对象在特定的情况下量n打造的?
整个q程的详l流E如下:
1. 在每ơ渲染一个物体之前,NiMaterialInstance会先判断q个物体的shaderE序是否需要更斎ͼ如果不需要更斎ͼq接返回当前Cach的NiShaderQ如果需要更斎ͼ NiMaterialInstance首先会根据物体的渲染状态ؓ其生成一个NiMaterialDescriptorQ然后将q个NiMaterialDescriptor和当前Cach住的NiShaderq行比较Q如果匹配仍然返回当前Cach的NiShaderQ如果不匚wQ将获得shader的工作{交给NiMaterialq行?
2. NiMaterial首先通过q个NiShaderFactory 查询匚wq个NiMaterialDescriptor的NiShaderQ如果找不到Q就通过NiMaterialDescriptor生成NiShaderQ同时生成一DShader代码Qƈ保存Cshader描述W中的特征码来命名对应的shader文g?
3. 当获得相应的NiShader对象后,NiMaterialInstance会调用NiShader的SetupGeometry接口Q在q个接口中会q行点声明?
以下是NiMaterialInstance为Geometry选择shader的代码:
NiShader* NiMaterialInstance::GetCurrentShader(NiRenderObject* pkGeometry,
const NiPropertyState* pkState,
const NiDynamicEffectState* pkEffects)
{
if (m_spMaterial)
{
bool bGetNewShader = m_eNeedsUpdate == DIRTY;
if (m_eNeedsUpdate == UNKNOWN)
bGetNewShader = pkGeometry->GetMaterialNeedsUpdateDefault();
// Check if shader is still current
if (bGetNewShader && m_spCachedShader)
{
bGetNewShader = !m_spMaterial->IsShaderCurrent(m_spCachedShader,
pkGeometry, pkState, pkEffects, m_uiMaterialExtraData);
}
// Get a new shader
if (bGetNewShader)
{
NiShader* pkNewShader = m_spMaterial->GetCurrentShader(
pkGeometry, pkState, pkEffects, m_uiMaterialExtraData);
if (pkNewShader)
{
NIASSERT(m_spCachedShader != pkNewShader);
ClearCachedShader();
m_spCachedShader = pkNewShader;
if (!pkNewShader->SetupGeometry(pkGeometry, this))
ClearCachedShader();
}
else
{
ClearCachedShader();
}
}
m_eNeedsUpdate = UNKNOWN;
}
return m_spCachedShader;
}
如果想通过NiFragmentMaterial实现自己的shader tree需要在NiFragmentMaterial提供的接口中实现自己D代码的逻辑Q代码块由NiMaterialLibraryNode装QNiMaterialLibraryNode既可以直接写C++代码来定义,也可以先写成XML脚本Q再׃门的解析工具转换成C++代码?
由NiStandardMaterial生成的shader代码文g如下图所C:
文g名就是NiMaterialDescriptor的掩码,用来标识的shader代码的行为?
Shader代码的行为描q如下:
Shader description:
APPLYMODE = 1
WORLDPOSITION = 0
WORLDNORMAL = 0
WORLDNBT = 0
WORLDVIEW = 0
NORMALMAPTYPE = 0
PARALLAXMAPCOUNT = 0
BASEMAPCOUNT = 1
NORMALMAPCOUNT = 0
DARKMAPCOUNT = 0
DETAILMAPCOUNT = 0
BUMPMAPCOUNT = 0
GLOSSMAPCOUNT = 0
GLOWMAPCOUNT = 0
CUSTOMMAP00COUNT = 0
CUSTOMMAP01COUNT = 0
CUSTOMMAP02COUNT = 0
CUSTOMMAP03COUNT = 0
CUSTOMMAP04COUNT = 0
DECALMAPCOUNT = 0
FOGENABLED = 0
ENVMAPTYPE = 0
PROJLIGHTMAPCOUNT = 0
PROJLIGHTMAPTYPES = 0
PROJLIGHTMAPCLIPPED = 0
PROJSHADOWMAPCOUNT = 0
PROJSHADOWMAPTYPES = 0
PROJSHADOWMAPCLIPPED = 0
PERVERTEXLIGHTING = 1
UVSETFORMAP00 = 0
UVSETFORMAP01 = 0
UVSETFORMAP02 = 0
UVSETFORMAP03 = 0
UVSETFORMAP04 = 0
UVSETFORMAP05 = 0
UVSETFORMAP06 = 0
UVSETFORMAP07 = 0
UVSETFORMAP08 = 0
UVSETFORMAP09 = 0
UVSETFORMAP10 = 0
UVSETFORMAP11 = 0
POINTLIGHTCOUNT = 0
SPOTLIGHTCOUNT = 0
DIRLIGHTCOUNT = 0
SHADOWMAPFORLIGHT = 0
SPECULAR = 1
AMBDIFFEMISSIVE = 0
LIGHTINGMODE = 1
APPLYAMBIENT = 0
BASEMAPALPHAONLY = 0
APPLYEMISSIVE = 0
SHADOWTECHNIQUE = 0
ALPHATEST = 0
NiStanderMaterial是Ҏq些掩码的数据来生成shader代码Q用户可以通过重蝲GenerateVertexShadeTree、GeneratePixelShadeTree、CreateShaderq些接口来定义自qshader生成规则?
通过前几节我们可以了解到Q想定义自己的材质,一是通过~写shader代码完成。在应用E序初始化的时候,q些shader代码会被初始化成NiShader对象Q进一步的通过NiShader对象来初始化NiSingleShaderMaterial对象Qƈ分配l渲染对象。在GameByro默认的渲染流E中Q这些步骤都是自动进行的Q美术只需?DMAX插g中ؓ几何体的材质指定ShaderE序Q导出到nif文gQ应用程序就能正加载ƈ渲染Q二是定义自qNiMaterialFragmentc,在类中定义如何生成shaderQ在应用E序q行时只要将q个cȝ实例指派l几何体Q这个类׃自动为几何体生成shader。这两种方式对于术人员来说Q主要区别在于,采用W一U方法定义的材质Q其渲染数据的设|必M格符合shader代码中所需的数据,否则׃报错。(比如_点数据必M格符合shaderE序的定义,必须为shader中每个采样器提供格式正确的纹理)Q而采用第二种Ҏ定义的材质,有很高的容错和适应性,但是q种定w性和适应性需要自己写代码来完成,GameByro提供的NiStanderMaterial提供了q套完整的机制。每个脓图槽内的贴图如果你设|就会生成相应的贴图处理程Q如果不讄Q就没有q张贴图的处理流E?
Z验证q个q程Q笔者尝试增加了一个自qshaderҎ——SubSurfaceScatteringQ简U?sQ其原理是模拟光在半透明物体中散的效果。由于该效果无须预处理过E,所有的贴图均来自磁盘文Ӟ所以比较容易融合到GameByro工作中?
W者将在FX COMPOSER中调试通过的fx文g攑օSDK中的SDK\Win32\Shaders\Data目录下,?DMAX的材质面杉K择GameByroShaderQ然后就可在昄shader的组合框中看到文件中定义的TechinqeQ选择点击apply按钮Q就会出现自定义的参数调整界面?
通过调整参数Q最l得到皮肤和玉器的渲染效果如下:
皮肤
玉器
GameByro的这套开发流E非常方便直观,但是术仅能为shaderE序分配静态的数据源,比如说光照图{,CubeMap{;而一些在E序中实时生成的U理数据则无法整合到术工具中,比如说阴影图、折图、反图{,q些都需要程序写代码来实现。调试v来就不大方便了。大部分情况下,我们只需要用GameByro提供的NiStanderMaterial可以完成大部分材质的需求,Ҏ的效果可以自己写shader或者通过引擎提供的shader库来完成Q只有当我们需要即Ҏ复杂的情况做很多不同的处理时Q我们才需要重载NiFragmentMaterial搭徏自己的shader tree。不q搭建shader tree的程序一般比较复杂,~写隑ֺ大,虽然引擎允许通过XML文g来编写材质节点,但是使用h仍然不方ѝGameByroq没有提供相关的后期处理的开发工P后期处理的特效ƈ不能所见即所得,q方面还需完善?
GameByro为几何体在特定的环境下生成专用的shader代码Q具有一定的灉|性,但是也付Z以下代hQ?
l 分析几何体的属性和当前状态,为其生成shader代码的过E有性能损耗?
l Shader代码生成后会保存到磁盘文件中Q这个过E如果不使用异步Q可能会引vd?
l 生成的NiShader对象会有内存消耗。由于GameByro默认的实现是所有的shader文g初始化成NiShader对象Q所以当游戏q行的时间久了以后会生成大量的shader文gQ这时候内存的消耗可能会很可观,同时加蝲的时间也会增加。不q可以自己控制加载的程Q在q里q行性能优化?
作者:叶v涟漪