集え!勇士の名の下に --《勇士online》研发回忆录 6

本文是用第一人称记载的真人真事,里面涉及到了大量他人的名称等信息,所以为了避免不必要的纠纷,谢绝转载 在上市计划失败之后,也即2007年的下半年,厦门分公司这边并没有受到太大的波及。因为纵使上市失败,9you依然是一个盈利的公司,走自研产品路线依然是公司的战略方向。所以上海本部及北京厦门的分公司的各个项目组,依然按部就班地进行开发。而我在这时候负责了游戏的特效播放模块和相关的工具。 如果要问什么是特效,时至今日,去问一个中国游戏业从业者,估计一百人有一百个回答。如果问一名策划,他可能会说:特效就是那种看起来很酷炫的各种风雨雷电刀光剑影。如果问一名美术,他就可能会说:特效么,就是除去场景、角色模型与动作、UI界面之外的其他的美术的统称。如果问一名程序,他可能会说:特效,可能就是用粒子系统、帧动画、还有就是一些特殊的着色器做的效果吧。的确,出发点和看待事物的角度不一样,便会得到不同的答案,而这也往往是导致各部门之间沟通困难,有时候甚至是鸡同鸭讲的情况。 从程序的角度上来说,大部分游戏所看到的“特效”,确实是使用粒子系统技术去实现的。GameBryo2.3版本的粒子系统已经做得非常之强大。可以直接从3DS MAX/Maya中导出特效文件到引擎自身的Nif文件中。现在热门的Unity3D引擎则没有采用这种直接从3DS MAX/Maya中导出的方式,而是自身实现了一套名为ShuRiken(这名字实质是日语“手里剑”的发音,即俗称的“忍者镖”)的粒子编辑器系统。这两种方式各有优劣在此不予以对比评论。但在当时,这种直接从美工熟悉的工具中直接导出生成游戏引擎可用的特效数据。对于降低学习成本,提高生产力是大有裨益的。 GameBryo2.3的粒子系统的核心类是NiParticleSystem,这个类表征了“粒子系统及粒子”概念。每一个NiParticleSystem可以绑定若干个修改器。这些修改器用以控制粒子系统中的粒子的生存期、包围了整个粒子的包围体、粒子的平移旋转放缩、还有就是对粒子的基本物理学模拟。 除去修改器外,NiParticleSystem还绑定了时间控制器和粒子发射器。时间控制器是用来控制粒子系统,随着每一帧时间的流逝,更新粒子及整个系统的相关属性的。而粒子反射器则是用来控制粒子的发射方式。例如NiPSysCylinderEmitter类就是用来控制粒子以一个圆柱状的样子发射出来。 在游戏引擎已经做了大部分的特效播放所需的基础工作前提下,游戏的特效播放模块和相关的工具所要实现的功能就变得较为简单了,要解决的问题就是“在何时何地去播放”的问题。在“何地”是一个很简单的transform setting的问题,即把特效资源的root node指定好它的parent node,以及设定这个root node在parent node的局部坐标系下的position,rotation,scale即可。而在“何时”播放则是一个涉及到上层逻辑,并且会涉及到网络同步通信的事情。 大约在2007年月,我做了第一个编辑特效播放逻辑的工具。这个工具当时实现的功能也可谓是比较简陋,主要就是给定一系列的特效nif文件,一系列的角色动作模型和动画序列帧,然后给某个角色模型的某一个动作序列帧,通过一个text key标签,指定在这个动作序列帧中某个时刻,播放某个指定的特效nif文件。然后把这些特效文件集合,角色模型及动作集合,以及特效与动作之间的关联关系,存储成一段lua代码脚本,运行时调用此lua脚本就能达到播放效果。 上面的图就展现了这样的一个关联关系:指定一个企鹅模型,企鹅模型有若干的动作序列,选取了名为walk01的序列,然后在序列帧中的第0.317238秒除,指定一个名为testattack~SS-LeftFoot-Emitter~Women_attack_effect的text key,即一个标签,然后在这个标签上绑定一个名为Women_attack_effect的特效文件。 在项目早期,用一个编辑器,把某个特效绑在某个动作上的方式是可行的——因为在这个阶段的需求原本就是很简单的,但随着项目的开展,绑定在角色动作上的特效播放,已经随着“角色技能”这一概念内容的丰富,变得包含了很多需要和其他游戏属性的相关联了,所以我早在2007年就做的这个编辑器的功能是远远不够的,而角色技能的流畅感,打击感是action game的核心体验,在经过多次反复和挫折之后,角色战斗和技能这块,最终还是没有使用我的这个编辑器去生成的数据,也因为我后来接手了游戏别的模块,没有去扩展这个工具。所以最后由专职程序专职动作美术专职动作策划,去一个一个动作调试绑定——当然,这已经是08年后半段的事情了。以技能绑定特效播放这一块为例,何时去播放特效,就是逐个逐个手动地去反复试验,试到特效的播放和角色动作刚好合拍为止,然后填写到表格上。一个技能有多复杂,里面涉及到有多少参数,从下面这个表格中的一个表项就可以看出端倪: 上述表格中有effect0到effect4共五个标签,表示一个技能所对应的一个动作序列上,有五个可用的技能特效位。这个五个技能特效位对应了动作序列上的五个text key。text key则是按照约定的规则,由动作美术在3ds max中指定并导出——这样子就可以使得美术和策划在3DS MAX阶段就能准确地定位播放技能特效的时间和位置。 整个2007年的下半年,项目组表面上风平浪静,然而背后却危机重重,首先公司最初给的时间是“一年内把东西做出来”,但到2007年底为止,整个产品可看到的内容,仅仅是主角在新手村场景跑一跑,还有就是几个简单的界面UI。到底公司对这个项目抱何种态度,还做不做?一时间人心惶惶。其次是到底要做成什么样子的一个游戏,是一个RPG,还是一个休闲竞技类的游戏?2007年一年也快过完了,都依然处于摇摆状态。以chenliangsen为主策的策划团队,在多方面的压力之下,处于风雨飘摇的境地。而程序与美术之间一些工作上的争执,程序组前端后端之间一些人事之间的龃龉,也逐渐浮出水面。在这个前途未明的局面下,大多数人,包括我自己在内,都发生了动摇。一些2007年入职的同事,包括美术,策划,程序,纷纷提出了离职。

集え!勇士の名の下に --《勇士online》研发回忆录 5

