﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-芳草春晖-文章分类-游戏引擎</title><link>http://www.cppblog.com/CrazyDev/category/13583.html</link><description>偶尔记录自己思绪的地方...</description><language>zh-cn</language><lastBuildDate>Thu, 20 May 2010 03:51:27 GMT</lastBuildDate><pubDate>Thu, 20 May 2010 03:51:27 GMT</pubDate><ttl>60</ttl><item><title>四叉树和八叉树的剔出选择(转)</title><link>http://www.cppblog.com/CrazyDev/articles/115700.html</link><dc:creator>CrazyDev</dc:creator><author>CrazyDev</author><pubDate>Tue, 18 May 2010 09:23:00 GMT</pubDate><guid>http://www.cppblog.com/CrazyDev/articles/115700.html</guid><wfw:comment>http://www.cppblog.com/CrazyDev/comments/115700.html</wfw:comment><comments>http://www.cppblog.com/CrazyDev/articles/115700.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/CrazyDev/comments/commentRss/115700.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/CrazyDev/services/trackbacks/115700.html</trackback:ping><description><![CDATA[英文原文出处：<a title=http://www.gamedev.net/reference/articles/article1485.asp href="http://www.gamedev.net/reference/articles/article1485.asp" target=_blank>http://www.gamedev.net/reference/articles/article1485.asp</a><br>翻译:宋晓宇<br><br><strong><span style="FONT-SIZE: 18pt">介绍：</span></strong><br>　　传统计算机图形应用--特别是的应用的需要一个实时，交互的方法来现实--通过处理一个发送到显卡的数据的最有效的图形数据子集的方法来决定图形数据的显示，而不是传送全部的数据，四叉树，八叉树，Bsp树，背面剔出，pvs集合很多其他方法都是针对这个目的而提出的。<br><br>　　流行的计算机图形卡近些年在处理能力和处理方法上程指数增长，当前的状态揭示出很多时候应该更好的和快速的找到一个好的数据集把它们送到显卡里，而不是把精力放在努力的找到一个最好的数据集。这样的数据集是一个近似的最好的数据集并且能经常发现它都有十分有效的算法，因此手头上的任务因此就变成了回顾已经存在的技术和算法并且尝试找到最快的选择，这对于找到最好的解决问题的方法并不是什么负担。<br><br><strong><span style="FONT-SIZE: 18pt">一、四叉树：</span></strong><br>　　四叉树作为数据处理表达技术的一个好方法已经有很多年了，特别是地形渲染引擎都能利用他很有效的作为剔出机制，剩余的这个章节将会给出一个小数量的说明四叉树并且传统的，高层的方法使用四叉树，在描述一个快速的方法之前。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/4885992475.jpg" target=_blank><img alt=http://school.ogdev.net/upload/img/4885992475.jpg src="http://school.ogdev.net/upload/img/4885992475.jpg"></a></div>
<br><br><strong><span style="FONT-SIZE: 18pt">四叉树数据结构：</span></strong><br>四叉树被描述通过对应每个父节点传递四个子节点，在一个地形渲染上下文里，根节点将会表达为这个围绕地形的正方形区域集，自节点表示为&#8220;左上&#8221;，&#8220;右上&#8221;，&#8220;左下&#8221;和&#8220;右下&#8221;象限，这些象限由根节点组成并且每一个都是由四个字节点递归的定义下面的描述将会说明这些概念一个四元数的数据结构并不负责,下面的伪码演示了这个四叉树的节点：
<div class=code>TPosition = record<br>x,y : float;<br>end;<br><br>PQuad = pointer to TQuadNode;<br>TQuad = record<br>p1,p2,p3,p4 : TPosition; // corners<br>c1,c2,c3,c4 : PQuadNode; // children<br>end;</div>
<br>一个简单的递归初始化一个深度为max_depth的四叉树如下：<br>
<div class=code>function InitQuad(x,y,w : float; lev : int) : PQuad;<br>var<br>tmp : PQuad;<br>begin<br>inc(lev);<br>if lev &gt; max_depth then return(nil);<br>new(tmp);<br>...initialize tmp node with corner data<br>w := w / 2;<br>tmp^.c1 := InitQuad(x - w, y - w, w, lev);<br>tmp^.c2 := InitQuad(x - w, y + w, w, lev);<br>tmp^.c3 := InitQuad(x + w, y + w, w, lev);<br>tmp^.c4 := InitQuad(x + w, y - w, w, lev);<br><br>return(tmp);<br>end;</div>
<br><strong><span style="FONT-SIZE: 18pt">基于四叉树的背面剔除</span></strong><br><br>　　在很多应用里主要的四叉树函数是提供一个有效的的视截体数据剔除,下面的基于视点的高层视截体剔除已经够了：
<div class=code>procedure CullQuad(q : PQuad);<br>begin<br>case Clip_Quad_against_View(q) of<br>inside : DrawQuad(q);<br>outside : ; // ignore quad<br>else begin<br>...CullQuad children of q<br>end;<br>end;<br>end;</div>
<br>Clip_Quad_against_View 是cullquad的关键函数，并且当然运行的函数和方法来解决这个问题是否是一个四元（或者多边形）交叉于这个视见体，通过交叉视见体金字塔作为平面集合检测平面作为一套光线，然后连续测试几何体的光线怎么和可视体平面相交的，光线相交测试的方程在很多３ｄ几何的书上都可以找到，这里不做重复。<br><br><strong><span style="FONT-SIZE: 18pt">二、选择基于四叉树的视点剔除：</span></strong><br><br>上面描述的方法一般情况下会得出正确的结果，但是他没有给这个简单的静态的四叉树提供任何东西，好多的优化可以应用到四叉树的剔除过程，下面的两个阶段产生了一个很快的和很有效的基于四元数的剔除：<br><br>　　*基于点的剔除：一个完全的剔除计算，他通过记录相关的四个角的可视点。<br><br>　　很多这样的情况可以被考虑，例如：如果一个单独的点在视见体内发现，那么这个块就在视见体内，如果所有的点都在视见体的一面，那么这个块就不在视见体内．一个数量的可能性就存在，并且需要通过一个完全的计算，但是上面给出的两种情况得出了一个充足的启发性来接受，丢弃或者将来重新定义一个潜在的块。<br><br>　　*Ｍｏｍｏｉｚａｔｉｏｎ：是一种储存计算结果的技术并且还需要相同的计算时查找储存的结果。<br><br>　　四元数作为一个静态结构，这种特殊角的点经常都是相同的，另外，很多块的角是四个块共享的，并且在循环传递四元数的角一遍又一遍的，通过决定一个角的相关位置关联这个可视体和方便的储存这个四元数的的计算结果。<br><br>　　下面的伪代码声明了这个算法，对于一个四元数横跨的区域是(0,0)到(256,256);<br>
<div class=code>(globals:) <br>Memoized : array[0..256,0..256] of byte;<br>posx,posy : float; // origin of FOW<br>Lx,Ly,Lz : float; // normal of left FOV plane<br>Rx,Ry,Rz : float; // normal of right FOV plane<br><br>function CheckPos(x, y : int) : int;<br>// checks position x,y against FOV planes, memoize<br>// Results: bit 1 (inside), bit 2 (left), bit 3 (right)<br>var<br>res : int;<br>begin<br>res := 0;<br>if (x-posx)*Lx + (y-posy)*Ly &gt; 0 then res := 2;<br>if (x-posx)*Rx + (y-posy)*Ry &gt; 0 then res := res or 4;<br>if res = 0 then res := 1;<br>Memoized[x,y] := res;<br>return res;<br>end;<br><br>function TestQuad(x, y, w : int) : int;<br>// quad-midpoint: (x,y)<br>// quad-width: 2w<br>// test quad against FOV<br>// Results: 0 (out), 1 (partially in), 2 (completely in), -1 (unknown)<br>var<br>m1,m2,m3,m4 : int;<br>tmp : int;<br>begin<br>m1 := Memoized[x - w, y + w];<br>if m1 = 0 then CheckPos(x - w, y + w);<br>m2 := Memoized[x + w, y + w];<br>if m2 = 0 then CheckPos(x + w, y + w);<br>m3 := Memoized[x + w, y - w];<br>if m3 = 0 then CheckPos(x + w, y - w);<br>m4 := Memoized[x - w, y - w];<br>if m4 = 0 then CheckPos(x - w, y - w);<br><br>tmp := m1 and m2 and m3 and m4;<br>if tmp = 1 then<br>return 2; // quad completely inside FOV<br><br>if m1 or m2 or m3 or m4 = 1 then<br>return 1; // quad partially inside FOV<br><br>if tmp =&gt; 0 then <br>return 0; // quad completely outside FOV<br><br>return -1; // else quad state undetermined<br>end;</div>
<br>上面的函数应该被清除并且及早的需要整数的的四元块程序<br>
<div class=code>procedure CullQuadtree;<br>begin<br>Clear_Memoized_Array_to_Zero;<br>CullQuad(Quadtree_Root);<br>end;<br><br>procedure CullQuad(q : PQuad);<br>begin<br>case Test(quadmidpoints and half-width) of<br>2 : ...handle complete quad<br>1 : ...handle partial quad<br>0 : ; // ignore quad<br>else begin<br>...CullQuad children of q<br>end;<br>end;<br>end;</div>
<br><strong><span style="FONT-SIZE: 18pt">三、另外需要考虑的：</span></strong><br><br>很多在代码里和一般的算法里都需要被考虑的是：<br><br>　　*四叉树算法的版本里表现出的只剔除左/右，不是近裁剪面，不是远裁减面或者上下都没有考虑，另外只有平面视角。因此这个算法只覆盖了四叉树的的高度剔除和视见面沿着这个四叉树的面。<br><br>　　我们扩展这些代码沿着3d的四叉树，增加如：应用四叉树的移出算法-任何可视的位置和方向将会正确的进行的东西。<br><br>　　*另外很多附加的点，视见体需要考虑执行的比如：如果两个点在视见体的前面并且对这FOV的一个面，这个块部分在这个视见体里，对于很多的算法，这样的关卡满足这个结果。<br><br>　　*主要关心这个算法的需要是在记忆需求里，尽管一般的沉浸记忆对于每个可能的块某些点需要一个附加的字节。因此如果正方形的区域有n个间隔，每个面都都需要n个字节来储存，通过典型的只有一个碎片的内存被一个给定的遍历访问，这一部分就被访问了很多次。<br><br>　　这篇文章总结了算法的表达，我发现这是一个有用积极的算法，如果你查询了相关的算法并且有感触可以写信给我咨询。
<img src ="http://www.cppblog.com/CrazyDev/aggbug/115700.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/CrazyDev/" target="_blank">CrazyDev</a> 2010-05-18 17:23 <a href="http://www.cppblog.com/CrazyDev/articles/115700.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>次世代游戏引擎光影制作简述</title><link>http://www.cppblog.com/CrazyDev/articles/115695.html</link><dc:creator>CrazyDev</dc:creator><author>CrazyDev</author><pubDate>Tue, 18 May 2010 08:53:00 GMT</pubDate><guid>http://www.cppblog.com/CrazyDev/articles/115695.html</guid><wfw:comment>http://www.cppblog.com/CrazyDev/comments/115695.html</wfw:comment><comments>http://www.cppblog.com/CrazyDev/articles/115695.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/CrazyDev/comments/commentRss/115695.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/CrazyDev/services/trackbacks/115695.html</trackback:ping><description><![CDATA[<strong>一、游戏光影的范畴 <br></strong>实际游戏制作当中,狭义的灯光只是指程序实时根据灯光影响的顶点色和动态阴影,但实际应用当中,影响玩家整体感觉的光影效果,也就是广义上的光影效果包含的范围要更加广泛。<br>程序的即时光影,shader的定制固然对光影效果至关重要,美术制作的lightmap,normalmap,VertexColor乃至Glow都可以构成一般玩家认知上的灯光效果。<br>即是说,在游戏制作应该以游戏玩家的感觉为基准整体的设置光影的效果,而并非仅以技术手段作为分界,对于游戏制作来说,综合程序美术的多种表现手段,最终呈现给玩家的光影的感觉,才是游戏制作当中的灯光。<br><br><strong>二、基本灯光系统介绍及使用方法</strong><br>从画面渲染来说,无论一般网游还是次世代游戏,运算的规则构建是大体相似的,我们可以通过分析这个构架得出其中制作各种光影效果的可能性,下图是比较常规的各种图素经过如何的运算达成了最终画面的pipeline。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3870298832.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3870298832.jpg " src="http://school.ogdev.net/upload/img/3870298832.jpg"></a></div>
<br><br>在这样的pipeline中,对最终光影有影响的环节<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3871184690.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3871184690.jpg " src="http://school.ogdev.net/upload/img/3871184690.jpg"></a></div>
<br><br><strong>Dark Map</strong><br>Dark Map 也被称为 Lightmap，这是我们目前常使用的方法之一。<br>这个通道一般放置bake过的灯光贴图,或者Ambient Occlusion Map, 常用的方式是赋予一个另外的UV ,这一张很小的Dark Map就可以在没有灯光的情况下获得良好的光影效果,很多引擎当中是可以由灯光自动生成lightmap的,它的效果一般用在静态物体上。<br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3875870158.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3875870158.jpg " src="http://school.ogdev.net/upload/img/3875870158.jpg"></a></div>
<br><br><strong>Project lights </strong><br>Project lights就是一般我们所说的硬件灯光,由于效果是即时运算,有一定的硬件要求,推荐一般不要超过3盏,否则对效率会有一定的影响.除非完全必要，不建议使用这种方法。<br>Project lights的运用一般是三个平行光来构筑一个整体的天光环境,(另外 Ambient light在这个过程中也有重要的意义),由于Project lights是对资源比较耗费的部分,应该由程序尽量优化。<br><br><strong>Base map </strong><br>Base map就是diffuse map就是我们常说的：贴图。<br>在贴图上我们可以在固有色之上进行光和阴影的绘制,从这个角度来说也是影响光影效果的重要方式,这种做法几乎不会在光影表现上耗费任何系统资源。效果主要依赖美术人员绘制贴图的水平。<br>同时，这也是在美术人员能力许可的条件下，完成大部分光影的最佳方式。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3691264776.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3691264776.jpg " src="http://school.ogdev.net/upload/img/3691264776.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3694395147.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3694395147.jpg " src="http://school.ogdev.net/upload/img/3694395147.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3694921405.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3694921405.jpg " src="http://school.ogdev.net/upload/img/3694921405.jpg"></a></div>
<br><br><strong>Project Shadow</strong> <br>Project Shadow和project light一般有相同的光源点产生,作用是生成阴影,由于project Shadow是动态的阴影,一般用于表现有动画的物体,实际运用中Project Shadow和lightmap往往互补使用,分别解决静态和动画物体的阴影。同样，这种方法非常耗费系统资源，除非完全必要，不建议使用这种方法。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3698467791.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3698467791.jpg " src="http://school.ogdev.net/upload/img/3698467791.jpg"></a></div>
<br><br><strong>Normal Map </strong><br>Normal Map是次世代游戏常用的技术,他解决了机能对面数限制的情况下如何体现物体细节的问题,通过RGB三色代替XYX三个Normal轴向值的方式获得了更多物体细节。<br>Noraml Map机理的主要是在各种灯光位置会对物体的diffuse specular和reflect进行扭曲来达到近似真实,但是他不会对base map产生影响,既是说normal map影响的仅仅是材质本身,而非贴图。<br>由于Normal Map的这一盲点,prallax map(或displacemap)方式被创造出来弥补normal map不能扭曲basemap的缺陷,实际应用中,常常在运用Normal Map之后配合prallax map来达到更好效果。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3704374670.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3704374670.jpg " src="http://school.ogdev.net/upload/img/3704374670.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3705272991.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3705272991.jpg " src="http://school.ogdev.net/upload/img/3705272991.jpg"></a></div>
<br><br><strong>Vertex Color </strong><br>Material of vertex map是游戏中重要的运用,顶点色可以适用的范围很多,透明柔体等都可运用,但是最主要的应用在色彩上,通过刷取顶点色可以方便迅速的改变物体的色调,对系统资源的要求也较低,但是他与灯光色彩有一定冲突,通常情况下灯光色彩与顶点色不会用于同一物体以避免冲突,顶点色可以手动绘制或者灯光烘培,在面数较低的物体上能发挥更多的影响。<br>顶点色是对游戏光影最佳的解决方法之一，也是最常用的解决方法。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3707259314.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3707259314.jpg " src="http://school.ogdev.net/upload/img/3707259314.jpg"></a></div>
<br><br><strong>Gloss map</strong> <br>Gloss map即我们所说高光贴图,一般灯光对物体高光是基于顶点色进行影响,如角色毛发,皮衣等物体财政很难表现出质感,如果运用Gloss map即可借助贴图的肌理界定高光的外形,也是可以通过较少资源占用达到影响光影效果的手段,另外很重要一点,Gloss map与我们一般常用3D软件中的界定不同,并非仅仅影响高光,对反射贴图也有影响。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3721312782.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3721312782.jpg " src="http://school.ogdev.net/upload/img/3721312782.jpg"></a></div>
<br><br>Gloss map其实并不一定需要是灰度图,用彩色贴图一样可以使用 <br><br><strong>Environment Map </strong><br>Environment Map可以理解为我们一般所谓的反射贴图,有球形和盒体等多种方式,表现金属物体时常用,可以通过 gloss map进行限制。<br>Enivironment Map在max下的贴图槽为Reflection slot,默认的贴图放置进去会用球形坐标方式进行反射,如果对贴图有更高要求,也使用box贴图,六张贴图组成反射盒,如果是dds box贴图可以直接调用,但是坐标会有不同。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3723172328.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3723172328.jpg " src="http://school.ogdev.net/upload/img/3723172328.jpg"></a></div>
<br><br><strong>MixGlow Map </strong><br>Glow Map一般用以体现各种发光物体,如灯，火焰等等，是比较常用的特效类channel，在Glow 贴图栏里面贴上所用glow map ，会和其他贴图的最终效果产生相乘效果。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3729843678.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3729843678.jpg " src="http://school.ogdev.net/upload/img/3729843678.jpg"></a></div>
<br><br><strong>Post Effects</strong><br>除去上述效果，在Final效果输出之后，有时我们会通过各种post effect效果对画面加强，如Glow等，对画面光影感觉也会有巨大的影响。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3732810506.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3732810506.jpg " src="http://school.ogdev.net/upload/img/3732810506.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3733384620.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3733384620.jpg " src="http://school.ogdev.net/upload/img/3733384620.jpg"></a></div>
<br><br><strong>Channel Mix</strong><br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3743859463.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3743859463.jpg " src="http://school.ogdev.net/upload/img/3743859463.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3744321500.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3744321500.jpg " src="http://school.ogdev.net/upload/img/3744321500.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3745548849.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3745548849.jpg " src="http://school.ogdev.net/upload/img/3745548849.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3748110257.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3748110257.jpg " src="http://school.ogdev.net/upload/img/3748110257.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3749159018.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3749159018.jpg " src="http://school.ogdev.net/upload/img/3749159018.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3750176913.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3750176913.jpg " src="http://school.ogdev.net/upload/img/3750176913.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3750872451.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3750872451.jpg " src="http://school.ogdev.net/upload/img/3750872451.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3753136923.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3753136923.jpg " src="http://school.ogdev.net/upload/img/3753136923.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/37537107094.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/37537107094.jpg " src="http://school.ogdev.net/upload/img/37537107094.jpg"></a></div>
<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3755084591.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3755084591.jpg " src="http://school.ogdev.net/upload/img/3755084591.jpg"></a></div>
<br><br><strong>灯光系统分级方法及制作要点</strong><br>在实际游戏制作当中,一般不会将所有的channel都开启,一方面其实要做出一款出色的游戏画面并非取决于使用多少新技术,令一方面也要考虑程序的负载。<br>根据游戏的种类和需求选择正确的方法合理的节省资源能够让游戏拥有更好的效果。<br><br><strong>技术是手段，不是诉求</strong> <br>《Doom III》 虽然在当时是技术绝对领先的游戏,但是美术过度的依赖先进的技术,导致在美术风格上的切入不够,本应该更优秀的游戏,未能更上一步。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/3763459715.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/3763459715.jpg " src="http://school.ogdev.net/upload/img/3763459715.jpg"></a></div>
<br><br>与之相对,几年后推出的《Lost Plant》则成功找回了游戏美术最本质的东西，使技术成为一种手段。<br><br>
<div style="OVERFLOW-X: auto; WIDTH: 100%"><a href="http://school.ogdev.net/upload/img/37655109434.jpg" target=_blank><img alt="http://school.ogdev.net/upload/img/37655109434.jpg " src="http://school.ogdev.net/upload/img/37655109434.jpg"></a></div>
<br><br>哪些地方？该使用哪些手段?<br>我们首先要明确，在一个成熟的美术团队中。对于一款游戏画面中的各种光影，是如何去表现，以达到画面效果和系统负担最佳的结合点的？<br>我们需要找到：最优化的手段。<br><br>From <a title=http://www.wz-online.net/web2/home/Detail.asp?id=47&amp;FromType=1 href="http://www.wz-online.net/web2/home/Detail.asp?id=47&amp;FromType=1" target=_blank>http://www.wz-online.net/web2/home/Detail.asp?id=47&amp;FromType=1</a>
<img src ="http://www.cppblog.com/CrazyDev/aggbug/115695.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/CrazyDev/" target="_blank">CrazyDev</a> 2010-05-18 16:53 <a href="http://www.cppblog.com/CrazyDev/articles/115695.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Gamebryo使用经验谈（1）</title><link>http://www.cppblog.com/CrazyDev/articles/113034.html</link><dc:creator>CrazyDev</dc:creator><author>CrazyDev</author><pubDate>Mon, 19 Apr 2010 16:04:00 GMT</pubDate><guid>http://www.cppblog.com/CrazyDev/articles/113034.html</guid><wfw:comment>http://www.cppblog.com/CrazyDev/comments/113034.html</wfw:comment><comments>http://www.cppblog.com/CrazyDev/articles/113034.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/CrazyDev/comments/commentRss/113034.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/CrazyDev/services/trackbacks/113034.html</trackback:ping><description><![CDATA[<p>Gamebryo的材质系统 <br><br>一个模型（有一大堆顶点跟索引数据组成）描画的方式，跟材质有很大的关系。<br>Gamebryo提供了一个很强大的材质系统。<br><br>首先gamebryo使用了一个自定义的Pipeline，这个也是在之前的文章中介绍过的。其实这个Pipeline就是大家最常用的一些Shader。GB帮我们总结出来了，并做成了一个标准的材质。这在GB里叫StandardMaterial。<br><br>标准材质跟Pipeline是相对应的。但是标准材质的实现是非常困难的，可以查阅NiStandartMaterial，大约有5000多行代码。GB会首先查找一下Shader文件夹下的那些Shader。<br>这些Shader的文件名是由一对数字+字母组成的。这些文件都是不重复的，因为GB内部通过Hash码得出这些值。如果在Shader文件夹下没有，那么GB会把当前的渲染方式记录到这个Shader中去，作为缓存。<br><br>当然你也可以构建自己的Material系统，比如GB的NiCommonMaterial里也给出了一些构建自己材质系统的例子，不过这是非常复杂，基本思想都是需要维护一个Shader树。<br><br>不过自己写Shader是非常方便的，你可以用RenderMonkey或者ShaderFX，把做好的*.fx文件放到Shader文件夹中，MAX再次打开的时候就会找到这些Shader。让美工使用起来非常的方便。<br></p>
关于优化的问题总结 <br><br>引擎本身做的很好，不会对速度产生太大影响。但是使用者往往会由于开发经验过低等等因素，导致游戏运行速度太慢。这里有些是因为对Gamebryo系统的不熟悉造成。<br><br><span style="COLOR: red">美术上：</span>美术如果不熟悉Art文档，不熟悉图形技术的话，用任何引擎都是白搭。其实大部分的浪费都是美术造成的。下面先说说美术的优化<br>1.注意Mesh的颗粒度，什么意思呢。就是主要不要有太多小物件。三角面很少的Mesh，如果材质相同的话，完全可以合并起来。<br><br><br>1.关于Gamebryo中的update与update select。这是avobject里重要的两个函数，也是Gamebryo中更新场景图最重要的函数之一。update select是做了优化的，也就是说，如果变换矩阵没有做出变换的话，gamebryo不会更新world信息，这样会节省一定的时间，所以如果是静态物体，请优先使用UpdateSelect.<br>相反动态物体，比如人物。如果确定他一定不是静态的话，请不要使用UpdateSelect，因为这会多做一次对变换矩阵是否改变的判断，这是没有意义的。<br><br>
<h1 class=ContentTitle><strong>gamebryo中的管线 </strong></h1>
跟开源图形引擎一样，商业引擎的价值在于帮助你做了很多前人的积累。<br>gamebryo就是这样的引擎，当然unreal做的更加牛。这是后话了，关于unreal的做法我将以后分析。<br><br>先来说说gamebryo，跟Ogre不一样的是，gamebryo有一个自己所谓的pipeline。<br>Ogre很灵活，当然灵活的代价是很多东西需要你自己做，gamebryo也很灵活，不过作为商业引擎，他同时帮你做了很多事情。<br><br><br><img src="http://blog.guibian.com/attachments/month_0903/g2009310112240.jpg"><br><br>
<h1 class=ContentTitle><strong>Gamebryo中的对象系统之一：智能指针和引用计数 </strong></h1>
本文是一系列Gamebryo底层系统的介绍。<br>Gamebryo除了是一个强大的游戏引擎之外，在研究他的源代码同时，我惊喜的发现他提供了很多值得我们在写代码过程中学习和借鉴的东西。<br>本文就是来介绍这些底层系统到底是在穿插在引擎中使用的。<br>Gamebryo的底层系统运用于他所有的模块和工具中，他提供了对象管理，引用计数，对象持久化（有点像Java的东西）以及快速的运行时型别转换等功能。<br><br><br><br>首先我们来介绍一下引用计数与智能指针。<br><br>在Gamebryo中所有基础NiRefObject的Object都会被计数的。这就意味着Object存储着所有引用他的数目。如果需要长期的引用一个对象的话，需要声明一个引用，并在结束时释放掉改引用。当引用计数等于零的时候，对象会自己释放掉所申请的内存区。不过这样看起来用起来会比较烦，好在GB提供了&#8220;smart pointers&#8221;来减轻繁杂的工作。<br><br>要注意的是所有继承NiRefObject的子类都必须通过堆来分配，不用使用于静态类型对象或者栈上对象。因为当引用计数为零的时候，该对象会被删除，而删除静态对象或者栈上对象都会导致内存错误或者崩溃。 <br><br>谈谈批量渲染&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;随着显卡寄存器数量越来越多,批量渲染已经不是什么稀奇的事情了.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其实,根据我最近研究发现,暴雪早在DX8时代就做了批量渲染这件事,所以一举占据了RTS老大的地位.很凑巧的时候我现在的项目也是个RTS类游戏.所以批量渲染就成了一个亟待解决的问题.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gamebryo是支持MeshInstancing的，就是模型的批量渲染。<br>&nbsp;&nbsp;&nbsp;&nbsp;哦，先介绍下DX9支持的几种Instancing的方式吧，一种叫HardwareInstancing，中文叫硬件批量渲染吧，其实是DX9提供了SetStreamSourceFreq这个接口，让你可以把一个数据源多次使用，比如我们要批量需然一组模型，每个模型有自己的Translation信息，但是他们的顶点和索引数据是共用的。例如，你要批量10个这样的模型的话，只要抽取出他们不同的数据上传到一个数据源中，然后顶点和索引数据重复使用十次就可以了。<br>&nbsp;&nbsp;&nbsp;&nbsp;另外一种是ShaderInstancing，所谓Shader Instancing，意思就是，把Instancing数据传到vertex shader里去，在渲染的时候通过一些方法索引到这些Instancing数据，用来对顶点数据做不同的描画。<br><br>Gamebryo帧渲染系统详解 <br><br>如果没记错的话，我曾经写过一篇关于Gamebryo帧渲染系统的内容。估计当时剖析的不是太详细。那么现在我在这里重新讲一下帧渲染系统，希望能把他将清楚。由于我手头没有代码，而且又是商业引擎，所以很多函数我并不是完整的使用gamebryo中的，能使用伪代码的地方我尽量使用伪代码。<br><br>好，首先再次强调下帧渲染系统是逻辑系统，严格上跟渲染没有任何关系，就是说如果你可以绕开帧渲染系统，一样可以画出想描画的东西，只不过GB这样做的目的是使得渲染层次更加清晰，灵活了。<br>OGRE也有类似的概念，在Ogre中也可以定义自己的层，但是由于没有帧渲染系统，所以层次上不如Gamebryo灵活，方便易用。<br><br>RenderFrame 和RenderStep我就不再赘述了，因为这两个概念很简单，里面也没有实质性的内容。你想怎么理解都可以，前者是后者的超集，Step又是Click的超集。<br><br>详细说一下RenderClick和RenderView。RenderView可以理解成我们所要描画的物件。RenderView里有AppendScene这样的接口，就可以把所有想View的东西都挂接上去。RenderView里还有个重要的工具叫做Culler，Culler是做什么用的呢？是负责裁剪的，这里的裁剪是逻辑上的裁剪，就是精确到几何体级别的裁剪。（注意不是三角面级别的）。Culler是作为Processor被加进去的，就是一个裁剪的过程。Culler提供了一些抽象接口，来满足用户的自定义裁剪。就是说你可以根据你的需要来在渲染前进行裁剪。<br><br>下面说到RenderClick，RenderClick精确的字面意思就是一次描画，这个类的功能也基本是这样，知道了要画的东西，但是要画到什么上去就需要RenderClick了，每个RenderClick对应了一个RenderTarget。就是要描画的地方。一个描画的过程是这样的，首先找到RenderTarget就是要描画的地方，因为如果是后期特效，有时候会有多个RenderTarget。<br>知道了要描画的地方后就开始描画了，首先从RenderView里找出要描画的几何体（这里的几何体已经是被裁剪过后的几何体了），然后Click里再做一个处理，这里的处理也是个Processor，就是意味着用户可以定义。这里处理的目的基本上是对物体做一个排序。这里不会再对几何体进行裁剪了，而是进行类似Alpha排序等这样的工作，保证物体被正确的按层次画出来。最终得到的RenderObjList就是用来描画的了。<br><br>整个过程其实还是比较简单的，给用户很多自己选择的机会。是一个不错的设计。 <br><br>GameBryo ---- 模板类<br>
<p>GameBryo提供了一太基本的模板容器类，这些容器在整个库内使用。</p>
<p><font size=4><strong>Lists</strong></font></p>
<p><font size=3>NiTPointerList对象可以包含和管理指针，智能指针，以及其他任何大小小于等于指针的元素，该链表可以有效的插入和删除所有元素，以及正向遍历和反向遍历所有元素，同样可以通过给定值查找元素的实体和所在位置，NiTPointerList的元素的内存是从一个共享内存中分配的，从而提高类的执行速度和内存效率，如果链表元素大于指针，程序可以使用NiTObjectList.</font></p>
<p><font size=4><strong>Array</strong></font></p>
<p><font size=3>NiTArray对象实现勒几乎可以包含所有对象的动态数组，该数组可以缩放，并且可以压缩(通过转移元素来移除空空间)。内置类型(char*, float, int等)使用NiTPrimitiveArray。NiMemObject派生出的类型使用NiTobjectArray。注意NiTArray的元素上限为65535；如果大于该限制，使用NiTLargeArray派生出的类，比如NiTLargePrimitiveArray或NiTLargeObjectArray。</font></p>
<p><font size=4><strong>Map</strong></font></p>
<p><font size=3>NiTPointMap对象实现勒哈希表的功能，允许任何类型的键值来影射到指针，智能指针，以及其他任何大小小于等于指针的元素，并能快速的储存和查找键值对，不过不能使用字符串键哈希表，而NiTStringPointerMap对象是专为此设计的，NiTPointMap和NiTStringPointerMap的元素内存也是从一个共享内存中分配的，从而提高执行速度和内存效率，如果map元素大于指针，程序可以使用NiTMap和NiTStringMap</font></p>
<p><strong><font size=4>StringMap</font></strong></p>
<p><font size=3>NiTStringMap和NiTStringPointerMap对象的函数和NiTMap和NiTPointerMap风格类似，但是允许字符串作为键，并且通过字符串比较来进行键散列</font></p>
<p><strong><font size=4>FixedStringMap</font></strong></p>
<p><font size=3>NiTFixedStringMap对象函数和NiTMap对象风格相似，但是允许NiFixedString对象作为键</font></p>
<p><strong><font size=4>Queue</font></strong></p>
<p><font size=3>NiTQueue实现勒基本所有类型对象的先进先出队列，但不提供智能指针，需要注意链表可以当做队列来使用</font></p>
<p><strong><font size=4>Set</font></strong></p>
<p><font size=3>NiTSet实现了基本所有类型的无序集合，也没提供智能指针，内置类型(char*, float, int等)使用NiTPrimitiveSet。NiMemObject派生出的类型使用NiTobjectSet，智能指针则使用NiTObject或者NiTPrimitivePtrSet，这将正确的处理引用计数。</font></p>
<p><font size=4><strong>Pool</strong></font></p>
<p><font size=3>NiTPool实现了小型对象的池，使得程序能通过一个池来分配小型对象，并能重复使用，而不是单独的去分配和释放一个小型对象</font></p>
<br><br>GameBryo ---- 场景图的几何更新<br><br>
<p>一个程序会在需要的时候改变一个节点的转换，计算该节点的时间转换，以及该节点子节点的其他转换将被延迟，直到应用程序调用例行的update。</p>
<p>update是高效使用深度优先来遍历子图计算世界转换和世界包围球，从而最大限度的减少节点的访问，当向下递归时，转换被更新，包括所有的自节点，当矩阵更新后，世界包围球通过递归调用返回</p>
<p>总之，转换在递归中被更新，而包围球在递归返回时得到</p>
<p>通常大多数的对象都不会移动的，所有只更新只限于小部分可以移动的对象。在场景图的数据处理初始化中，当应用程序使用到场景图前，至少要对场景图的根节点进行一次例行update，这样保证所有节点的世界信息及本地信息都是最新的。</p>
<p>在应用程序帧到帧的运行中，当符合下面任何一条时，应用程序必须调用对象&#8220;O&#8221;的update。</p>
<p>&#183;O被绑定到父亲节点或者从父亲节点解除绑定</p>
<p>&#183;O绑定了一个新子节点或者解除了一个子节点的绑定</p>
<p>&#183;O任何一个转换被改变</p>
<p>注意一下，调用当前发父亲节点或者任何祖先节点的update可以代替当前节点update的调用。例如，如果对象A绑定了子节点B和C，只需要调用A的update就够了。没有必要调用三个对象的update，应用程序会以批处理的方式更新。</p>
<p>例如，当应承需要改变了一个活动角色的所有的关节矩阵，他应该推迟update直到所有的改变都完成，并且只需要调用一次角色根节点的update。</p>
<p>但是，注意update尽量在场景图的更下层调用，如果一个场景图每帧只有一个叶子被改变，那么调用根节点的update就太过分了，这将降低性能。</p>
<br>GameBryo ---- 网格数据共享<br><br>
<p><font size=3>为了世界中对象只需要少量的顶点，一棵树可以用来代表场景图，每个对象被单独表现为树中的节点。但是，在绝大多数场景图中，会多次出现一个需要大量内存来存储的复杂对象，绝大多数的内存是消耗在纹理和顶点数据上，比如纹理坐标和法线。</font></p>
<p><font size=3>如果一个应用程序需要多份这样一个对象，是有可能通过共享的NiDataStream来分享模型空间的几何信息，颜色，纹理和其他颜色。换句话说，若干网格对象可能分享NiDataStream对象，在这种情况下，场景图是有向非循环图，而不是一棵树。</font></p>
<p><font size=3>在几何数据共享的情况下，叶网格对象共享NiDataStream对象的模型空间网格。但是,两个模型数据的实例是在世界的不同位置,因为他们代表的多个网格对象,并且每个副本自己单独的转换。</font></p>
<p><font size=3>这些管理是在应用程序的内部透明处理的——应用程序只需要建立两个网格对象使用同一个NiDataStream对象。网格对象甚至要比一套最小的网格数据小的多，因为网格对象不像数据流，不存在每个顶点的数据</font></p>
<p><font size=3>下面的图象是一个典型的情况：</font></p>
<p>&#160;</p>
<div forimg="1"><img class=blogimg border=0 src="http://hiphotos.baidu.com/%BA%DA%B5%C4%B7%A2%D7%CF/pic/item/741a0302a5cfd7294afb5115.jpg" small="0"></div>
<p>&#160;</p>
<p>&#160;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (两个网格对象使用同一个NiDataStream)</p>
<p><font size=3>两个叶网格对象分享一个轮胎NiDataStream对象的顶点位置和法线。一个网格对象对应到自行车的前轮，另一个对应到自行车的后轮。NiDataStream对象自身存储的网格顶点的位置和法线被共享，两个网格对象都保存表现自己网格的转换。</font></p>
<p><font size=3>注意以下，Gamebryo不为任何类型的NiAVObject提供多父亲的功能。绑定一个已经拥有父节点的对象C，会导致C自动脱离原来的父节点</font></p>
<br>GameBryo ---- Working with Properties<br><br>
<p><font size=3>Gamebryo通过一套相互独立的渲染性质为每个能渲染的叶子对象定义了渲染属性，每一个渲染属性都为能渲染的对象定义了某一方面的渲染状态，并且都是NiProperty的子类。对个可渲染的对象可以共享渲染属性。针对一个对象的完整渲染状态是所有属性的完整组合。当属性状态对象存在与NiRenderObject叶子节点时，这个个体渲染属性就被绑定在场景图的任何NiAVObject上。这正式用于每个可渲染叶子对象产生属性状态的每个NiAVObject的属性。一个属性被绑定到一个NiAVObject将影响所有子数上的子对象(包括它自己)，除非在子树中同样的属性类型被其他属性所替代。如果没有任何属性被设置到场景图的对象上，该对象会通过合适的默认属性来绘制。每个属性类型的默认相当于为该类型设置一个默认构造，每个NiAVObject都包含了一个绑定与它的所有属性的链表，一个NiAVObject可能没有任何属性绑定，也有可能绑定一个或多个属性。所有的方式达到一个NiAVObject能绑定的每样属性的最大值。注意确保应用程序任何时候都不能给单一的NiAVObject绑定一个以上已经类型的属性。一个单一的NiAVObject绑定已经类型的一个以上的属性会导致奇怪的视觉效果和未知的问题。</font></p>
<p><strong><font size=5>绘制属性类型</font></strong></p>
<p><font size=3>NiProperty对象在Gamebryo中的数据层次如下：</font></p>
<p><font size=3>NiObject</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiAlphaProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiDitherProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiFogProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiMaterialProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiShadeProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiSpecularProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiStencilProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiTexturingProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiVertwxColorProperty</font>就如上面讨论的，属性设置从根到叶层次。一个被绑定到NiAVObject对象的属性会影响该对象及它的子对象。除非在更低的子树中绑定该类型的其他属性。因此，一个可渲染叶节点的当前状态是由场景图中它祖先的链所决定的</p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiWireFramProperty</font></p>
<p><font size=3>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiZBufferProperty</font></p>
<p><strong><font size=5>更新属性到集合物体上</font></strong></p>
<p>&#160;</p>
<p>Gamebryo会让整个场景图保持绑定属性，绑定在每个可渲染叶对象的属性状态是包含所有提供的类型的属性的数组，这样，每个可渲染的对象包含一个直接指向用来绘制的渲染属性的指针。这很重要，因为渲染只涉及可渲染的叶对象，而不是整个场景图。于是每个可渲染对象的属性状态都是继承其他可渲染对象的所有属性的副本。</p>
<p>Gamebryo使用一个系统类似使用NiAVObject::Update函数来更新这些属性状态对象。这个类似的渲染属性函数是NiAVObject::UpdateProperties。当出现下面的情况UpdateProperties必须在obejct"O"或任何一个他的祖先调用下一次渲染</p>
<p>&#183;一个以O个根节点的树刚被创建</p>
<p>&#183;一个属性被绑定到O或从O移除</p>
<p>&#183;O被绑定到节点P或从P上被移除</p>
<p>注意，当只改变了一个已有属性，应用程序不需要调用UpdateProperties。</p>
<p>要实现最佳性能，这些UpdateProperties的调用可以以相同的方式进行批处理来执行批处理更新,如果应用程序将在子树上绑定或解除绑定许多属性，它必须调用所有的绑定或解除绑定函数，然后在子树的根部调用一次UpdateProperties，通常的，因为属性和子节点的绑定和解绑没有每一帧这样频繁，所以UpdateProperties要比每帧积累属性快的多，但是对于程序员。将多出一个额外的小负担。</p>
<br>GameBryo ---- SoftParticles<br><br>
<p>软粒子主要是为了解决粒子广告牌和场景几何相交时，产生的生硬边缘，如下图烟雾与地面相交时的边<img class=blogimg border=0 src="http://hiphotos.baidu.com/%BA%DA%B5%C4%B7%A2%D7%CF/pic/item/b117d366e9752f0faa184c1c.jpg" small="0"></p>
<p>为了解决上面的情况，我们需要用到场景的深度信息，如下图：</p>
<p><img class=blogimg border=0 src="http://hiphotos.baidu.com/%BA%DA%B5%C4%B7%A2%D7%CF/pic/item/8561271ea6b9db344034171b.jpg" small="0"></p>
<p>在一般的渲染管线中，点P3就是产生生硬边的点，为了改善这种情况，SoftParticles通过改变粒子的alpha值来处理粒子后面的场景，这里使用了自定义的shader常量来决定距离d以便我们调整alpha值（d为world place中），任何距离原场景深度大于d的粒子相素我们将不处理他的alpha值(对应上图P1的公式)，</p>
<p>上图中点P1正好达到该距离，点P1到P3的alpha混合程度会递增，距离d设置的越小，那效果就越接近于硬粒子的效果，因为P1的条件很容易满足，对alpha值的修改会减少，但是如果距离设置的过大，那P2就很容易满足，这样导致Len/d产生的值很小，让粒子变的很透明，造成的粒子很稀疏，具体的效果要自己手动调节。</p>
<p>这种边缘软化的方式只是近似的，当场景的法线于摄像机方向锤子时会失效，当出现这种情况时，随便粒子与相交面很接近了，但因为摄像机与相交面近乎垂直，而粒子相素的深度检测是沿与摄像机方向的，从而产生一个很大len值，导致了本来应该成为P3效果的点，成为P1。</p>
<p>DEMO5个类，<span class=NDLSystemFont>SoftParticles，<span class=NDLSystemFont>MRT_ColorDepthMaterial，<span class=NDLSystemFOnt>SoftParticlesMaterial，<span class=NDLSystemFont>SoftParticlesManager，MRT_ColorBlackMaterial</span></span></span></span></p>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>SoftParticles::CreateScene()</span></span></span></span></p>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>负责创建场景，从Nif文件中获取场景，摄像机及粒子系统，设置alpha排序，剔除，及默认材质MRT_ColorDepthMaterial, MRT_ColorDepthMaterial继承于NiStandardMaterial，重载了函数bool HandleFinalVertexOutputs()和函数bool HandleFinalPixelOutputs()，这两个函数分别在vertex shader和pixel shader的最后执行，通过HandleFinalVertexOutputs函数，为vertex shader的output结构增加成员NiMaterialResource* pkVertOutViewTexCoord = Context.m_spOutputs-&gt;AddInputResource("float4", "TexCoord", "World", <br>"PosViewPassThrough");作为纹理坐标的格式输出</span></span></span></span></p>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>NiMaterialNode* pkSplitterNode = GetAttachableNodeFromLibrary(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "PositionToDepthNormal");<br>&nbsp;&nbsp;&nbsp; kContext.m_spConfigurator-&gt;AddNode(pkSplitterNode);<br>&nbsp;&nbsp;&nbsp; kContext.m_spConfigurator-&gt;AddBinding(pkViewPos,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pkSplitterNode-&gt;GetInputResourceByVariableName("Input"));<br>&nbsp;&nbsp;&nbsp; kContext.m_spConfigurator-&gt;AddBinding(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pkSplitterNode-&gt;GetOutputResourceByVariableName("Output"),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pkVertOutViewTexCoord);</span></span></span></span></p>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>获得自定义函数PositionToDepthNormal，将pkViewPos作为传入参数，把新增的pkVertOutViewTexCoord作为输出参数</span></span></span></span></p>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>而HandleFinalPixelOutputs则在输入结构中增加float4 WorldPosProjected : TEXCOORD6;代码如下</span></span></span></span></p>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>NiMaterialNode * pkInputResource;<br>&nbsp;&nbsp;&nbsp; pkInputResource = kContext.m_spConfigurator-&gt;GetNodeByName("PixelIn");</span></span></span></span></p>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>&nbsp;&nbsp;&nbsp; NiMaterialResource* pkDepthFromVP = pkInputResource-&gt;AddOutputResource(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "float4", "TexCoord", "World", "WorldPosProjected");</span></span></span></span></p>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>然后在输出结构中增加深度颜色，通过WorldPosProjected来赋值，CreateScene()还将场景中所有的粒子系统添加进<span class=NDLSystemFont>SoftParticlesManager，添加完所有的ParticlesSystem后，<span class=NDLSystemFont>SoftParticlesManager通过Initialize()函数，创建默认的粒子材质，以及<span class=NDLSystemFont>SoftParticlesManager自己的RnderView及click,然后通过InitializeScene()为每个粒子系统设置材质，并添加进RnderView,这里要注意执行顺序</span></span></span></span></span></span></span></p>
<span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont>SoftParticles::CreateFrame()</span></span></span></span>
<p><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFOnt><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFont><span class=NDLSystemFont>该函数中，通过获取后备缓冲的属性，来创建相同一张texture，用来渲染深度，不过格式要设置为NiTexture::FormatPrefs::SINGLE_COLOR_32，表示32位的单通道颜色，用R通道表示，然后新建一个RenderGroup，要包含原来的后备缓冲以及先建的位图缓冲，让场景同时渲染到这两个缓冲上，并将纹理缓冲作为<span class=NDLSystemFont>SoftParticlesManager中创建的click的一个输入</span></span></span></span></span></span></span></span></p>
<br>GameBryo ---- MSAA<br>
<p>MSAA(MultiSampling Anti-Aliasing)</p>
<p>GB中MSAA实现的关键代码基本是直接用的D3D，除了自己的渲染批次系统，DX9不能直接对渲染到纹理启用MSAA，但是提供了渲染表面surface,可以对surface启用MSAA。</p>
<p>实现的关键步骤大致如下</p>
<p>1。NiRenderedTexture::Create创建一个和背景缓冲一样大的RenderTexture，NiRenderTargetGroup::Create利用RenderTexture创建RenderTarget，屏幕最终渲染的目标就是该RenderTarget，我们需要的也是将MSAA后的数据给RenderTexture</p>
<p>2。通过D3D的CreateRenderTarget按照自定的MSAA级别来创建surface，利用surface的buffer创建另外一个MSAARenderTarget</p>
<p>3。新建一个RenderView，GB中用的NiScreenFillingRenderView，就是D3D中四个顶点组成的矩形，该RenderView绑定一个baseTexture为步骤1所创建的RenderTexture的NiTexturingProperty，创建RenderClick挂接该RenderView，并设置一个CallBackFunc，然后将该click插到mainClick后面，这样用两pass来完成MSAA</p>
<p>4。主click将画面渲染到开启MSAA的MSAARenderTarget，然后进入新click的CallBackFunc，获取MSAARenderTarget的buffer，用D3D的StretchRect复制数据到RenderTexture，这样新click就会渲染出进行MSAA后的texture</p>
<img src ="http://www.cppblog.com/CrazyDev/aggbug/113034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/CrazyDev/" target="_blank">CrazyDev</a> 2010-04-20 00:04 <a href="http://www.cppblog.com/CrazyDev/articles/113034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GameByro渲染系统剖析（转）</title><link>http://www.cppblog.com/CrazyDev/articles/113020.html</link><dc:creator>CrazyDev</dc:creator><author>CrazyDev</author><pubDate>Mon, 19 Apr 2010 14:37:00 GMT</pubDate><guid>http://www.cppblog.com/CrazyDev/articles/113020.html</guid><wfw:comment>http://www.cppblog.com/CrazyDev/comments/113020.html</wfw:comment><comments>http://www.cppblog.com/CrazyDev/articles/113020.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/CrazyDev/comments/commentRss/113020.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/CrazyDev/services/trackbacks/113020.html</trackback:ping><description><![CDATA[<div id=blog_text class=cnt>链接：<a href="http://www.cppblog.com/rise-worlds/archive/2009/04/22/80688.html">http://www.cppblog.com/rise-worlds/archive/2009/04/22/80688.html</a>
<h2></h2>
<h3>引言：</h3>
<p>GameByro作为一款次世代引擎，使用了复杂的材质系统，用来满足各种各样的需求。材质代表了物体受到光照后所呈现出的质感，而这种质感在计算机图形学中需要着色代码来完成，所以当前流行的图形引擎设计是使用被渲染对象的材质与shader相关联，GameByro也不例外。GameByro的材质系统可以通过shade tree生成shader程序，增强了应用程序层对可编程渲染管线的控制能力。</p>
<h4>渲染架构概览：</h4>
<p>在GameByro中，对象表面的色彩、纹理、光滑度、透明度、反射率、折射率、发光度等可视属性与传统的材质系统分离，独立的成为了对象的渲染属性（NiProperty），而材质（NiMaterial）仅用来对着色程序的封装，这样就实现了渲染数据和渲染方法的分离，降低了耦合性。如上所说的这些可视属性在Gamebyro中会封装成一个属性对象，在应用程序中如果对对象挂载这个属性对象，在GPU程序中就可以访问这个属性对象的值。渲染属性对象可以在创建时指定其类型，如纹理、浮点、矩阵、向量或数组，此外一些全局性的对象也可以通过在Shader中用语意声明为全局object对象，如灯光和摄影机等，这样就可以以同样的方式来访问这些对象上的属性。</p>
<p>GameByro每一帧的渲染（NiRenderFrame）划分为多个步骤（NiRenderStep），每个步骤又包含很多个批（NiRenderClick），NiRenderFrame封装了上层对渲染系统调用的接口，而NiRenderClick则代表了图形硬件的一次绘制操作（对渲染队列中所有的对象的顶点缓存调用DrawPrimitive），当应用程序调用NiRenderFrame的Display接口时, NiRenderFrame会依次调用每一个NiRenderStep的Render()接口，NiRenderStep就会执行所有的NiRenderClick操作。</p>
<p>对于每个NiRenderClick来说，首先要设置视口和渲染目标，也就是渲染数据流的入口和出口。视口建立以后就可以通过关联的摄影机对场景图中的对象进行裁剪（默认的有视口裁剪和遮挡裁剪，此外还可以通过回调函数加入自己的裁剪方式），将未被裁剪的对象放入渲染队列。然后Gambyro会根据材质来对渲染队列中的对象进行排序，让材质相同的对象处于相邻位置，这样可以减少切换shader的开销。</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image002_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image002 border=0 alt=clip_image002 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image002_thumb.jpg" width=244 height=105></a></p>
<p>如图所示为帧渲染系统的结构图（简化版）</p>
<h4>材质系统：</h4>
<p>GameByro中的材质代表渲染对象所采用的方法。前面说过。纹理属性包含了着色所需的原料，那么材质就指定了对这些原料的加工方法。基于当前可编程渲染管线设计，材质就成为连接对象与GPU程序的中间层，应用程序可以通过材质将shader应用于几何体。</p>
<p>NiMaterial类是所有材质的基类，这个类通过一个Map来保存当前应用程序中所有NiMaterial的指针，当然这个Map是静态也就是说相当于全局变量，通过static NiMaterial* NiMaterial:: GetMaterial(const NiFixedString&amp; kName)接口对这个全局的Map进行访问。也就是说，当前环境中所有的NiMaterial对象是通过NiMaterial类来管理的。此外NiMaterial类还通过静态成员变量保存了一个工作路径（即shader文件路径），通过这个路径加载shader文件。NiMaterial就像是一个中介，全权代理对对象的渲染工作。用户可以通过重载NiMaterial来实现自己的渲染机制。每个NiMaterial都是全局性的，可以作用于多个甚至是所有的渲染对象，但一个渲染对象也可以拥有多个NiMaterial，但只能有一个处于激活状态的NiMaterial。</p>
<p>NiMaterial的派生类NiFragmentMaterial提供了对可编程渲染管线完整的控制机制，内部保存了NiShader的哈希表、一个NiGPUProgramCache数组。并且NiFragmentMaterial会生成一个用来编译GPU程序的shade tree（后面会有解释）。这样的话，每个NiFragmentMaterial可以对应多个shader程序，这样就提供了一种机制，在运行时根据不同的运行环境和渲染对象不同的状态，来选择合适的shader程序。在NiRenderClick依次渲染可见集中的每个对象时，首先会判断其是否需要被渲染的标记（flag），如果需要被渲染，则使用NiMaterial::IsShaderCurrent接口判断当前shader（上一次渲染所使用的shader）是否有效，所谓有效就是仍然存在并且可以应用于本次的渲染对象，如果无效，则会调用NiMaterial::GetCurrentShader获得shader，用于本次渲染。NiMaterial::GetCurrentShader会根据渲染对象的属性和当前环境硬件条件来选择合适的shader程序。当然，可以通过重载IsShaderCurrent和GetCurrentShader接口来指定自己的有效性判断规则和如何选择shader程序的方案。 NiFragmentMaterial提供了一套搭建shade tree的框架，用户可以通过重载来搭建自己的shade tree，当然，如果不想通过shade tree的形式生成shader程序也可以，使用NiSingleShaderMaterial可以从文件生成shader程序。</p>
<p><strong>Shade Tree</strong><strong>：</strong></p>
<p>什么是shade tree呢？我们通常编写的shader代码是线性执行的，即每个pass流程执行的是文本上定义好的shader流程，每一段shader功能模块是按一定顺序依次执行的。如果需要修改流程中的某一部分就需要更改相关的shader代码并重新编译。而shade tree将shader代码以树形结构组织起来，每一个shader代码块（一般是一个函数）都会被编译成一个节点，通过定义输入变量和输出变量来提供数据流的入口和出口，这些节点的插入和删除可以通过应用程序来控制，从而灵活的控制整个渲染过程。这样shader程序中的一些核心模块可以由美术通过工具生成，然后插入到shade tree中，只要输入和输出的接口不变，就无须修改其他代码，从而降低了美术开发shader的门槛。</p>
<p>GameByro通过以下几个类搭建shade tree:</p>
<p>l NiMaterialConfigurator：shade tree被封装在这个对象中，Uniform constants被封装在NiMaterialResource中，而NiMaterialNode封装了相关的shader代码，所有的资源和节点通过NiMaterialResourceBinding连接起来。当所有的连接都确立以后，NiMaterialConfigurator会调用Evaluate接口生成GPU程序和一个输入Uniform资源的集合。</p>
<p>l NiMaterialFragmentNodes：这个类包含了一个shader代码片段的集合，这些代码片段为不同的平台和编程语言所编写。这就为shader程序员提供了更大的灵活性，用以控制他们的代码在不同平台和图形硬件上的表现。例如：在高端平台可以采用高级的shader model提供更好的效果，而在低端平台上可以关闭一些特效来加快速度。</p>
<p>l NiMaterialNodeLibraries：这个类是一个NiMaterialNode的集合，其实也就是一个shader库。它允许shade tree节点完全基于数据驱动。shader库的生成可以通过两种方式，解析XML文件或用XML文件生成C++代码。GameByro提供了相关的解析器和代码生成器。</p>
<p>l NiMaterialResources：shade tree中的Uniform constants，支持多种数据类型，包括Constant、Predefined、Attribute、Global、Object。</p>
<p><strong>固定管线的渲染：</strong></p>
<p>GameByro支持固定管线的渲染，其纹理混合过程如下。</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image004_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image004 border=0 alt=clip_image004 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image004_thumb.jpg" width=166 height=244></a></p>
<p>固定管线的着色处理流程</p>
<p>上图很清楚的显示出了每个stage的操作，平行的表示两张纹理的采样是同时进行的，特定情况下右边的纹理可能被忽略。</p>
<p>大部分情况下，应用程序不会使用上面所有的stage，开启或者关闭那个stage可以由应用程序来指定。</p>
<p>以下为多重采样的原理图:</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image006_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image006 border=0 alt=clip_image006 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image006_thumb.jpg" width=244 height=238></a></p>
<p>固定管线的纹理多重采样</p>
<h5>缺省的着色处理流程：</h5>
<p>GameByro提供了一个默认的着色处理流程，封装在NiMaterial的派生类NiStandardMaterial中，这个类执行类似于固定管线的流程，在不同阶段将纹理采样、并将采样到的数据混合到最终的结果中去。</p>
<p>GameByro默认的材质系统的特性如下：</p>
<ul>
    <li>Skinned and unskinned transformations. Skinned transformations can support up to 30 bones per draw call.
    <li>Vertex colors
    <li>Base maps
    <li>Normal maps
    <li>Parallax maps
    <li>Dark maps
    <li>Detail maps
    <li>Bump environment maps
    <li>Gloss maps
    <li>Glow maps
    <li>Decal maps (up to 3)
    <li>Cubic and spherical environment maps
    <li>Point/Spot/Directional/Ambient lights contributing to the diffuse, specular, and ambient color. Up to 8 total lights. Per-pixel or per-vertex.
    <li>Projected light maps. Clipped or unclipped. (Up to 3)
    <li>Projected shadow maps. Clipped or unclipped. (Up to 3)
    <li>Texture transforms per map.
    <li>Per-vertex fog </li>