本文是用第一人称记载的真人真事,里面涉及到了大量他人的名称等信息,所以为了避免不必要的纠纷,谢绝转载 9you网IPO定价出炉仅两天之后,7月6日,与9you网一直纠纷不断的《劲舞团》韩国开发商T3公司发表公开信,称“9you网背叛了我们”,并表示近期将正式起诉9you网。中韩纠纷正式公开化。拜网络所赐,虽然时过境迁,但在当年是业界大新闻的这件事情各大游戏网站都有详细报道,17173网站还专门制作了专题,详细记录了事情的来龙去脉。翻开老黄历,事情的大概经过就是下面的样子: 5月21日,《劲舞团2》的中国运营权被授予另一家网游运营商the9city。这使9you网陷入了与the9city竞争的困扰,双方的关系开始僵化。 5月22日,9you网发布声明称9you拥有《劲舞团》的商标权,其后续游戏将不可使用与此相关的中文名字。 5月31日,T3发布声明称所有有关“audition”《劲舞团》游戏的权利包括著作权及商标权都属于开发公司T3。 7月4日,T3宣布:采取一切手段从9you讨回欠款;不再与9you续约《劲舞团》。双方矛盾开始激化。 7月6日,9you回应:T3所述毫无根据,《劲舞团》的全球发行商为韩国Yedang,T3无权干涉续约。9you网董事会不得已于同日16:00 决定暂停IPO。 7月10日,韩国Yedang发表声明,向9you网施压。9you决定暂缓上市。 7月18日,9you再次声明:拖欠T3公司分账款一事,纯属捏造。 7月19日,9you网高层首次向媒体表态:这是一场蓄谋已久的敲诈。 7月20日,9you网回应:将起诉T3 律师团已经行动。 7月30日,韩国单方面宣布与9you解约《劲舞团》。9you声明:韩国单方面解约将遭法律惩罚,将以《劲舞团》商标侵权正式起诉韩方。 7月31日,the9city宣布获《劲舞团》运营授权 08年8月生效,称没有任何企业拥有《劲舞团》中文商标注册权。9you反驳the9city,强调已注册《劲舞团》游戏商标。 8月16日,9you网董事会决定撤销9you网的上市申请,向大阪证券交易所提交相关的撤销申请。 8月17日,大阪证券交易所于8月17日批准了撤销9you网上市申请。 9月12日,9you宣布续签《劲舞团》至2010年。消息人士称9you为此需支付4500万美元,及30%的游戏收入分成。the9city对此未作表态。 作为《劲舞团》的研发商,韩国T3和9you的积怨由来已久,冰冻三尺并非一日之寒,但赶在9you上市之际发难,总是很耐人寻味的。the9city和9you之间的纠纷,在商言商,也不见得是什么难以理解的事情。唯一让人难以释怀的就是每个正常中国人心中的必定存在的民族主义情结——正如某评论文章所指出的那样子:三方都是输家。 9you网因此纠纷而暂缓上市,失财。T3全资子公司G10也被波及而上市搁浅,失信。而the9city这样子做,从非商业角度来说,就是两家中国的公司内斗,让韩国人坐地起价观虎斗,失义。也许朱先生对业界和广大玩家的置疑早就不以为然了,但站在财务的角度来看,作为一家上市公司,the9city大把大把撒出的是广大投资人的钱,对于这种严重偏离商业规则的巨额资金流出行为,如果the9city不能给出一个合理的解释澄清的话,也将会面临美国证监会的调查。 中韩纠纷终究还是落幕了。雄心勃勃的上市计划失败了。客观地说,一家公司上市与否,对每个员工当然都有影响。区别就是影响大小而已。对一般基层员工来说,影响就几乎可以忽略不计。但对于高管及中级干部来说,影响甚剧。首先,一家未上市的公司,虽然目前仍然盈利,但以后该怎么走下去的问题,对高级管理层而言确确实实就是一个巨大的考验。其次,批量制造百万富翁的梦的破灭,对于上承高管下接员工的中层干部来说,更是严重考验了其心态和毅力。回过头来看,因此而心态发生剧烈变化的中层干部,为数不少。唯一聊以欣慰的是,这件事,更是坚定了高管层走自主研发的路线。只有自己的产品才不受制于人,才是王道——两年之后的,the9city失去了《WOW》的代理权之后,整个公司的发展状况,更是深刻印证了这一点。 无论如何,《勇士online》团队并没有因为这件事情受到太多的影响,研发还是按照既定的计划在走。走到2007年的下半年,场景的制作方式和实现基本上是已经成熟了,接下来主要要搞的,就是重头戏——角色的动作管理,特效管理,动作游戏应有的打击效果和被击打效果的实现,以及相关的服务端同步。整个游戏,最难的就是在这里。无论是程序,策划,还是美术,对于这一全新的类型,大家都是小白。这一做,就是做了一年多,才最终确定一套可用的框架。中途有过多次的反复;猜忌质疑;人际关系之间的冲突;上司的进度压力;以及项目夭折的预感。

集え!勇士の名の下に --《勇士online》研发回忆录 4

本文是用第一人称记载的真人真事,里面涉及到了大量他人的名称等信息,所以为了避免不必要的纠纷,谢绝转载 计算地表高度的方法很传统也很经典,就是用一根射线从无穷远处,以垂直于水平面的方向向地表网格投射,计算出这射线和地表网格中的某个三角形的交点。这个交点的高度值就是这一点地表的高度。了解了这一点之后,问题就变成了:构成地表的网格那么多,到底要向哪一个网格投射射线?在何处投射射线?投射多少根射线? 为了标识出需要投射的网格。客户端程序和美术部门约定,在对场景建好模之后,在所有的可通行的区域,紧贴着地表网格,贴上一块名字含有walkable字符标识的网格。要和射线进行投射相交检测的,就是这一块网格。在客户端,以每10unit乘以10unit的大小把walkable网格划分成若干虚拟的格子。射线向每一个格子的中点做投射,如果能求到交点就表示这格子能通行,记录下交点的高度值,如果求不到交点就表示该格子不能通行。 客户端采用10unit乘以10unit主要是为了精确,如果格子过大的话,就会出现角色脚踩的地方和真正表示该格子高度的交点不重合,而可能导致角色的脚会陷下去,或者浮起来的问题。但服务端如果采用10乘以10的大小的话,数据量又过大了。所以服务端采用了100unit乘以100unit的大小作为服务端的地表格子。后来发现100乘以100又过大了,经常会出现因为精度判断不一致的问题而导致客服不同步。最后选用了60乘以60的大小。 一开始时投射线算地表高度的算法,客户端和服务端都是使用了同一套算法。只是因为单元格的大小不一样,所以投射的位置点也不一样,这时候,会出现某一个“客户端小格”,在客户端计算所得到的值,是可通行的,而服务端对应的那个能包住那个“客户端小格”的“服务端大格”,却是不可通行的情况。这一问题,导致了后面角色使用技能攻击时,经常有一些怪物被轰击出场景外,被卡在那里,走不进去,也走不出来的囧况。 除了要计算地表的高度之外,按策划的要求,还需要做一个能“刷怪”的关卡编辑器,所谓“刷怪”就是在关卡里面摆放会出现的怪物或者NPC。策划部门早期给的需求很简单,就是用一两个小方块代替了怪物或者NPC本身的模型。在07年的时候这个简单的功能是够用了,但转到后期大批量“刷怪”时,就显得捉襟见肘了而不得不重新编写关卡编辑器了。由此可见对项目的统筹管理,“未雨绸缪”是多么的重要,能想得到的事情就要预先去做而不是等到以后。当然,这也就是后话了。 约在2007年7月,第一个版本的关卡编辑器我就做出来了,功能很简单,甚至可以说很矬。主要就是导出地表高度和简单地刷怪。为了能在事后对关卡编辑器生成的文件便利地修改。还附带着做了一个简单的文件观察器。 约在2007年7月,第一版关卡编辑器出来之际。Epic中国的副总裁刁劲翀到访厦门9you。刁先生此行主要是给Epic的神器——Unreal3引擎做宣传。Unreal3引擎有多强大这个无需我多言了。除去渲染内核之外,刁先生还演示了引擎周边的各式辅助开发工具。如场景编辑器,剧情事件编辑器,甚至还有和物理引擎相关的编辑器。我当时提出想看看Unreal3引擎是怎么处理偶人动画的。刁先生就现场演示了用相关编辑器拖拽一个偶人模型,编辑偶人的关节,指定各种参数,等等。同是使用了PhysX引擎作为底层的支持,Unreal3就封装得完善得多。 CQ在观看了刁先生操作了Unreal3的模型动画编辑器后,提了一个问题:“顶点动画和骨骼动画是用两种截然不同的算法思想实现的,Unreal引擎是怎么做到一个模型能同时混合播放顶点动画和骨骼动画的?”对此,刁先生没有正面回答,他只是点出了:无论是顶点动画也好,骨骼动画也罢,最终还是归结到操作构成模型的每一个顶点,使之产生位移,以产生动画效果。既然都是操作顶点,那么就自然会有办法让这两种动画融合在一起。 刁先生的这番回答让我想起在Gamebryo-PhysX中,当物理系统接管负责播放模型的动画时,预编辑好的模型的骨骼动画就不能播放了。也许是Gamebryo的开发人员,在封装的PhysX时,没有考虑如刁先生所说的,最终的任何动画归根结底就是操作顶点。他们只是为了方便,就有一种播放动画的方式,就只能一次单独播放一种的吧。 Unreal3的演示完毕之后。《勇士online》开发组的评价就是:和Unreal3引擎相比,我们用的Gamebryo就好像是处于原始社会阶段。客观地说,确实如此。但在随后的5年时间里,我目睹了很多用Unreal3引擎开发最终却搞砸了的游戏。我不得不在此谈谈关于选择引擎的看法。 Unreal3引擎很强大,这不假,但强大带来了两个问题,第一个就是价格问题。作为世界顶级的游戏引擎,并且提供了如此完善的周边开发工具,其价格也是不菲的。对于处于创业阶段的中小团队来说可谓是天价了。如果说中国不缺资本,缺的是知本的话。那么第二个问题就是学习成本的问题。Unreal引擎提供了那么多的周边开发工具,每一个工具能用得好来都能成为一方专家了,更遑论要去寻根问底去追踪剖析它的源代码实现了。一个开发团队,想吃透Unreal3引擎,两三年的使用也仅仅是登堂入室而已,要成为专家级的人物不用它做过两三款游戏都算不上。如果单从“能实现很酷的3D效果来看”,Unreal3等大型商业引擎当然是当仁不让的头号选择,但“实现很酷的3D效果”的另一个前提,就是需要很酷的硬件支持。而网游产品则是要面向于普罗大众;面向于一台电脑用个三五年都不升级的屌丝。作为一名什么都不考虑,只痴迷于技术的程序员来说,有得用Unreal引擎当然能满足他的好奇欲和征服欲,以及一些莫名其妙的优越感和满足感。但作为技术总监,项目负责人,或者作为战略决策人的老板们,则要通盘去从人力成本和金钱成本等方面上,综合考虑抉择。如果不顾当前的实际需求而片面地搞些噱头,不是一个成熟的决断。可惜,纵观业界,尤其是一些有抉择之权却无抉择之技术分析能力的老板们,搞商业宣传商业忽悠到最后,连自己都忽悠到了,令人扼腕叹息。 刁先生的推荐演示在厦门没什么收获,厦门开发团队还是坚持使用了Gamebryo引擎。但在北京9you,Unreal引擎却得到了他们团队的负责人的青睐。北京9you主打的产品,在07年年底立项的《神兵传奇》,就是采用了Unreal3引擎开发。 2007年的中段,《勇士online》项目,厦门9you,乃至整个9you,表面上都风平浪静,各项目各部门都在有条不紊地做着自己的工作。9you上海总部那边正为9you网在日本上市做着大量的工作。从《劲舞团》系列的火爆程度,以及北京上海厦门各研发团队各项目都已全线铺开的势头来看,整个9you上下对这次在大阪证券交易所的IPO成功,都抱乐观态度。 2007年6月份,大阪证券交易所发布公告,称9you网的上市申请已获得核准,将于7月12日正式挂牌。9you将成为继盛大和九城之后第三家以网游概念上市的中国互联网企业。7月4日,9you网确定IPO发行价为每股20万日元(约合1632美元),融资174亿美元。 然而,就在这时候,意想不到的事情发生了。从某个意义来说,《勇士online》,甚至整个9you,命运从这时起,发生了改变。

集え!勇士の名の下に --《勇士online》研发回忆录 3

本文是用第一人称记载的真人真事,里面涉及到了大量他人的名称等信息,所以为了避免不必要的纠纷,谢绝转载 游戏场景是整个游戏的舞台,游戏剧情的进展都要在这个舞台上演。网络游戏的场景管理主要集中在两块:一块是纯客户端方面的,对被渲染的可视数据进行组织管理,使得渲染速度尽可能地高效。另一块就是客户端和服务端共同使用的地形数据的组织和管理。 前一种纯客户端的对可视数据进行组织管理的方法有很多种,例如:binary space tree,octree,quadtree,BVH(bounding volume hierarchy),等等。方法虽多,但万变不离其宗,无一不是以尽可能快速地把不在当前view frustum中的可视数据给剔除出来,以减少要渲染的图元个数。 不同的游戏类型和视角方式所采用的可视数据组织方式也有不同。如果是室内游戏,有着很明显的墙壁分界的话,大都采用binary space tree的方式去组织。最典型的是id software的《quake》系列。quadtree和octree大部分是使用在室外场景。如果场景的高度起伏不明显,在高度处理上不需要过多逻辑判断的话,一般都会采用quadtree,反之就采用了octree。目前国内大部分的MMORPG都是采用octree及其变体去组织场景的。 因为Gamebryo引擎的scene graph本身就是采取一种“父子兄弟层次架构”的方式去组织rendered object。即每个rendered object之间,存在着父子关系,或者兄弟关系。对某一个rendered object进行一些坐标变换操作,比如平移的时候。该rendered object在scene graph中的所有作为它的“孩子”的rendered object也会随之平移。而恰好BVH也是基于这种层次架构思想的。所以Gamebryo引擎缺省的可视数据组织方式,就是采用了BVH的方式。 在2007年上半年,可能是对Gamebryo引擎的工作机制不熟悉的原因;也可能是美术部门对MMORPG,尤其是对《World of Warcraft》的偏好,他们一直希望程序能够提供一个类似于《WOW》那样子的地图编辑器。希望能够完全脱离3DS MAX或者maya。直接在编辑器里面“刷地表摆房子”。为此甚至和程序组之间产生龃龉。事后看来,只能说当时大家都处于对Gamebryo引擎能做什么都不熟悉的阶段。抛开一些主观的情感,单从纯技术的角度来说,“类似于《WOW》那样子刷地表摆房子”和《勇士online》的横版视角模式是格格不入的。因为视角的原因,为了减少数据量,横版游戏的制作都是大有取巧之处的。以《勇士online》为例,场景中的远景处,是采用一张2D图片作为背景——因为离镜头很远,看起来基本上都是静态的。近景处,在怎么移动镜头也不可见的地方,不需要建模。也就是说,很多在游戏中看来的房子,树木等等,建模建好后,在导出数据时都是掀掉一半的。而这种方式又怎能直接套用到《WOW》那种全视角的地图呢,万一,玩家把镜头转到房子后面时怎么办?岂不是穿帮了。而且横版游戏的每个关卡,或者说子场景,都不是很大,完全可以用3DS MAX制作好之后再导出的。 经过了一系列的争执,反复,误解和协商之后,最终《勇士online》还是直接使用了3DS MAX进行场景编辑。然后导出Gamebryo引擎能使用的数据格式。所谓的第一种的纯客户端的对可视数据进行组织管理,就正式确定下来了。而在客户端和服务端共同使用的地形数据的组织和管理。客户端和服务端的意见相左。 所谓地形数据的组织,简单来说,可以理解为地形的高度信息和类型的集合。即地面上某一点,或者说,某一块地表,它的高度是多少;它是一块怎样的地面,是沼泽,还是平地?地形数据的管理就涉及到在这些地形上活动的游戏对象,如何去根据地形信息去设置自己的行为表现——比如从平地走到一个陷阱,怎样掉下去?走到泥泞地时,怎样减少移动速度。客户端如何表现?服务端如何判定?等等。 作为客户端这边的意见,CQ的想法是倾向于使用一套“octree加地表高度数据”的方式去组织和管理地形数据。在这个方案中,度量场景的坐标系,是基于浮点数的三维坐标系,可以认为是一个连续的坐标系。所有游戏对象的位置坐标,攻击判定范围,都是基于这个三维坐标系的考虑。因为是三维的坐标系,所以在处理角色对象的位置信息时,还可以处理垂直方向的高度信息。这点在攻击判定时显得更为真实:假如某个角色A,在高处,或者跳起来,向另一个角色B,释放了一个飞行道具C。C在角色B的头顶上飞过。C在B的头顶上飞过时,虽然在客户端Gamebryo引擎自身的collision system能正确地算出C没击中B,但在服务端,因为是使用这个客服公用的地形数据,如果不能处理垂直方向的高度信息的话,则会认为C击中B了,因为此时的C,它的水平面上的xy坐标,和B是一样的。 引入octree的原因主要就是意图降低对象之间交互计算的次数。octree的原理就是将空间分为8的整数次方个等大小的立体区域。根据当前某玩家在场景中的位置,快速地拣选出玩家在场景中那个部分,然后就只选取该部分的其他游戏对象,比如NPC,或者monster与玩家进行交互。处于玩家不在部分的monster或NPC就不必进行交互处理。这样子就大为降低了交互次数,减低了服务端CPU计算的压力。 至于引入地表高度数据,是为了在游戏中能准确地设置玩家,NPC,monster的位置。因为在游戏中,玩家在移动的或者站立的时候,双脚必须要紧贴地面的——否则就腾空而起穿帮了。要紧贴地面的方法有二,一种就是在游戏运行时,实时地根据玩家当前的xy位置,即水平面的坐标值,然后计算出地面在该水平面坐标值下的高度值Z,然后把这个z值设置给玩家,但显然,要精确地计算出这坐标值是需要通过射线和三角形求交点的算法。在运行期去计算是很耗时的,更不用说同时会有很多个玩家或者monster在同一个场景中。所以我们采用了第二种方法,就是预先用工具算地表的高度信息存储在外部文件中,游戏运行前直接把高度信息载入到内存,在运行时某一点的地面的高度值,可以直接通过查表获取。这样子的效率,是在运行时实时计算地面高度值不可同日而语的。 CQ大约在2007年4月份左右做出了第一个基于octree的示例模型。但在和服务端沟通时,服务端主程hy提出了不同的意见。出于经验和对性能效率的考虑。hy不赞成使用三维浮点数坐标系。他更倾向于使用离散的二维网格坐标。也就是说把一个三维场景的地面,划分为若干个格子。每一个格子存储有该格子所对应的地面的高度。在服务端的游戏世界中,角色的水平面上的位置坐标值,就已经不是一个一个具体的浮点数实际坐标值,而是离散的格子编号索引值。在客户端中,某玩家在世界中某个位置(x,y,z),在服务端就变成了它在第(i,j)格,它的高度位置值是这个(i,j)格所存储的地面高度。游戏对象之间的交互,尤其是玩家和monster之间的交互,更多地是由monster所挂接的AI脚本,去控制交互与否。 两种方案各有优劣,客户端的方案的优点是精确,并且能够处理高度存在差异时的情况。服务端的方案优点是实现起来较迅速,并且在交互判定上速度较快,但不足之处就是交互判定较粗糙,对于那些水平面上坐标相等,就是高度不相等的对象,无法直接进行高度方向的判断。如果要做这方面的判定的话,就还得额外想别的办法。 在一番争论之后,在服务端的坚持下,最终还是采用了服务端方案作为对地形数据组织管理的方案。要使用这一个方案,第一个任务就是需要实现一个计算地表高度并将其存储在文件中的工具。