</ul>
<p>下图显示为不同的纹理、灯光、材质属性的组合过程，不过需要注意的是，视差贴图和凹凸贴图属于特殊的情况，它们仅仅影响到纹理采样的UV坐标，而并非直接对最后的颜色值产生贡献。视差贴图会改变所有贴图采样的UV坐标，而凹凸贴图仅对环境贴图的UV产生影响。</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image008_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image008 border=0 alt=clip_image008 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image008_thumb.jpg" width=236 height=244></a></p>
<p>以上流程完全由shade tree构建，NiStandardMaterial提供了大量的函数接口用于对每个流程的控制，用户可以通过重载相关的接口，插入自己的shade tree节点，修改每一步的操作或处理过程。例如：</p>
<p>virtual bool HandleBaseMap(Context&amp; kContext, NiMaterialResource* pkUVSet,</p>
<p>NiMaterialResource*&amp; pkDiffuseColorAccum,</p>
<p>NiMaterialResource*&amp; pkOpacity, bool bOpacityOnly);</p>
<p>当然，整个流程的顺序和结构修改起来比较困难，如果有需要可以定制自己的材质系统，搭建自己的shade tree。</p>
<p>NiStandardMaterial提供了若干回调函数，这些函数可以动态的修改流程，分割PASS，对shader运行失败进行容错。</p>
<p>l SplitPerPixelLights/SplitPerVertexLights:这两个函数分别作用于逐顶点光照和逐像素光 照，当物体所受的光源数量太多，超过了顶点或像素着色器的能力时，通过这些函数可以将失败的pass分割成两个，如果分割出的pass仍然不能执行，那么函数会被递归调用，直到每个pass只有一个光源为止。</p>
<p>l SplitTextureMaps：这个函数会把对纹理采样的pass进行分割，当纹理查询过多时，顶点或像素着色器就会过于复杂，这时就可能导致shader运行失败。此函数只能迭代一次，生成一个额外的pass。</p>
<p>l DropParallaxMap：这个函数用来从几何体上移除视差贴图，且不产生额外的pass。</p>
<p>l DropParallaxMapThenSplitLights：这个函数首先调用DropParallaxMap移除视差贴图，然后一直调用SplitPerPixelLights直到失败为止。</p>
<h5>NiMaterialInstance：</h5>
<p>NiMaterial并不是直接与NiRenderObject相关联，而是经过了NiMaterialIstance这个中间层，由它来代理将NiMaterial关联到几何体，它负责调用NiMaterial为NiRenderObject生成NiShader，通过改变NiMaterialIstance上的接口SetMaterialNeedsUpdate，可以决定每一帧NiRenderObject所使用的材质是否需要被更换，通过接口SetDefaultMaterialNeedsUpdateFlag，可以决定当前材质所需的数据（即当前渲染流程所需的数据）是否要被更新。这样每个NiMaterialIstance只能被一个NiRenderObject所拥有，而多个NiMaterialIstance可以共享一个NiMaterial，这样就减少了重复创建NiMaterial的时间和空间上的开销，同时降低了渲染对象个材质之间的耦合度。</p>
<p>如下为材质系统的类结构简化图：</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image010_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image010 border=0 alt=clip_image010 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image010_thumb.jpg" width=244 height=184></a></p>
<h4>渲染属性：</h4>
<p>前面提到过，GameByro将渲染所需要加工的数据全部封装在了NiProperty中，只要在shader中用指定的语法进行声明，就可以访问这些属性的值。</p>
<p>目前引擎中已经定义了12种属性，均派生自NiProperty，分别代表渲染数据的12种不同类型：</p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiAlphaProperty.htm"><u><font color=#0000ff>NiAlphaProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiDitherProperty.htm"><u><font color=#0000ff>NiDitherProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiFogProperty.htm"><u><font color=#0000ff>NiFogProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiMaterialProperty.htm"><u><font color=#0000ff>NiMaterialProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiRendererSpecificProperty.htm"><u><font color=#0000ff>NiRendererSpecificProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiShadeProperty.htm"><u><font color=#0000ff>NiShadeProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiSpecularProperty.htm"><u><font color=#0000ff>NiSpecularProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiStencilProperty.htm"><u><font color=#0000ff>NiStencilProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiTexturingProperty.htm"><u><font color=#0000ff>NiTexturingProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiVertexColorProperty.htm"><u><font color=#0000ff>NiVertexColorProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiWireframeProperty.htm"><u><font color=#0000ff>NiWireframeProperty</font></u></a></p>
<p>l <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Reference/CoreLibs/NiMain/NiMain_Class_Reference/NiZBufferProperty.htm"><u><font color=#0000ff>NiZBufferProperty</font></u></a></p>
<p>用户也可以自定义属性类型，但所对应的数据类型要被shader语言所支持。</p>
<h4>光照与阴影：</h4>
<p>光照与阴影密不可分，因为阴影就是由光照产生的，前面在材质系统中已经提到过光照对着色的影响，这里重点阐述，GameByro是怎样根据光源产生阴影的。由GameByro提供的阴影均基于ShadowMap技术，</p>
<p>但也提供了ShaowVolume的示例代码。</p>
<p>Shadowing System是完全建立在帧渲染系统上的， 通过一个RenderClick生成ShadowMap，然后在正常渲染流程开始之前将ShadowMap更新到可见集内每一个渲染对象上。这样当渲染对象使用NiStandardMaterial时就会根据光源的阴影技术来对ShadowMap进行采样，并将结果与最终的输出颜色按一定比例混合。</p>
<p>阴影系统由以下几个类构成：</p>
<ul>
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Write_Materials.htm"><u><font color=#0000ff>Shadow Write Materials</font></u></a></strong>:从NiFragmentMaterial派生，封装了生成 ShadowMap的算法和着色程序。 </li>
</ul>
<p>GameByro提供了三种类型的Shadow Write Materials，分别为NiPointShadowWriteMaterial、</p>
<p>NiDirectionalShadowWriteMaterial、NiSpotShadowWriteMaterial，适用于三种不同的光源类型。</p>
<ul>
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Technique.htm"><u><font color=#0000ff>Shadow Technique</font></u></a></strong>:这个类封装了阴影算法的细节，包括生成ShadowMap和使用ShadowMap投射阴影。
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Render_Click.htm"><u><font color=#0000ff>Shadow Render Click</font></u></a></strong>: 这个类是一个生成ShadowMap的批，这个类的对象是由shadow click generator负责生成。
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Click_Validator.htm"><u><font color=#0000ff>Shadow Click Validator</font></u></a></strong>: <a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Render_Click.htm"><u><font color=#0000ff>Shadow Render Click</font></u></a>:通过此类对象判断接受阴影的几何体对于shadow generator来说是否可见。
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Map.htm"><u><font color=#0000ff>Shadow Map</font></u></a></strong>: 每个ShadowMap对象包含一个作为阴影图的纹理，shadowmap对象由shadowManager直接管理，每个shadowmap对象都被一个shadow generator引用。
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Cube_Map.htm"><u><font color=#0000ff>Shadow Cube Map</font></u></a>:</strong> 同shadowmap作用相同，只是阴影图的纹理类型为CubeMap。主要用于点光源生成的全方向阴影。
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Generator.htm"><u><font color=#0000ff>Shadow Generator</font></u></a>:</strong> 阴影生成器，每个ShadowGenerator都对应一个NiDyamicEffect（NiLight的基类），也就是为这个NiDyamicEffect代表的光源生成阴影， 生成阴影所采用的技术由对象引用的ShadowTechnique来决定。
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Click_Generator.htm"><u><font color=#0000ff>Shadow Click Generator</font></u></a>:</strong> 生成ShadowMap的Shadow Render Click都由此类负责创建。这个类为每个ShadowGenerator指定ShadowMap，并负责每帧更新ShadowMap和ShadowMap所对应的变换矩阵。
    <li><strong><a href="mk:@MSITStore:D:%20桌面%20Gamebryo.chm::/Programmer_s_Guide/Shadowing_System/Architecture/Shadow_Manager.htm"><u><font color=#0000ff>Shadow Manager</font></u></a>:</strong>所有的ShadowMap、ShadowTechnique、ShadowGenerator、shadow render click对象都由ShadowManager统一管理，并负责使用一个shadow click generator 在每一帧生成一个shadow render click的列表。 </li>