集え!勇士の名の下に --《勇士online》研发回忆录 2

本文是用第一人称记载的真人真事,里面涉及到了大量他人的名称等信息,所以为了避免不必要的纠纷,谢绝转载 在2007年的上半年,整个开发组对于游戏要做成什么类型,依然是处于“摸着石头过河”的阶段。其实当时,开发团队内部成员,就连什么是“格斗游戏”这一概念都还很懵懂。那时候,就是各有各的理解。这也反映出中国游戏业确确实实处于很原始的初级阶段,就连术语都没完全统一。有人把一直所说的“格斗游戏”,理解为英文术语的“combat game”。而有人则理解为“action game”。乍一看来,两者都是即时性的打打杀杀。但在具体实现上,对游戏画面的表现上大相径庭。 按英文直译,combata game就是所谓的“格斗游戏”,通常都是一对一单挑的模式。强调细微入至的攻击判定;强调动作招式的细节和表现;无论是攻击者还是受击者都有丰富的攻击受击表现。代表作有《street fighter》,《King of Fighter》,《Tekken》等。比如下图就是: 姑且不讨论street fighter 4有没有引入物理引擎去实时计算动作,就单看表现,上下两图Zangief都是处于受对方腿击的状态。第一个图是Sakura的冲天踢,这时候Zangief是处于被踹飞的状态,第二个图则是Sakura跳跃攻击踢中zangief的头部,这时候Zangief的受击表现是向下扑倒。如果Sakura是采用攻击下盘的扫堂腿,则Zangief的表现就应该是摔个仰八叉。而如果攻击者不是Sakura,而是有灼焰脚招式的feilong,那么受击者的被击打表现就应该是被烧焦了: 而action game更多被翻译为“动作游戏”。典型的就是《Prince of Persia》。相对于combat game,动作游戏的动作招式较为粗放单调,攻击判定要简单得多;仿真度没combat game那么高。而且更多是用在一些主角一对多的通关类的游戏——马踏连营一扫一大片的酣畅淋漓的感觉是动作游戏的最核心体验。比如,攻击方用“A招式”攻击受击方。基本上这个“A招式”是不分“打到受击方的上半身”或者是“打到受击方的下半身”的。在所谓的“搓招”,即“通过不同的按键组合释放出不同的招式”的要求上,大部分动作游戏不作要求。而“搓招”的熟练与否,却是鉴别一个combat game玩家,是骨灰级玩家还是菜鸟玩家的重要标志。 除了到底要做成“combat game”,还是“action game”尚未完全统一之外,对于这个游戏要做成一个纯竞技竞速休闲类的游戏,还是要做成一个大型的RPG,在整个开发团队内部也并未完全统一思想。2007年的中国网络游戏市场,正是MMORPG泛滥之际。托“一人有代码就全国有代码”之福,短短三四年,市场上就充斥着良莠不齐的RPG游戏,各运营商的推广运营手段层次不穷无所不用其极,市场竞争相当残酷。一个RPG从研发到运营都是要重金投入的。而且越往后推广费用就越夸张。9you是靠代理了《劲舞团》起家。作为一种音乐类休闲游戏,在当时可谓是异军突起。正因为这个原因,王先生的“不趟RPG这趟浑水”,在当时就作为整个公司战略思想。所有开发组,都围绕着“做休闲类游戏”这一指导思想去做。厦门这边也不例外。另一个项目《劲爆滑板》就是一个竞速类游戏。而我们这边的项目,也起了个暂定名了,叫《格斗飞人》 当时主策的想法还是偏向于做成一款RPG,整个世界观的设定都是按RPG的方式去走的。但问题就在于这和整个公司的战略思想相悖,能不能通过高层那关都是个问题。所以做成一个竞技场挑战模式的休闲游戏;和做成一个做任务角色养成的RPG两种截然不同的想法,同时存在。并且左右摇摆不定。整个游戏世界观也在不断的折腾反复。而程序组这边没法去等策划案最终敲定了,才能动手去做。而是要对一些确定的东西要先行一步了。在反复摇摆之后,最终定下了“场景及视角是横版卷轴”这一模式。 现在回头看来,当时定的“横版卷轴”这一模式,意义深远。因为在后来这款游戏处于夭折边缘而可能要改成什么别的游戏的艰苦阶段。这一模式始终都没变换过,而坚持这种既定的模式,无论是在程序实现上,还是在美术资源的制作上,都避免了大量的无用功。试想,如果当时不坚持这种模式,采用45度俯视角,或者就是全视角的方式去制作的话,所有的代码逻辑就得重写,所有的在横版模式上可以取巧制作的美术资源,尤其是场景和特效,就不得不重做。带来大量的人力浪费不说,更重要是“冷了兄弟们的心”,对士气打击是很严重的。时至今日,一些拍拍脑袋就要求这样要求那样的老板们,在决策时,是否真的从各个角度去权衡过呢? 约在07年4月底定下来场景视角模式之后,程序这边就开始着手编码了。此时我入职一月多。对Gamebryo-PhysX能做到什么程度已经有一个较清晰的了解。正如前面所说,Gamebryo-PhysX的功能实在是太弱了,根本无法去实现逼真的打斗效果模拟。所以开发组决定,暂缓使用物理引擎,我也先做其他客户端前期开发工作。和CQ一道,都先把Gamebryo系统摸熟再说。 约在2007年5月底左右,原来的客户端程序yuanhui离职,CQ接手yuanhui原来的客户端框架搭建工作。囿于yuanhui的经验和专长等原因,对他写的那个框架,开发组的评价并不是太高,对此的评语是“像用做一个2D游戏的思路去设计的”。因为写好的代码并不算多,CQ决定重写一个客户端框架。第一个短期的目标,就是作一个登录场景和一个新手村场景,要让玩家能登录到新手村中去漫游。2007年的3D游戏,玩家可以换装已经是很普遍的功能了。所以我先行做换装功能的相关的基础搭建,以及实现一个换装编辑器。以供策划和美术使用。 换装功能不难,我在tencent时已经做过类似的工作。只不过在tencent使用的是封装了Gamebryo的gfx框架。而这次是直接用Gamebryo编写而已。方法很简单:假如视为某个部件模型,比如穿着在躯干上的铠甲为A,视整个角色模型为E,原来穿的铠甲部件模型为OA。首先取得A的蒙皮数据S,根据S取得附着在其上面的骨骼数据B。然后遍历去整个E所有的和B同名的骨骼数据EB,把EB设置到S中。最后就把OA那一部分的模型从E中卸载掉,再把A挂接到原来的OA所挂接的地方就可以了。 如果用烧伤植皮手术去理解这个换装过程就很直观,首先把新皮肤和肌体做一个排斥测试,如果新皮肤和肌体不发生排斥反应(也即把EB能设置到S中),那么就把烧伤的皮肤给做个清创手术(OA从E中卸载掉),把新皮肤植上去(挂接A到E上)。换一个部件是如此,换多个部件实质上就是重复上述的过程。如果把每一个部件的换装数据描述组织成文件,用一个外部工具去生成的话。就成了一个换装编辑器的工作: 换装功能的客户端表现层实现后,对应的相关网络逻辑也就不难描述了。在服务端,每一个玩家都有一个称为“外观包”的数据结构。这个数据结构描述了当前组成每一个玩家角色模型的各种部件的ID值。当A玩家换装后,就把换装的部位的新ID值发给服务器,服务器则再广播给区域内其他玩家。其他玩家的客户端代码将会同步地显示出换装后的A玩家的最新外观。 约在07年6月份左右客户端就已经完成了换装功能,算是先行一步。相关的换装服务端逻辑安排在较后的一段时间才实现。这时,在场景的实现和数据组织方式上,程序部和美术部之间,还有客户端程序和服务端程序之间,发生了争论。

集え!勇士の名の下に --《勇士online》研发回忆录 1

本文是用第一人称记载的真人真事,里面涉及到了大量他人的名称等信息,所以为了避免不必要的纠纷,谢绝转载 一眨眼,《勇士online》团队解散已经将近两年。研发《勇士online》这个我完整地参与了从立项到上线运营整个过程的网络游戏,在风光旖旎的厦门,我度过了和大学四年一样长的时间。作为人生中一个重要的阶段,在这四年中留下了很多值得回忆铭记的人和事。在往事逐渐随风飘去之前。我决定汇集脑海中留下深刻印象的这四年中的人和事。此记。 2006年岁末,带着在深圳tencent不甚愉快的半年工作经历,我回到了家乡,计划在家乡休息学习一段时间,整理归纳工作三年来的一些业务技术心得。主要是翻译在tencent使用过的Gamebryo2.1引擎帮助文档,还有就是剖析一个小型的DirectX8框架——dxframework。通过研究这个小框架我进一步地理解了3D图形渲染的基础理论,甚至还写了好几万字的剖析文档。但可惜计算机的发展日新月异,较之Direct3D8,现今的Direct3D11时代,完全的可编程管线已经取代了固定渲染管线,3D渲染编程方式上发生了翻天覆地的变化。所以dxframework框架也早已被管理员放弃了维护。 约在2006年11月初左右,认识于珠海kingsoft,曾共事于onwind的CQ也刚赋闲。在CQ赋闲之前,我曾联系到之前也一起在onwind共事的jokiting,那时jokiting已经在厦门9you上班了,于是我电话建议CQ去厦门9you试一试。最后CQ约在2006年11月中下旬左右入职了厦门9you。我则留在家中继续学习。转过2007年1月左右,再次联系CQ,得知他已加入一个项目了。新项目是一个“格斗游戏”,用的是Gamebryo引擎。我当时也打算继续做3D客户端网游,加之又对Gamebryo有过些编程经验,所以就萌生了自05年初离开后,重返这个小岛的想法。于是就给CQ发简历和demo小程序,和他打过招呼,年后就回厦门看看。 2007年的农历新年来得特别迟,2月17日了才除夕。所以拖到3月1日我才动身去厦门。下了火车,想起两年前和送自己回家的CQ和另一位同事liaoweiqing说的:“我还会回来的”,感慨万千。厦门火车站依旧是那么狭小拥挤,虽说9U所在的软件园一期其实我在05年初去过一次的,但还是打了个车直接到了软件园一期。在公司电梯口见到了正准备下来接我的CQ。CQ人样依旧,只是比两年前要清瘦了一些,可能是因为坚持长跑的缘故。寒暄了一阵,他帮我把行李暂放在办公室内。然后就继续上班了,我就按照流程填表格面试。 首先进行技术面试的是另一个《劲爆滑板》项目组的主程序xzy。他针对我简历中所罗列的工作经验和技术专长做了一些提问,我的回答他觉得OK,然后就是由两位公司两位高管,seasonwind和sabina面试我。 Sabina也可谓是一个传奇人物。她毕业于沈阳音乐学院。作为中国游戏业界最早的一批从业人员,她是在厦门新瑞狮起步的,和onwind的老总lls,zechuan,以及一些我认识于珠海kingsoft的资深美术,如yes,xiaohua,还有一位曾共事于onwind的jiaoqiang。都是从新瑞狮出来的同一批战友。后来她被调任到南京新瑞狮担任管理工作。新瑞狮解散后,在业内几经沉浮波折,最后在9U的老总王先生的投资下,在厦门成立了分公司,主打研发。Sabina作为常驻厦门的分公司主管,还兼任了当时尚未正式命名的CQ所在的项目的“格斗游戏”制作人。 sabina问我,为什么想来厦门9you。我回答:环境好,心情能静下来,有熟人,最关键的是,之前在其他公司参与的项目,虽然有上线的,但要么是开了个头,要么是结了个尾,而我想从头到尾完整地完成一款游戏。sabina对我的这个想法表示赞同。其他的问题也就和一般的面试差不多,谈妥了薪金之后,就等入职通知。 按CQ原来的设想,希望在游戏中引入物理引擎,以增强游戏的真实感。最简单地说,就是希望通过物理引擎,根据角色受打击的位置,作用力的大小和方向,去实时地计算出角色应有的表现动作。比如,当角色处于待机动作时,用同样的作用力,踢向他的躯干和腿部,所需要播放的动作应是不同的。踢向躯干,可能会使受力角色向后飞出去。而踢向腿部,则可能让受力角色原地摔倒。如果在采用传统的3D动画的实现方式,就要在建模阶段预先建好各种可能的受击表现——这显然工作量实在是太大了。而且预编辑动画的仿真度是远远不够的。所以对“格斗游戏”来说,引入物理引擎是必要的。在项目开始之初,既然大家的概念都是做一个“格斗游戏”,那么引入物理引擎是一个很自然的选择,因为厦门9U这边的人事招聘都需要走一遍“通知上海总部,然后由总部回文确认”的流程,所以在等待从2007年3月1日到15日这走流程的两周时间中。我在黄厝村塔头社,CQ住处旁边租了一个小房子,开始着力学习PhysX。 2007年3月15日,我正式入职,当时的团队还不是很多人,当时的策划团队有四人,主策划是chenliangsen,执行策划是做数值的zhongjinde,文案和系统策划绯红和guojian。“绯红”是男的,只是他的RTX签名一直都是“绯红”,所以大家都叫他“绯红”,或者“小红”。最搞笑的是数值策划zhongjinde,因为他的工作是数值设定为主,工作环境就是excel。所以他效法人们对孔丘的尊称,给自己的RTX签名起了个雅称“表子”,这事情一时传为笑谈。无独有偶的是后来有一位新数值策划加入,也因为精通excel操作,被大家尊称为“表哥”。 当时的美术团队是主美shentao,其他美术成员则有linxi,zwf,小刘忙,老俞,fb,xuchaoyuan,老苗,xiaoxing,以及被大家合称为“ZP”的两个女生:zl和pf,等等。xiaoxing是和我同一天入职的。其中linxi主要负责3D角色建模,yudefang和zwf则是3D场景,fb负责3D角色动作。xuchaoyuan则是原画。老苗,zl,pf做2D界面为主。xuchaoyuan的学历不高,但他艺术天赋过人。后来在业界他也闯出一点名气,此乃后话。 客户端程序有我,CQ,djj,zhw,yuanhui四人。服务端程序有后来调任为项目主程的hy,zhangronggan,xiexiaoyong,以及rojane四人。还有两个女实习生程序,一个是厦大的硕士生xuxu,还有一个则是解放军通信工程学院毕业的CQ的同乡wangxin。一开始的分工是CQ去学习gamebryo引擎,djj去做UI界面,zhw则做网络通信和上层逻辑相关的东西,yuanhui则搭建整个游戏客户端的初始框架。而我就负责学习PhysX引擎,研究在游戏中引入物理系统的可行性。 PhysX引擎开发得比较早,但Gamebryo引擎则到2.3版本才开始对其加入支持。并且仅支持PhysX2.6.2版本。现在回过头来看,我认为Gamebryo2.3对PhysX封装得很不完善,尤其是偶人(rag-doll)动画系统做得严重不足。当时的Gamebryo-PhysX系统无法对物理动画和预编辑动画作完美混合。也就是说,当播放物理动画时,预编辑的3D动画就要停止播放,等物理运算结束之后,3D预编辑动画才重新接管角色的动画播放。这个还不算是最严重的缺陷。更为严重的问题是,当时的Gamebryo-PhysX系统所支持的偶人动画角色,仅能作为“受力方”。即物理引擎只能被动地接受外界的作用力输入,去实时计算出受力时应作的表现,而无法给把角色本身视为“施力方”——当时的Gamebryo 3DS Max插件,也仅仅是做到了给角色模型绑定了偶人动画骨骼,而无法给角色模型本身加入一个施力点。这样子的话就会带来问题。因为在格斗游戏中,受力方所受到的作用力,是由施力方依据不同的动作发出的。如果不能在角色模型中编辑指定的话,就根本无法产生逼真的击打效果。就好比下面的示意图 女剑士使用了一招月星闪。如果使用物理引擎去实时计算受击方的受击动作的话,那么,女剑士的劈剑方向和力度都将会决定了受击方的表现。忽略作用力大小不考虑,单考虑施力方向:如果女剑士是从上往下劈斩的话,那么对方的受击部位应该是上半身,那这时候对方的表现应该是两腿一软被砍倒地。而如果女剑士是从下往上挑斩的话,那么对方的受击部位则应该是下半身,这时候对方的表现应该是被挑飞到空中。如果要考虑作用力大小的话(试考虑街头霸王中的“轻拳”,“中拳”,“重拳”之分),情况就更复杂——不仅是受击方的受击表现,施力方的发招表现也有不同,比如一般来说,轻拳出招力小势轻,收招快,能快速转防守状态。重拳出招力大势沉,收招慢,留有破绽多……。凡此种种,非常复杂。很明显,要完成上述的功能,最起码的,施力方的攻击动作的相关物理参数,如施力方向和大小,要可编辑才行。而彼时的Gamebryo-PhysX系统应付不了这个最起码的需求。所以,在当时并没有完全确定我们要做的游戏到底是什么样子的情况下,不能钻牛角尖,必须走另一条路子。