</ul>
<p>阴影系统静态结构如下：</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image012_2.gif"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image012 border=0 alt=clip_image012 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image012_thumb.gif" width=220 height=240></a>整个阴影渲染的流程大致如下：</p>
<p>1. 在应用程序初始化阶段，通过调用NiShadowManager的Initialize()接口实现对整个阴影系统的初始化，此时应用程序会注册所有的ShadowTechnique，并初始化NiShadowClickGenerator。</p>
<p>2. 当我们创建一个NiLight以后，我们可以通过NiShadowManager为这个NiLight新建一个NiShadowGenerator，NiShadowGenerator会通过NiLight的类型来选择合适的NiShadowTechnique，此时NiShadowManager会为新的NiShadowGenerator创建一个NiShadowRenderClick。</p>
<p>3. 当帧渲染系统启动后，NiShadowRenderClick的PerformRendering()接口会被调用，此时NiShadowRenderClick会通过引用的NiGenerator获得阴影生成的着色程序和所需的数据（例如深度偏移），同时通过NiGenerator引用的Camera获得场景图中的可见集。下一步就是对可见集中的渲染对象添加ShadowWriteMaterial并设为激活状态，而ShadowWriteMaterial的类型是根据NiDyamicEffect的类型指定的。最后NiRenderClick就会启动渲染流水线，将可见集中的对象的深度全部渲染到ShadowMap中。</p>
<p>4. 在第一个RenderClick中生成了ShadowMap，下面就要使用这些ShadowMap投射阴影。在每一帧开始之前，用户还可以自己指定不接受阴影的节点，手动将其插入NiShadowGenerator：：m_kUnaffectedReceiverList中。在渲染BackBuffer的RenderClick中，首先会对场景图中的节点进行一次遍历，将不受阴影的节点放入NiShadowGenerator：： m_kUnaffectedCasterList的列表。在对每个节点进行渲染时，会遍历NiShadowManager中所有的NiShadowGenerator，判断这些NiShadowGenerator是否对这个节点有影响，判断的规则是此节点是否存在于UnaffectedReceiverList和UnaffectedCasterList这两个链表中，如果存在于任何一个链表，则节点不受此NiShadowGenerator影响，如果受此NiShadowGenerator影响，那么就将该NiShadowGenerator上的ShadowMap和数据更新到节点上的渲染属性中，NiStandardMaterial会根据这些数据选择对ShadowMap采样的方式，并将结果混合到最终的输出颜色中。</p>
<p>值得注意的是，点光源的shadowMap默认的是采用CubeMap实现，用户可以通过接口选择不使用CubeMap实现，当采用CubeMap实现时，光源无法产生软阴影。</p>
<h4>渲染系统的扩展：</h4>
<p>为了验证GameByro渲染系统的扩展性，笔者尝试着加入了一个后期处理特效Screen Space Ambient Occlusion（SSAO）,即屏幕空间的遮蔽，由于仅仅为了熟悉GameByro的渲染流程，所以笔者并未对SSAO算法做深究，仅仅用了自己简化的算法。</p>
<p>在渲染过程中先单独使用一个RanderClick将场景中的深度渲染到一张纹理上，然后在渲染到后台缓冲区的RenderClick中对深度纹理进行采样，执行SSAO算法，将结果混合到最终的结果中。采样点的偏移坐标是通过对一组随机向量进行归一化再乘以0~1之间的随机数而生成的，即长度为0~1之间的随机向量。</p>
<p>PS代码如下：</p>
<p>float VerticalRange:GLOBAL; //控制XY方向采样范围变量，可以在应用程序层对其进行调整</p>
<p>float HorizontalRange:GLOBAL; //控制在Z方向采样范围的变量</p>
<p>float calAO(float2 texCoord,float dw, float dh ) //通过当前像素所标和偏移量计算AO</p>
<p>{</p>
<p>float2 coord = float2(texCoord.x + dw, texCoord.y + dh);</p>
<p>float4 CenterPos = tex2D(DepthSampler,texCoord);</p>
<p>float4 CurPos = tex2D(DepthSampler,coord);</p>
<p>float depthDiff = clamp(CenterPos.z - CurPos.z,0,VerticalRange);</p>
<p>float ao = depthDiff/length(CurPos.xyz - CenterPos.xyz);</p>
<p>return ao;</p>
<p>}</p>
<p>// Pixel shader</p>
<p>float4 PS_SSAO(VS_OUTPUT In) : COLOR</p>
<p>{</p>
<p>float2 texCoord = In.BaseTex;</p>
<p>float depth = tex2D(DepthSampler,texCoord).z;</p>
<p>float ao = 0.0;</p>
<p>float scale = HorizontalRange/depth; //因为采样范围会受深度影响，故除以此系数。</p>
<p>for(int i=0; i&lt;32; ++i)</p>
<p>{</p>
<p>float2 offset = arrRandomPt[i].xy* scale;</p>
<p>ao += calAO(texCoord, offset.x, offset.y);</p>
<p>}</p>
<p>ao/=32;</p>
<p>float4 color = tex2D(BaseSampler,texCoord);</p>
<p>color.xyz *= (1.0 - ao);</p>
<p>return color;</p>
<p>}</p>
<p>最终实现效果如下：</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image014_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image014 border=0 alt=clip_image014 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image014_thumb.jpg" width=244 height=231></a><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image016_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image016 border=0 alt=clip_image016 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image016_thumb.jpg" width=238 height=244></a></p>
<p>SSAO生成的明暗图 无SSAO材质</p>
<p>以下两图上图为无SSAO效果，下图为开启SSAO后的效果。</p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image018_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image018 border=0 alt=clip_image018 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image018_thumb.jpg" width=244 height=226></a></p>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image020_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image020 border=0 alt=clip_image020 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_91A9/clip_image020_thumb.jpg" width=244 height=229></a></p>
<h4>总结：</h4>
<p>GameByro的帧渲染系统是比较灵活，想加入自己的渲染流程是比较容易的，此外由于RenderTarget和RenderView都可以由用户指定，所以想实现自己的shader效果不是很难。然而，NiStanderMaterial的shade tree比较复杂，总共高达6000行代码以上，过程非常复杂，这就是说，如果想实现自己的材质处理流程也要付出相当大的工作量，阴影系统虽然实现了较低的耦合度，但是实现过于复杂，不够简洁高效。</p>
<p>&nbsp;</p>
<p>作者：叶起涟漪</p>
</div>
<img src ="http://www.cppblog.com/CrazyDev/aggbug/113020.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/CrazyDev/" target="_blank">CrazyDev</a> 2010-04-19 22:37 <a href="http://www.cppblog.com/CrazyDev/articles/113020.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GameByro的NiCamera的使用（转）</title><link>http://www.cppblog.com/CrazyDev/articles/113019.html</link><dc:creator>CrazyDev</dc:creator><author>CrazyDev</author><pubDate>Mon, 19 Apr 2010 14:34:00 GMT</pubDate><guid>http://www.cppblog.com/CrazyDev/articles/113019.html</guid><wfw:comment>http://www.cppblog.com/CrazyDev/comments/113019.html</wfw:comment><comments>http://www.cppblog.com/CrazyDev/articles/113019.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/CrazyDev/comments/commentRss/113019.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/CrazyDev/services/trackbacks/113019.html</trackback:ping><description><![CDATA[<p>链接：<a href="http://www.cppblog.com/rise-worlds/archive/2009/07/22/90859.html">http://www.cppblog.com/rise-worlds/archive/2009/07/22/90859.html</a><br><br>1.&nbsp; 创建Camera.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1)通过 NiNew NiCamera的方式获得<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2)通过读取NIF文件,找到适合的Camera Node<br>2. 创建<u>NiRect</u>&lt;float&gt;对象, 设置Camera视口大小(假设对象为NiRect&lt;float&gt; kRetViewPost)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1)全屏幕 kRetViewPost = NiRect&lt;float&gt;(0.0f, 1.0f, 1.0f, 0.0f); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2)特定大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 假设当前窗口大小800*600, 我们要在大小为200*100, 相对坐标为 (cx = 300, cy = 100)的范围内显示Camera的内容, 那么视口大小为:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 计算视口比例: ratioLeft = cx / 800, ratioButon = 1.0 - (cy + 100) / 600; ratioRight = (cx + 200) / 800, ratioTop =&nbsp; 1.0 -&nbsp; cy / 600;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; kRetViewPost = NiRect&lt;float&gt;(ratioLeft&nbsp; ratioRight , ratioTop , ratioButon ); <br>3. 创建NiFrustum对象, 设置Camera截面体比例大小<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NiFrustum(float fLeft, float fRight, float fTop, float fBottom, float fNear, float fFar, bool bOrtho = false)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fLeft/fRight/fTop/fButtom的值是相对于显示区域的中心点和显示模式而定的. <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1) 显示模式:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a) 正交模式 (bOrtho = true)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 假如显示区域是200*100的大小, 那么fLeft = -(200/2)，fRight = 200/2, fTop = 100/2, fButtom = -100/2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b) 非正交模式 (bOrtho = false)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不管显示区域多大, fLeft/fButtom的值都在[-0.5, 0.0)之间,&nbsp; fRight/fTop 的值都在(0.0, 0.5]之间; 如: (-0.4, 0.4, 0.46, -0.46)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 正交与非正交的区别在于, 使用非正交模式时, Camera的映像大小会受到与聚焦物体间的距离影响(即可实现缩小放大的功能), 而正交模式则不会.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2) 显示比例<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在正交模式下通过调用AdjustAspectRatio来设定, 而在非正交模式通过fLeft(fRight)与fButtom(fTop)的比值来设定.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3) 裁剪距离(例如fNear = 1.0f, fFar = 1000.0f)<br>4. 通过NiCamera::SetViewPort(const NiRect&lt;float&gt;&amp;)和Camera::SetViewFrustum(const NiFrustum&amp; )分别设定ViewPort和Frustum</p>
<img src ="http://www.cppblog.com/CrazyDev/aggbug/113019.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/CrazyDev/" target="_blank">CrazyDev</a> 2010-04-19 22:34 <a href="http://www.cppblog.com/CrazyDev/articles/113019.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GameByro 特性分析（转）</title><link>http://www.cppblog.com/CrazyDev/articles/113017.html</link><dc:creator>CrazyDev</dc:creator><author>CrazyDev</author><pubDate>Mon, 19 Apr 2010 14:29:00 GMT</pubDate><guid>http://www.cppblog.com/CrazyDev/articles/113017.html</guid><wfw:comment>http://www.cppblog.com/CrazyDev/comments/113017.html</wfw:comment><comments>http://www.cppblog.com/CrazyDev/articles/113017.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/CrazyDev/comments/commentRss/113017.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/CrazyDev/services/trackbacks/113017.html</trackback:ping><description><![CDATA[<p>链接：<a href="http://www.cppblog.com/rise-worlds/archive/2009/04/22/80691.aspx">http://www.cppblog.com/rise-worlds/archive/2009/04/22/80691.aspx</a><br><strong><br>引言</strong>：
<p>GameByro是一款成熟的商业引擎，已经被许多成功的商业项目所使用，不仅包括文明4、上古卷轴4，辐射3等跨平台知名单机游戏，还有EA的大型3DMMORPG战锤online。GameByro完全基于面对对象设计，结构清晰，便于使用和扩展。灵活的可编程渲染架构使它比较容易支持最新的图形技术。另一方面，由于强调通用性，GameByro并没有对上层应用做太多的支持，其工具和插件大多仅具有一些通用功能，所以它更像是一个游戏开发套件而不是一个完整的FrameWork。
<p><strong>引擎特性概览：</strong>
<p>&#183; 跨平台（PC,XBOX360,PS3）
<p>&#183; 场景图表现为层次化的结构
<p>&#183; 同目前主要的建模工具集成（插件支持）
<p>&#183; 高效的可视性裁剪
<p>&#183; 在所有的平台上支持高级3D硬件加速
<p>&#183; 高级纹理和着色效果
<p>&#183; 动态碰撞检测
<p>&#183; 细节分级表现（LOD）
<p>&#183; 灵活的渲染，排序和裁剪算法
<p><strong>对象系统</strong>：
<p>GameByro的对象系统的使用遍及整个引擎，其主要实现如对象管理，引用计数，对象生存期等功能。可以通过对几个基类的继承来使用对象系。
<p>其主要特性主要有：
<p>1. 智能指针（引用计数）
<p>2. 运行时类识别（RTTI）
<p>3. 场景对象复制
<p>4. 序列化（对象文档化）
<p>5. 对象命名
<p>6. 对象扩展数据的管理
<p>7. 定时器（主要用于动画）
<p>8. 多线程操作
<p><strong>驱动层</strong>：
<p>GameByro对图形接口进行了封装，将图形API从引擎的渲染系统中剥离出来，在PC系统上，GameByro同时支持DX9和DX10两种接口。
<p><strong>DX9</strong><strong>渲染器特性</strong>：
<p>l GameByro不允许多个DX9Renderer对象同时存在，且不能与DX10Renderer共存。但在同一帧内，GameByro的DX9Renderer可以同时渲染多个窗口。
<p>l GameByro的DX9渲染器支持对资源的多线程访问，同时也支持对资源的预载，但是多线程的操作仍然受到严格的限制。
<p>l DX9Renderer 的固定管线蒙皮操作最少支持4块骨骼（矩阵），同时基于shader的蒙皮操作支持30块骨骼。
<p>l DX9Renderer通过对后台缓冲的多重采样支持全屏抗锯齿，范围在2倍到4倍之间。
<p>l 大多数硬件在DX9下对几何体的一个单一的piece所能接受的光照数量都有一定的限制（指渲染的一个批），如果超过这个限制，GameByro会根据距离或者是影响因子放弃一些光照。正因如此，在应用程序中，当8个以上的逐顶点光照作用于同一对象时，就会有一些被抛弃。
<p>l DX9Renderer提供了视口的左右反向功能，用于实现后视镜、立方体贴图表面绘制等功能。
<p>l DX9Renderer支持顶点雾，可以通过相关接口设置雾的最大值。
<p>l DX9Renderer完全支持DX的纹理格式。应用程序可以将自己的Direct3D textures提供给GameByro，引擎会自动将其封装到自己的材质对象中并允许添加属性和特效。通过提供自己的DX的纹理格式，GameByro能够正确的使用自并非天然支持的纹理格式。
<p>l 纹理相关特性和限制：DX9Renderer需要显卡支持在同一pass中至少使用两个纹理，否则渲染器则不会创建成功。此外受限于硬件的功能还有：
<p>1. Cube Mapping（立方体纹理）
<p>2. Specular Bump Mapping（凹凸镜面贴图）
<p>3. Diffuse Bump Mapping（凹凸反射贴图）
<p>4. Non-Power-of-Two Texture Size（编长非二整数幂的纹理）：使用这种纹理有如下限制：纹理寻址模式必须设置为clamp，Mipmapping不能使用。
<p>5. Texture Downsizing（纹理精度缩减）
<p>6. Texture Compression（压缩纹理），DX9Renderer支持载入经过压缩的纹理格式，并提供了纹理即时压缩的功能。如果硬件支持压缩纹理，DX9Renderer可以将经过压缩的纹理数据直接交给硬件处理，否则DX9Renderer会自动将数据解压后再交给硬件。
<p>7. Texture Apply Modes Supported：DX9Renderer不支持APPLY_DECAL纹理应用模式。
<p>8. Texture Format Requests（纹理格式要求）：精确的纹理像素映射依赖于目标硬件和显示器像素位宽。
<p>9. Palettized Texture Formats（调色板纹理格式的支持）。
<p>10. Sphere Mapping（球面映射）：GameByro中所有的渲染器都支持世界空间中的球面映射。
<p>11. DX9Renderer支持加载包括DDS、TGA、BMP格式的文件。Cube textures和volume textures都能被正确的加载，但是只能通过三维坐标寻址。
<p>12. GameByro的编辑器支持导出DX原格式的纹理，并在加载的时候不进行格式转换。将DX格式的纹理直接交给DX处理。
<p><strong>引擎渲染系统分析</strong>
<p><strong></strong>GameByro的渲染是由帧渲染系统(Frame Rendering System) 控制的，帧渲染系统封装了每一个批次的绘制过程，这样可以方便的控制整个渲染流程。
<p><strong>阴影系统：</strong>
<p>GameByro的阴影系统是使用帧渲染系统的典范，通过对已有的类以继承的方式进行功能扩展，实现了灵活的阴影渲染功能。GameByro自身支持三种光源的阴影图生成，分别为点光源（全方向光）、平行光、聚光灯。
<p>阴影系统统一由一个管理器负责控制。阴影的算法被封装成了一个个类对象中。系统中同时存在着几种阴影技术，即可以选择使用，也可以同时使用几种阴影技术作用于同一几何体。阴影系统会自动裁剪掉那些没有阴影接受体的光源，同时在计算每个光源所产生的阴影时，会裁剪掉那些不受此光源影响的几何体，达到性能优化的目的。用户很容易对阴影系统进行扩展，实现自己的阴影算法。
<p>GameByro的阴影主要是基于shadowMap技术，点光源使用了立方体阴影图，软阴影则使用了方差阴影图技术（VSM）。
<p>GameByro的阴影系统集成到了场景编辑器中，可以在编辑器中直接为光源指定阴影效果，所见即所得，提高了开发效率。
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image002_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image002 border=0 alt=clip_image002 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image002_thumb.jpg" width=244 height=119></a>
<p>（如图所示，在编辑器中同时使用点光源阴影和方向光产生的软阴影）
<p><strong></strong>
<p><strong>地形系统：</strong>
<p><strong></strong>GameByro的terrain是简单易用的，并且方便为其扩展，用以满足项目的其他功能需求。
<p>地形系统由以下几部分组成：
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image004_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image004 border=0 alt=clip_image004 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image004_thumb.jpg" width=244 height=142></a>
<p>1. NiTerrainComponent：是整个地形系统的入口，通过Terrain Interactor可以像访问容器一样访问NiTerrainComponent所有的sub-component。
<p>2. Terrain Interactor 地形迭代器，通过Terrain Interactor可以启动所有的数据查询，比如简单的碰撞检测，meta-data查询，高度图导入和文件加载。几乎所有基于terrain系统的操作都是通过Terrain Interactor完成的。
<p>3. NiTerrainSectorComponent 的内部主要包含一个负责组织地形数据块的四叉树。此对象负责从磁盘加载执行物体与地形的射线相交查询，转换地形几何体，管理内部资源（包括共享的顶点流和shader）。同时NiTerrainSectorComponent还负责进行四叉树的遮挡查询和生成可见集。
<p>4. Data Leaves and Blocks 四叉树的切分等级是通过NiTerrainComponent设置的，四叉树的每个节点都包含一个地形块，块的边长必须是2的整数幂。四叉树的根节点等级为N，叶节点为0，由高到底细节依次增加，等级为0的叶节点所包含的地形块拥有最高的细节。
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image006_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image006 border=0 alt=clip_image006 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image006_thumb.jpg" width=244 height=90></a>
<p>5. Material Layers：地形系统通过掩码图来为地形指定材质。掩码的数据指定了地表的每个部分所对应的材质。同一地表最多支持四种材质，根据各自的权重进行混合，权重数据也是通过掩码来获得。
<p>材质在地形的渲染系统中扮演着很特殊的角色，可以通过对材质指定meta-data来附载其他的信息（比如在雪地行走的声音），同时也可以通过对材质的纹理贴图为地表增加顶点或像素着色程序。此外每种材质可以包含如下指定类型的纹理贴图：
<p>l Diffuse/Base Map 反射贴图，通过3或4个通道决定反射光的颜色。
<p>l Detail Map 细节贴图，为反射贴图提供Alpha值。
<p>l Normal Map 法线贴图。
<p>l Parallax Map 视差贴图，用来保存表面像素的高度偏移信息。
<p>此外GameByro还提供了简单的地形射线追踪和水面渲染。地形射线追踪目前的实现很简单，就是用一个射线查询地表的三角形，以后会与Physics进行集成，支持通过物理引擎l实现物体与地表的碰撞检测。水面渲染主要是用两张纹理动画混合而成，通过凹凸贴图来表现水波。引擎为水面所在的地表生成一个灰度图，保存地表距离水面的高度。这里预先定义了两种颜色，深水颜色和浅水颜色，如果水面以下没有地表就直接使用深水颜色。再像素着色器中可以对高度图进行采样，从而得出当前像素的水深，然后根据这个深度在深色和浅色之间进行差值，得到最终水的颜色。不过这个效果并不是很理想，如需更好的效果，还需要对其进行扩展，比如说，增加反射和折射等。
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image008_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image008 border=0 alt=clip_image008 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image008_thumb.jpg" width=244 height=128></a>
<p><strong>材质</strong>：
<p>GameByro的材质主要是通过3DMAX的插件进行编辑的，并提供了标准的材质节点。
<p>（在GameByro中，材质节点的主要作用是负责根据着色过程生成相应的shader代码）这个节点一共有9个通道，并提供了大量可供调整的参数，这样就可以让美术方便的使用逐像素光照、视差贴图、环境体贴图这些效果，在游戏中，GameByro通过不同的材质属性和不同的光照环境编译出不同的shader代码，以二进制的形式保存在磁盘上，下次启动时载入内存，这样就不用重复编译，但在第一次进入游戏中时会有些延迟。灵活强大的材质是次世代引擎的重要特征，GameByro在这方面做到了灵活，但是美术仍然不能通过可视化工具控制材质的处理过程，只能编辑资源和修改参数，编辑性还不够强大。
<p><strong>Shader</strong>：
<p>GameByro提供了一个极具扩展性的shader系统，并将它与艺术创作工具紧密结合。GameByro定义了自己的shader格式，基于文本的.NSF文件和基于二进制的.NSB文件，.NSF文件编译后生成.NSB文件，这个过程即可以在开发时完成，也可以在运行时进行。 .NSF文件使用的是GameByro自定义的语法，并使用自己的语法分析器将其编译成相关平台的代码。实际上，GameByro的shader系统在应用程序和GPU程序之间做了一层封装，屏蔽了不同平台之间的差异，使得应用程序可以用相同的接口来与GPU之间进行交互，而GPU程序最终还是通过cg或hlsl来实现的，这样做的好处是，当平台发生改变时，只需要根据具体的平台来编写相应的shader程序，而不需要去修改应用程序。当然，shader系统也直接支持cg和hlsl格式的文件，使用方法同NSF、NSB文件相似。GameByro的Shader系统提供了一套完整的查错机制，方便使用者对程序进行debug。
<p>GameByro的shader系统集成到了所有的配套开发工具中，这样就实现了所见即所得，美术在工具中就可以直接看到资源在游戏中的效果，从而提高了开发效率。
<p>GameByro通过引入自己的shader文件格式为跨平台提供了方便，同时也增加了系统的复杂性，使用起来稍微有点繁琐，开发人员又不得不去熟悉它的语法，如果开发中不考虑跨平台的话，想要自己开发特效，也可以不使用NSF文件，直接使用fx格式的文件。
<p><strong>引擎自带的特殊材质</strong>：
<p><strong>AdditiveLightMapsMaterial</strong>（附加光照图材质,使用额外的光照图纹理来混合当前场景光照计算的结果。下面的插图是使用投射光照图纹理的渲染效果。）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image010_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image010 border=0 alt=clip_image010 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image010_thumb.jpg" width=244 height=175></a>
<p><strong>AlphaTextureBlender</strong>（纹理Alpha混合）
<p><strong><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image012_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image012 border=0 alt=clip_image012 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image012_thumb.jpg" width=244 height=134></a></strong>
<p><strong>Aniso</strong>（各向异性光照，左下的飞机）、<strong>Halo</strong>（光晕效果。上面的飞机）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image014_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image014 border=0 alt=clip_image014 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image014_thumb.jpg" width=244 height=176></a>
<p><strong>ASMSkinningLighting</strong>（参与光照计算的骨骼动画蒙皮）
<p><strong>ASMSkinningNoLighting</strong>（不参与光照计算的骨骼动画蒙皮，效率经过了优化）<strong></strong>
<p><strong></strong>
<p><strong>BaseBumpWithSpatialGlow</strong>（带有辉光效果的凹凸贴图）<strong></strong>
<p><strong><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image016_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image016 border=0 alt=clip_image016 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image016_thumb.jpg" width=244 height=154></a></strong>
<p><strong>Chrome</strong>（金属外表，使用基本纹理、漫反射cube map、镜面反射cube map、法线贴图等效果实现类似于金属表面的光泽和质感，并支持基于此效果的蒙皮操作）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image018_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image018 border=0 alt=clip_image018 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image018_thumb.jpg" width=244 height=154></a>
<p><strong></strong>
<p><strong>Colorize</strong>（使用一个自定义的属性来动态的设置或者修改颜色）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image020_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image020 border=0 alt=clip_image020 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image020_thumb.jpg" width=244 height=154></a>
<p><strong>FXSkinning and HLSLSkinning</strong>（使用可编程渲染管线进行骨骼动画的蒙皮）
<p><strong>GeneralGlow</strong>（普通辉光效果）
<p><strong>GeneralDiffuseSpecularGlow</strong>(普通镜面反射高光辉光)
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image022_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image022 border=0 alt=clip_image022 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image022_thumb.jpg" width=244 height=100></a>
<p><strong>GeneralDiffuseSpecular</strong>（普通镜面反射高光，支持蒙皮操作）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image024_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image024 border=0 alt=clip_image024 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image024_thumb.jpg" width=244 height=92></a>
<p><strong>Dot3BumpMap</strong>（凹凸贴图，右图为蒙皮效果）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image026_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image026 border=0 alt=clip_image026 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image026_thumb.jpg" width=244 height=108></a>
<p>Glass (玻璃效果)
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image028_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image028 border=0 alt=clip_image028 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image028_thumb.jpg" width=244 height=184></a>
<p><strong>OilyFilm</strong>（油状薄膜）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image030_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image030 border=0 alt=clip_image030 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image030_thumb.jpg" width=244 height=173></a>
<p><strong>LuminanceTransfer</strong>（类似于热源侦测仪的效果）<strong></strong>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image032_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image032 border=0 alt=clip_image032 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image032_thumb.jpg" width=244 height=107></a>
<p><strong>Outlining</strong>（蓝图或草图）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image034_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image034 border=0 alt=clip_image034 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image034_thumb.jpg" width=244 height=137></a>
<p><strong>ParallaxMapping</strong>（视差贴图，下面右图是使用了ParallaxMapping的效果）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image036_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image036 border=0 alt=clip_image036 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image036_thumb.jpg" width=244 height=129></a>
<p><strong>PerPixelLighting</strong>（逐像素光照，同时支持固定管线和可编程渲染管线）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image038_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image038 border=0 alt=clip_image038 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image038_thumb.jpg" width=244 height=134></a>
<p><strong>SkinnedToonShadingWithOutline</strong>（卡通渲染，支持骨骼蒙皮、描边）
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image040_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image040 border=0 alt=clip_image040 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image040_thumb.jpg" width=244 height=137></a>
<p><strong>纹理</strong>：
<p>GameByro的纹理系统支持多重纹理、投射纹理、动态纹理、特效纹理，并且使用纹理的一些高级特性是平台无关的。当然GameByro中纹理的功能仍然受限于硬件特征。主要特性和限制见渲染器。
<p><strong>工具及插件：</strong>
<p><strong>G</strong>ameByro提供了场景编辑器，资源浏览器，资源的物理模型浏览器、动画编辑工具，字体创建工具，以及3DMAX、MAYA插件，材质编辑是在插件中进行的，这套工具包含了完整的3D资源开发流程。<strong></strong>
<p><strong>物理系统</strong>
<p>GameByro通过PhysX提供物理仿真模拟，只需几行简单的代码就可以将GameByro的场景和PhysX建立起联系，不过需要在显式的调用相关接口来进行物理仿真对象和场景对象间的同步，即将物理仿真的结果作用于场景中的对象。同时，还可以通过参数来决定是否需要等待物理仿真运算的结果。这就保证了一定的灵活性，例如，用户可以不对静态物体进行同步，达到优化的目的。还可以通过把更新的接口放在下一次循环开始的时候调用，这样可以减少由于等待物体仿真模拟造成线程的阻塞，充分的利用了CPU资源。
<p><strong>粒子系统</strong>：
<p>GameByro的粒子系统为3ds max 和 Maya等粒子生成工具提供了完整的插件系统，并完全支持软粒子，即粒子会受到力的影响。
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image042_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image042 border=0 alt=clip_image042 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image042_thumb.jpg" width=244 height=152></a>
<p><strong>流处理引擎</strong>：
<p>GameByro实现了一个完全跨平台的流处理引擎。使用者可以方便的利用它完成多线程任务，如资源的加载等，完全发挥多处理器平台的威力，而不用关注底层细节。
<p><strong>效率分析：</strong>
<p><strong></strong>为了对GameByro进行全面的评估，笔者试着对GameByro的渲染效率进行了简单的测试，本人的机器配置如下：
<p>l CPU：E2180
<p>l GPU：2400PRO
<p>l 内存：2GB
<p>基本可以代表当前中低端玩家的配置。
<p><strong>场景一</strong>：
<p>下图为一个大的室外地形场景，包括建筑物、水面，地表使用了法线贴图，除了植被以外，基本包含MMORPG的大部分室外场景要素。最后测试的结果平均帧数为21帧。帧数偏低的原因是整个场景使用了逐像素光照，同时没有采用LOD进行优化。
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image044_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image044 border=0 alt=clip_image044 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image044_thumb.jpg" width=244 height=139></a>
<p><strong>场景二</strong>：
<p>如图所示场景中有100个角色在做不同的动画，每个角色有51个骨骼，整个场景共有287900个多边形和556600个顶点，并且场景中有一个点光源和平行光源，在没有阴影的情况下帧数大约在27~28帧左右。这些骨骼动画使用了GameByro提供的骨骼动画优化方法，性能有一定提升。
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image046_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image046 border=0 alt=clip_image046 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GameByro_934A/clip_image046_thumb.jpg" width=244 height=142></a>
<p>由以上结果可以看出，GameByro的骨骼动画渲染效率尚可，而室外地形渲染系统的性能稍显不足，再没有角色和植被的情况下，仅能达到22帧左右，如果在实际的游戏环境还要算上其他游戏系统（网络、逻辑、UI等）的性能消耗，这是不足以让玩家流畅进行游戏的，还需进一步手动的优化。
<p><strong>总结</strong>：
<p>GameByro拥有一套灵活的架构，便于使用和扩展，易于上手；配套的开发工具比较齐全，拥有完整的美术开发流程；完全支持跨平台，便于游戏的移植；同时由于为了保证灵活性，在设计上面比较偏重于功能和扩展，而不是性能。
<p>高度的可定制性带来的是较低的整合，GameByro没有提供游戏框架，地图编辑器也仅仅实现了最通用的功能，即地表和物件的摆放。这就说明，如果要使用GameByro开发产品，就需要自己整合GameByro，并对其进行一定的修改和扩展。这也是它与UnrealEngine3区别最大的地方。
<p>&nbsp;
<p>作者：叶起涟漪 </p>
<img src ="http://www.cppblog.com/CrazyDev/aggbug/113017.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/CrazyDev/" target="_blank">CrazyDev</a> 2010-04-19 22:29 <a href="http://www.cppblog.com/CrazyDev/articles/113017.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GPU程序在GameByro中的使用（转）</title><link>http://www.cppblog.com/CrazyDev/articles/113013.html</link><dc:creator>CrazyDev</dc:creator><author>CrazyDev</author><pubDate>Mon, 19 Apr 2010 13:53:00 GMT</pubDate><guid>http://www.cppblog.com/CrazyDev/articles/113013.html</guid><wfw:comment>http://www.cppblog.com/CrazyDev/comments/113013.html</wfw:comment><comments>http://www.cppblog.com/CrazyDev/articles/113013.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/CrazyDev/comments/commentRss/113013.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/CrazyDev/services/trackbacks/113013.html</trackback:ping><description><![CDATA[链接：<a href="http://www.cppblog.com/rise-worlds/archive/2009/04/22/80699.html">http://www.cppblog.com/rise-worlds/archive/2009/04/22/80699.html</a><br><br>
<h4>引言：</h4>
<p>GameBryo拥有一套复杂的材质系统，这套材质系统可以根据渲染对象的状态和属性生成不同的shader代码，提高了渲染流程的适应性，可以使你定义一套材质能适应多种渲染对象。同时，GameByro将shader的初始化和使用插件化，方便与美术工具集成，并且实现了平台无关性。为了实现这些目的，GameByro使用了一套复杂的机制，本文主要解析GameByro如何生成、编译并使用shader代码。
<h4>Shader</h4>
<p>GameBryo的shader的接口封装在NiShader中，顶点数据流声明，常量表的访问，渲染状态的设置都是通过这个类（有点类似于D3Deffect）。在程序运行NiShader是由NiShaderFactory负责管理的，NiShaderFactory通过NiShaderLibrary从文件中创建shader，用全局性的map管理起来。NiShaderLibrary通过解析shader文本创建NiShader对象，并调用3D图形接口编译shader代码，将这个类以dll的形式封装，就可以作为插件来使用。NiShader类的创建可以通过解析文件来进行，也可以通过C++的类来定制，只需从NiShader上继承即可。GameByro为PC平台提供了一个NiD3DXEffectShaderLib库，这个库提供了解析shader文件和初始化shader对象的功能。用户只需按GameByro定义的格式编写shader代码的语意和注释，NiD3DXEffectShaderLibrary就会根据文本来创建NiD3Dshader对象，在应用程序中就可以通过Techinqe的名称来访问这个对象。通过这种机制，我们将shader文本文件放在相关美术工具指定的目录下，在工具中就可以使用这些shader，并且能够通过shader的语意和注释为相关参数和变量生成UI，方便美术调试。
<p>WIN平台上的整个流程如下：
<p>1. 应用程序在启动时会先初始化整个shader系统，接下来导入Shader解析库和加载库（dll的形式）。
<p>2. 接下来应用程序将NiD3DShader的初始化工作委托给NiShaderLibrary来处理，NiShaderLibrary首先通过NiD3DXEffectLoader载入所有的shader文本文件，并通过NiD3DXEffectParser解析文本生成NiD3DXEffectFile对象，同时NiD3DXEffectLoader还负责将shader代码编译成二进制形式的GPU程序。
<p>3. 最后由NiD3DXEffectTechnique负责通过NiD3DXEffectFile上的信息生成NiD3Dshader对象。
<p>4. 所有的shader对象创建后，NiShaderLibrary的初始化就结束了，最后由NiShaderFactory负责统一管理。
<h4>材质：</h4>
<p>NiMaterial为渲染对象生成和定义Shader,NiMaterialInstance为渲染对象分配 和Cach Shader。NiFragmentMaterial提供了一个Shader Tree框架，在它的继承类中可以使用这个框架搭建shader tree。这个机制允许NiFragmentMaterial根据对象不同的渲染状态生成不同的shader代码，Cach在内存中，并保存到磁盘文件。GameByro描述符的概念大量使用，包括前面提到的Shader解析过程也是通过描述符来传递信息。在材质系统中主要使用了NiMaterialDescriptor和NiGPUProgramDescriptor这个两个类做描述符，这两个类中保存的信息是兼容的，都是为了描述某种材质在渲染对象的某一特定渲染状态下所对应的GPU程序的特征。NiFragmentMaterial通过渲染目标的状态和属性生成NiMaterialDescriptor，并通过NiMaterialDescriptor查找匹配的shader，如果找不到，则通过shader tree生成相应的shader程序，并保存到磁盘文件中。当下一次应用程序启动时就可以通过这个文件直接创建NiShader对象。可以说通过NiFragmentMaterial生成的shader代码是为特定的渲染对象在特定的情况下量身打造的。
<p>整个过程的详细流程如下：
<p>1. 在每次渲染一个物体之前，NiMaterialInstance会先判断这个物体的shader程序是否需要更新，如果不需要更新，就直接返回当前Cach的NiShader；如果需要更新， NiMaterialInstance首先会根据物体的渲染状态为其生成一个NiMaterialDescriptor，然后将这个NiMaterialDescriptor和当前Cach住的NiShader进行比较，如果匹配仍然返回当前Cach的NiShader，如果不匹配，将获得shader的工作转交给NiMaterial进行。
<p>2. NiMaterial首先通过这个NiShaderFactory 查询匹配这个NiMaterialDescriptor的NiShader，如果找不到，就通过NiMaterialDescriptor生成NiShader，同时生成一段Shader代码，并保存到以shader描述符中的特征码来命名对应的shader文件。
<p>3. 当获得相应的NiShader对象后，NiMaterialInstance会调用NiShader的SetupGeometry接口，在这个接口中会进行顶点声明。
<p>以下是NiMaterialInstance为Geometry选择shader的代码：
<p>NiShader* NiMaterialInstance::GetCurrentShader(NiRenderObject* pkGeometry,
<p>const NiPropertyState* pkState,
<p>const NiDynamicEffectState* pkEffects)
<p>{
<p>if (m_spMaterial)
<p>{
<p>bool bGetNewShader = m_eNeedsUpdate == DIRTY;
<p>if (m_eNeedsUpdate == UNKNOWN)
<p>bGetNewShader = pkGeometry-&gt;GetMaterialNeedsUpdateDefault();
<p>// Check if shader is still current
<p>if (bGetNewShader &amp;&amp; m_spCachedShader)
<p>{
<p>bGetNewShader = !m_spMaterial-&gt;IsShaderCurrent(m_spCachedShader,
<p>pkGeometry, pkState, pkEffects, m_uiMaterialExtraData);
<p>}
<p>// Get a new shader
<p>if (bGetNewShader)
<p>{
<p>NiShader* pkNewShader = m_spMaterial-&gt;GetCurrentShader(
<p>pkGeometry, pkState, pkEffects, m_uiMaterialExtraData);
<p>if (pkNewShader)
<p>{
<p>NIASSERT(m_spCachedShader != pkNewShader);
<p>ClearCachedShader();
<p>m_spCachedShader = pkNewShader;
<p>if (!pkNewShader-&gt;SetupGeometry(pkGeometry, this))
<p>ClearCachedShader();
<p>}
<p>else
<p>{
<p>ClearCachedShader();
<p>}
<p>}
<p>m_eNeedsUpdate = UNKNOWN;
<p>}
<p>return m_spCachedShader;
<p>}
<p>如果想通过NiFragmentMaterial实现自己的shader tree就需要在NiFragmentMaterial提供的接口中实现自己拼装代码的逻辑，代码块由NiMaterialLibraryNode封装，NiMaterialLibraryNode既可以直接写C++代码来定义，也可以先写成XML脚本，再由专门的解析工具转换成C++代码。
<p>由NiStandardMaterial生成的shader代码文件如下图所示：
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image002_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image002 border=0 alt=clip_image002 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image002_thumb.jpg" width=244 height=141></a>
<p>文件名就是NiMaterialDescriptor的掩码，用来标识的shader代码的行为。
<p>Shader代码的行为描述如下：
<p>Shader description:
<p>APPLYMODE = 1
<p>WORLDPOSITION = 0
<p>WORLDNORMAL = 0
<p>WORLDNBT = 0
<p>WORLDVIEW = 0
<p>NORMALMAPTYPE = 0
<p>PARALLAXMAPCOUNT = 0
<p>BASEMAPCOUNT = 1
<p>NORMALMAPCOUNT = 0
<p>DARKMAPCOUNT = 0
<p>DETAILMAPCOUNT = 0
<p>BUMPMAPCOUNT = 0
<p>GLOSSMAPCOUNT = 0
<p>GLOWMAPCOUNT = 0
<p>CUSTOMMAP00COUNT = 0
<p>CUSTOMMAP01COUNT = 0
<p>CUSTOMMAP02COUNT = 0
<p>CUSTOMMAP03COUNT = 0
<p>CUSTOMMAP04COUNT = 0
<p>DECALMAPCOUNT = 0
<p>FOGENABLED = 0
<p>ENVMAPTYPE = 0
<p>PROJLIGHTMAPCOUNT = 0
<p>PROJLIGHTMAPTYPES = 0
<p>PROJLIGHTMAPCLIPPED = 0
<p>PROJSHADOWMAPCOUNT = 0
<p>PROJSHADOWMAPTYPES = 0
<p>PROJSHADOWMAPCLIPPED = 0
<p>PERVERTEXLIGHTING = 1
<p>UVSETFORMAP00 = 0
<p>UVSETFORMAP01 = 0
<p>UVSETFORMAP02 = 0
<p>UVSETFORMAP03 = 0
<p>UVSETFORMAP04 = 0
<p>UVSETFORMAP05 = 0
<p>UVSETFORMAP06 = 0
<p>UVSETFORMAP07 = 0
<p>UVSETFORMAP08 = 0
<p>UVSETFORMAP09 = 0
<p>UVSETFORMAP10 = 0
<p>UVSETFORMAP11 = 0
<p>POINTLIGHTCOUNT = 0
<p>SPOTLIGHTCOUNT = 0
<p>DIRLIGHTCOUNT = 0
<p>SHADOWMAPFORLIGHT = 0
<p>SPECULAR = 1
<p>AMBDIFFEMISSIVE = 0
<p>LIGHTINGMODE = 1
<p>APPLYAMBIENT = 0
<p>BASEMAPALPHAONLY = 0
<p>APPLYEMISSIVE = 0
<p>SHADOWTECHNIQUE = 0
<p>ALPHATEST = 0
<p>NiStanderMaterial就是根据这些掩码的数据来生成shader代码，用户可以通过重载GenerateVertexShadeTree、GeneratePixelShadeTree、CreateShader这些接口来定义自己的shader生成规则。
<h4>增加自己的渲染效果：</h4>
<p>通过前几节我们可以了解到，想定义自己的材质，一是通过编写shader代码完成。在应用程序初始化的时候，这些shader代码会被初始化成NiShader对象，进一步的通过NiShader对象来初始化NiSingleShaderMaterial对象，并分配给渲染对象。在GameByro默认的渲染流程中，这些步骤都是自动进行的，美术只需在3DMAX插件中为几何体的材质指定Shader程序，导出到nif文件，应用程序就能正确加载并渲染；二是定义自己的NiMaterialFragment类，在类中定义如何生成shader，在应用程序运行时只要将这个类的实例指派给几何体，这个类就会自动为几何体生成shader。这两种方式对于美术人员来说，主要区别在于，采用第一种方法定义的材质，其渲染数据的设置必须严格符合shader代码中所需的数据，否则就会报错。（比如说，顶点数据流必须严格符合shader程序的定义，必须为shader中每个采样器提供格式正确的纹理）；而采用第二种方法定义的材质，就有很高的容错和适应性，但是这种容错性和适应性需要自己写代码来完成，GameByro提供的NiStanderMaterial就提供了这套完整的机制。每个贴图槽内的贴图如果你设置就会生成相应的贴图处理流程，如果不设置，就没有这张贴图的处理流程。
<p>为了验证这个过程，笔者尝试增加了一个自己的shader特效——SubSurfaceScattering，简称3s，其原理是模拟光在半透明物体中散射的效果。由于该效果无须预处理过程，所有的贴图均来自磁盘文件，所以比较容易融合到GameByro工作流中。
<p>笔者将在FX COMPOSER中调试通过的fx文件放入SDK中的SDK\Win32\Shaders\Data目录下，在3DMAX的材质面板选择GameByroShader，然后就可在显示shader的组合框中看到文件中定义的Techinqe，选择点击apply按钮，就会出现自定义的参数调整界面。
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image004_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image004 border=0 alt=clip_image004 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image004_thumb.jpg" width=244 height=175></a>
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image006_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image006 border=0 alt=clip_image006 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image006_thumb.jpg" width=244 height=210></a>
<p>通过调整参数，最终得到皮肤和玉器的渲染效果如下：
<p><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image007_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image007 border=0 alt=clip_image007 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image007_thumb.jpg" width=244 height=188></a>
<p><strong></strong><strong>皮肤</strong>
<p><strong><a href="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image009_2.jpg"><img style="BORDER-RIGHT-WIDTH: 0px; DISPLAY: inline; BORDER-TOP-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px" title=clip_image009 border=0 alt=clip_image009 src="http://www.cppblog.com/images/cppblog_com/rise-worlds/WindowsLiveWriter/GPUGameByro_9EC0/clip_image009_thumb.jpg" width=244 height=172></a></strong>
<p><strong></strong><strong>玉器</strong>
<h4>总结</h4>
<p>GameByro的这套开发流程非常方便直观，但是美术仅能为shader程序分配静态的数据源，比如说光照图等，CubeMap等；而一些在程序中实时生成的纹理数据则无法整合到美术工具中，比如说阴影图、折射图、反射图等，这些都需要程序写代码来实现。调试起来就不大方便了。大部分情况下，我们只需要使用GameByro提供的NiStanderMaterial就可以完成大部分材质的需求，特殊的效果可以自己写shader或者通过引擎提供的shader库来完成，只有当我们需要即根据复杂的情况做很多不同的处理时，我们才需要重载NiFragmentMaterial搭建自己的shader tree。不过搭建shader tree的程序一般比较复杂，编写难度大，虽然引擎允许通过XML文件来编写材质节点，但是使用起来仍然不方便。GameByro并没有提供相关的后期处理的开发工具，后期处理的特效并不能所见即所得，这方面还需完善。
<p>GameByro为几何体在特定的环境下生成专用的shader代码，具有一定的灵活性，但是也付出了以下代价：
<p>l 分析几何体的属性和当前状态，为其生成shader代码的过程有性能损耗。
<p>l Shader代码生成后会保存到磁盘文件中，这个过程如果不使用异步，可能会引起阻塞。
<p>l 生成的NiShader对象会有内存消耗。由于GameByro默认的实现是将所有的shader文件初始化成NiShader对象，所以当游戏运行的时间久了以后会生成大量的shader文件，这时候内存的消耗可能会很可观，同时加载的时间也会增加。不过可以自己控制加载的流程，在这里进行性能优化。
<p>&nbsp;</p>
<p>作者：叶起涟漪</p>
<img src ="http://www.cppblog.com/CrazyDev/aggbug/113013.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/CrazyDev/" target="_blank">CrazyDev</a> 2010-04-19 21:53 <a href="http://www.cppblog.com/CrazyDev/articles/113013.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>