深度探索DxFramework 10-4

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com #第10章 进入三维的世界 4 10.5.4.变换包围盒 变换包围盒,除了变换包围盒顶点的坐标之外,还得变换包围盒的面的法线向量。如果有一个转换矩阵M对平面的各点进行变换,那么这个平面的法线向量的正确变换矩阵应该G为对各点进行变换的矩阵的逆转置矩阵(inverse-transpose matrix)。所以有下列代码: void C_BoundingBox::TransformBoundingBox(D3DXMATRIX* p_TransformMatrix) { D3DXMATRIX tempMatrix; //取得转换矩阵的逆矩阵,然后再将其转置 D3DXMatrixInverse(&tempMatrix, NULL, p_TransformMatrix); D3DXMatrixTranspose(&tempMatrix, &tempMatrix); int i; //变换包围盒的各个角顶点 for (i = 0; i < 8; i++) { D3DXVec3TransformCoord(&point[i], &point[i], p_TransformMatrix); } //实质上就是对平面的法线进行变换 for (i = 0; i < 6; i++) { D3DXPlaneTransform(&plane[i], &plane[i], &tempMatrix); } } 10.6 观察世界的窗口,摄像机类 10.6.1 视口设置 10.6.2 雾化效果 雾化是指在三维图形程序中模拟真实世界中的云雾,水蒸汽,灰尘等大气效果。从生活经验我们可以得知,在雾气比较严重的地方,距离观察点近的地方物体比较清晰,远处的物体则随着它离观察点的距离的增加而变得越来越模糊。通过使用雾化效果将能增加三维场景的真实感。在Direct3D中,雾化效果是根据当前景物的颜色和雾的颜色,与随着物体和观察点距离增大而衰减的混合因子(fog blending factor)混合实现的。这里的术语“观察点”,就是当前摄像机在世界坐标中的位置,或者说,观察坐标系的原点在世界坐标系下的位置。当距离景物距离观察点越远的时候,混合因子就越小,场景中物体的颜色就越小,而雾的颜色就越大,景物就会变得越模糊。Direct3D渲染流水线计算雾化效果的公式如下: COLOR = f*COLORscene + (1-f)*COLORfog 其中COLOR表示最终经过了雾化处理的颜色。COLORscene表示景物原有的颜,色。COLORfog表示雾的颜色,这颜色是由应用程序自定义的。f表示雾化的混合因子。Direct3D有两种雾化处理方法:顶点雾化(vertex fogging)和像素雾化(pixel fogging)。顶点雾化在渲染流水线中的顶点变换和光照阶段实现。基本原理是根据多边形的每个顶点与观察点的距离计算出每个顶点的雾化程度。然后根据计算结果,针对每个多边形进行插值计算。像素雾化则在像素绘制阶段实现,它通过判断每个像素对于观察点的深度值进行雾化效果计算。像素雾化与顶点雾化相比,计算结果更为精确,但也将消耗更多的系统性能。 由上面的雾化计算公式可以看到:雾化效果是物体可见程度的反映。雾化效果计算公式算出的结果值越小就表示景物的可见度就越低。而最终的计算结果 除了取决于景物颜色和雾的颜色之外,还取决于雾化混合因子的取值。Direct3D提供了一些枚举值表示几种雾化因子的计算方式。如下:

深度探索DxFramework 10-3

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com 第10章 进入三维的世界 3 10.5 无规矩不成方圆,C_BoundingBox类 10.5.1.包围盒的种类 DXFrameWork的包围盒是一个凸六面体,由6个面,8个顶点,12条边组成的闭合体。因此,DXFrameWork也使用了相关的数据结构来描述包围盒。代码如下: class C_BoundingBox { ... public: D3DXVECTOR3 point[8]; //8个顶点 D3DXPLANE plane[6]; //6个表面 } 8个顶点和6个面和编号的示意图如下: ###10.5.2.包围盒的数据结构和初始化设置 //根据给定的图元计算出它的包围盒 void C_BoundingBox::FindBoundingBox(C_Primitive* p_Primitive) { unsigned i; //获取 DWORD numVertices = p_Primitive->numVertices; // Lock the vertex buffer C_VertexManipulator vertexMemory; p_Primitive->LockVertexBuffer(&vertexMemory); // Find the mins and maxs for each X Y and Z axis D3DXVECTOR3 position; float minX; float minY; float minZ; float maxX; float maxY; float maxZ; position = vertexMemory[0].

深度探索DxFramework 10-2

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com 第10章 进入三维的世界 2 10.4 Direct3D的渲染流水线 10.4.1.渲染流水线的体系架构和速度 渲染流水线的主要功能就是,在给定如三维的物体、摄像机、光源、光照模式、纹理等条件下,如何在显示器屏幕或者是其他输出设备中生成绘制一幅二维的图像。和现实生活中的流水线,比如工厂的装配线类似,渲染流水线也可以分为多个“阶段”(stage)。就是指在整个渲染过程中,执行了其中一部分功能的一个过程。如果各个阶段是以并行的方式运行的话,那么流水线整体的快慢程度是由流水线中最慢的那个阶段所决定的。在实时计算机图形学中,渲染流水线可以在概念上将其大约分成三个阶段:应用程序阶段、几何阶段和光栅化阶段。每个阶段有可以看成是一个比较细的流水线,可以由其他阶段组成,而且这些个子阶段有些是可以并行运行的,如下图: 最慢的流水线阶段决定了绘制速度,即图像更新速度,这个速度一般用fps(frame per second)表示。也就是每秒绘制的图像数。在这里请注意,在渲染流水线中,绘制一帧所需的时间,不能简单地对数据通过各阶段所花费的时间做一个简单的相加。而是只需要找到最慢的阶段的速度,就是能得到整个阶段的绘制速度。这也就是渲染流水线的特性。在此举一个例子来说明: 假定某个输出设备(比如是显示器)的绘制速度最高是60fps,整个流水线中最费时的阶段为50毫秒每帧。那么每绘制一帧。在先不考虑输出设备的情况下,流水线能提供的最高渲染速度为1/0.05=50fps。接着可以把这个值调整为输出设备的频率。输出设备最高的可以是60fps,那么也就是说,输出设备的所能调整到的、小于或者等于50fps的绘制速度,就是整个流水线的最大绘制速度。 如其名字,应用程序阶段是由使用渲染流水线的应用程序来驱动的。可以通过软件实现之。比如在这一阶段,可以计算物体的碰撞检测,力反馈等物理仿真运算。(在本书写作之际,物理加速卡已经开始进入实际应用阶段,在往后发展中这部分阶段也可以用硬件完成。)接着的就是几何阶段,在这阶段中实现对三维物体的变换、投影、光照等处理,可以用软件或者硬件实现。这阶段的主要内容是计算绘制的内容、如何绘制、何处绘制等等。最后,光栅化阶段是利用了前面阶段产生的数据进行图像绘制。 10.4.2.渲染流水线的应用程序阶段 应用程序阶段在几何阶段之前。在这一阶段的功能就是把要渲染的几何体输入到几何阶段。在应用程序阶段,通常实现了物体之间的碰撞检测。此外,应用程序也是检查其他输入信息的地方,比如鼠标,键盘,各种各样的虚拟现实设备的输入信息。在这一阶段中还实现了一些不在其他阶段执行的计算。 10.4.3.渲染流水线的几何阶段 几何阶段主要负责了大部分的顶点和几何操作。几何阶段可以划分成如下几个子阶段。在一些情况下。一些子阶段是可以并行运行的。几何阶段可以进一步划分成如下图的几个子阶段。 在极端情况下,渲染流水线中所有的阶段都可以用软件来实现。但是值得注意的是,几何阶段执行的是计算量非常高的任务。正因为如此,现代显卡把几何阶段所需完成的很多功能都用硬件实现了。 10.4.4.模型和观察变换 在往屏幕显示的物体过程中,表征物体的模型往往是需要变换到一些不同的坐标系统中去。最开始物体是处于它自身的模型空间里,这个模型可以认为它没有进行任何的变换。对模型进行模型变换,便可指定它在所处场景中的位置(position)和朝向(orientation)。同一模型可以和多个模型变换联系在一起,允许同一个模型有多个副本。这样子可以使得同一模型在同一场景中有多个不同的位置朝向。而不需要对这模型做复制操作。模型变换的变换对象是模型的顶点和法线。如果将模型应用模型变换之后,所有的模型都将处于同一世界中。这时候可称为此模型处于世界空间或者是世界坐标中。世界空间是唯一的,所有的模型经模型变换后都处于同一个世界。 在实际绘制中,只有能被摄像机观察到的模型才进行绘制。相机在世界坐标中有位置和三个相互正交的朝向,用来对相机自身进行定位。为了便于裁剪和投影,在完成模型变换之后,必须对所有的模型进行变换。变换到以相机坐标为原点。以其三个相互正交的朝向方向为坐标轴方向的空间中去,这个空间便称为观察空间。 10.4.5.光照和着色计算 为了使得渲染结果更加真实,可以给场景配上一个或多个光源。同时还可以选择是否使用灯光影响几何体的外观。几何模型可以给其每个顶点指定相关联的颜色,或者是覆盖上一副图像(就是所谓的纹理,后面章节将详细介绍)。对于存在着灯光的场景来说,可以用光照方程计算模型上每一个顶点的颜色,这个方程近似地模拟了光子和物体表面之间的实际作用。在现实世界中,光源发出光子,然后被表面反射或者是吸收。在实时计算机图形学中,是没有过多的时间来模拟这种现象的。因此,这些简单的光照方程并没有包含着非常真实的反射和阴影。此外,模型在图形上通常是以三角形来表示。目前大部分的图形硬件也都是采用三角形为基本渲染图元(primitive)。物体表面每个顶点的颜色可由光源及其自身的顶点位置和法线、顶点所在的材质等属性来计算。当在屏幕中绘制三角形的时候,可以通过对三角形的顶点进行插值运算。为了模拟更加高级真实的光照效果,可以采用像素着色技术。 一般地,光照计算是在世界空间中进行的。但是如果在对物体模型做观察变换的同时也把光源变换到观察空间中去的话,其光照效果将会是一样的。因为这时候,光源,摄像机,和物体模型之间的相对位置依然保持不变。 10.4.6.投影变换 完成光照处理之后,渲染流水线开始进行投影,目的就是为了将视体变换成为一个单位立方体。这个立方体的对角顶点分别为(-1,-1,-1)和(1,1,1)。通常地也称为单位立方体为规范化视体(canonical view volume)。现在主要有两种投影方法:正交投影(orthogonal projection)和透视投影(perspective projection)。 正交投影的视截体通常是一个矩形的盒子,正交投影就是把这个视体变换成为单位立方体。正投影的主要特性就是平行线在变换之后彼此之间仍然能保持平行。这种变换是平移和缩放的组合。相比之下,透视投影要复杂一些。在透视投影中,物体的投影后大小遵循“近大远小”的原则。即物体离相机越远,投影之后将会变得越小;平行线在地平线处可以相交。从几何上而言,透视投影的视截体可以称为视锥,即一个以矩形为底面的,顶部被截掉的金字塔。同样的视锥也可以通过透视投影变换成一个单位立方体。经过投影变换后,图像的Z坐标信息将不存在(存储在Z缓冲区中了),这种投影变换将模型从三维投影到二维。 10.4.7.裁剪 只有当模型部分或者完全地存在于视体内部的时候,才需要将其发送到光栅化阶段绘制出来。如果一个图元完全地位于一个视截体内部的时候,可以直接将其送入下一阶段。完全不在视截体内部的模型,可以完全不将其送入下一阶段。对那些只有部分在视截体内部的模型,则需要对其进行裁剪。例如:当一条线段的一个端点在视截体内部而另一端点在外部的时候,就需要根据视截体对该线段进行裁剪。位于视截体外部的端点将被新的端点所代替,新的端点是线段和视截体的交点。投影变换之后的模型只对单位立方体进行裁剪。在裁剪之前进行观察变换和投影变换的优势就在于可以使得裁剪方法和问题比较一致。除了视截体的6个平面之外,用户可以自定义裁剪平面来进行裁剪操作。 10.4.8.屏幕映射 在视截体内部经过裁剪了的图元才能进入屏幕映射阶段。进入此阶段。坐标仍然三维的,每个图元的x坐标和y坐标都将变换到屏幕坐标系上。屏幕坐标系连同z坐标一起称为窗口坐标系。假定一个窗口里对场景进行了绘制,窗口的左下角坐标为(x1,y1),右上角坐标为(x2,y2)。其中x1<X2,y1<y2。屏幕映射首先进行平移,然后进行缩放。在映射过程中z坐标不受影响。新的x和y坐标系称为屏幕坐标系,与z坐标一起(-1≤z≤1)进入光栅阶段。 10.4.9.光栅化阶段 给定来自几何阶段,经过变换投影之后的顶点、颜色和纹理坐标。光栅阶段的目的就是给每个像素(pixel,picture element的所写)指定正确的颜色,以便于正确绘制整幅图像。这个过程称为光栅化或者是扫描转换,也就是说把屏幕空间中的二维顶点转化为屏幕上的像素。屏幕空间中,每个顶点右一个z值(通常称之为深度值)、一种或者两种颜色、以及一组或者多组纹理坐标。其中纹理坐标将会和顶点或者是屏幕上的像素联系在一起。 在几何阶段中,渲染流水线操作的对象是一些多边形。而在光栅化阶段中,是对像素进行操作。每个像素的信息存储在颜色缓冲器(color buffer)里。颜色缓冲器是一个矩形的颜色序列。每种颜色都含有红色,绿色,蓝色三种分量。 为了避免在绘制时产生撕裂现象,图形系统一般都使用双重缓冲机制。也就是说:渲染流水线首先在后台缓冲区(back buffer)中绘制好要绘制的内容,然后切换到前台缓冲区(front buffer)中。如此不断地绘制,切换。 光栅化阶段还负责可见性问题。这也就是意味着当绘制完整个场景之后,颜色缓冲区将包含从摄像机观察点从可以观察得到的图元。大部分的图形硬件通过Z缓冲器来解决可见性问题。Z缓冲器和颜色缓冲器的大小形状是一样的。每个像素都存储着一个Z值。这个Z值是从摄像机到最近图元的距离。当将某图元绘制成像素的时候,就要把同一像素位置处的图元Z值和同一像素的Z缓冲器的内容进行比较。如果新得到的Z值小于Z缓冲器的Z值,则说明将要绘制的图元与摄像机的距离比原来距离相机最近的图元还要近。这样子,像素的Z值和颜色值就由当前图元所对应的值来更新。反之,则保持不变。 因为Z缓冲区对每次将要绘制在同一像素位置上的图元都要进行比较操作,所以使用了Z缓冲区算法,对于不透明的像素绘制顺序是任意的。对于部分透明的像素而言则不能任意顺序绘制。必须在绘制所有非透明像素之后进行。而且必须从后到前的顺序进行。 在三维场景的绘制中,纹理是一种能够提高真实感的有效技术。简单而言,对某一物体进行纹理贴图就是指把一幅图像贴到该物体表面上。纹理图像可以是一维,二维,或者是三维。其中二维图像最为普遍。目标物体可以是一系列连在一起的三角形,也可能是一些四边形,球体,圆柱体等等。 颜色缓冲区用来存储像素的颜色。Z缓冲区用来存储每个像素的Z值。还能使用其他的缓冲器来产生一些特殊的图像效果。使用alpha通道和颜色缓冲器联系在一起可以存储一个与每个像素相关的不透明值。从而使得图像产生一些半透明的效果。 帧缓冲器(frame buffer)通常包含一个系统中所具有的所有缓冲器。还有一种缓冲器称为累积缓冲器(acceleration buffer)。在这个缓冲器中可以用一组操作符对图像进行累积。例如为了产生运动模糊。可以对一系列表示运动的图像进行累积和平均。图元发送到并通过光栅化阶段后。从相机视点能看到的东西就可以在屏幕中显示出来了。 10.4.10 Direct3D的渲染流水线架构

深度探索DxFramework 10-1

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com 第10章 进入三维的世界 1 10.1.3D预备数学知识和相关代码的分析 10.2 向量,矩阵,四元数 10.3 三维几何工具集 10.3.1 与三维空间的平面相关算法 对于给定的3D点P0和法向量N,过点P0并且和N垂直的平面就可以定义为满足方程N.(P-P0) = 0的集合。(不明白这个定义怎么来的读者,可以回忆一下两向量点积的几何意义)。如下图,平面中任意点与P0的连线都与法线N是垂直的。 如果令法向量N为(A,B,C),点P0为(P0x,P0y,P0z)。则平面的方程为: $ Ax + By + Cz + D = 0 $ ( 此时 $ Ax + By +Cz + D = \overset{\rightharpoonup}{N}\cdot P $ ,$ D = -\overset{\rightharpoonup}{N}\cdot P $ ) 根据平面法线的指向,平面把它所处的空间划分成两个半空间,法线所指向的半空间称为正半空间,也可以称为正侧空间。另一半则称为负半空间,也称为负侧空间。判断某点和某平面的位置关系可以依照上面三个判断式,如下: 判断式 位置关系 Ax+By+Cz+D=0 点(x,y,z)在平面上 Ax+By+Cz+D>0 点(x,y,z)在正半空间 Ax+By+Cz+D<0 点(x,y,z)在负半空间 在此可以举一个例子来说明。如下图: 在图中,平面A由法线N(0,1,0)和点P0(0,5,0)定义。那么,定义式Ax+By+Cz+D=0的D为: D=-N.P0 = -(00+15+0*0) = -5 P3的坐标为(6,5,2),代入方程式Ax+By+Cz+D中,为: 60+51+0*2-5=0 所以P3在平面上。P1的坐标为(0,10,0),代入方程式Ax+By+Cz+D中,为: 00+101+0*0-5=5>0 所以P1在平面的正半空间。P2的坐标为(0,2,0),代入方程式Ax+By+Cz+D中,为: 00+21+0*0-5=-3<0 所以P1在平面的负半空间。tool3d.cpp文件中定义了点和平面的位置关系判断的两个函数,如下: