| 越 的个人资料三无之徒照片日志列表 | 帮助 |
|
|
11月16日 重构 Dream Engine2 资源管理开发完 MMO Demo 之后,开始重构引擎了,首要解决的就是之前 Artists 对资源文件管理复杂的问题,而从这个问题,逐渐引伸出资源管理的问题,最后演变成为一个大规模的重构方案。 首先说明 DE2 之前的资源管理设计,在开发 DE2 初始时,并没有对资源管理进行详细的设计,所以 DE2 的资源管理基本上延用了 DE3D 的方式。具体分为以下几点: 1.资源文件结构。典型的资源有几种,比如纹理、几何数据、动画、材质、场景图节点等等,资源文件都是单独存储成文件的,这样的设计初衷是为了实现更大的灵活性,例如:不同的材质文件可以引用相同的纹理文件,不同的场景图节点描述文件可以引用相同的几何数据或者材质数据等等,但事实上在实际的游戏开发中并没有设想的那样美好,稍后将详细描述实际使用中存在的问题。 2.资源引用关系。资源和资源之间往往存在着引用关系,例如材质使用了一张纹理,场景图节点使用了一个几何数据或者是材质等,在 DE3D 中我们使用了最直观也是最容易想到的方式:资源文件中直接记录所引用资源的相对资源总目录的文件路径。 3.资源对象的管理,由每个资源管理器自己管理。资源对象的查找和生命周期管理也是由各自的资源管理器完成。资源对象是通过引用计数来控制生命周期的,当引用计数为零时,自动释放资源对象。每个资源对象在引用其他资源对象时是直接设置的,比如材质对象中包含了对纹理对象的引用,在材质中直接使用纹理对象指针。 4.异步资源加载,是通过多线程完成的。在多线程中实现资源从磁盘到 RAM,从 RAM 到 Build 资源对象的过程,当异步加载资源时指定资源创建的回调。引擎在资源加载和创建完成时进行回调。 以上基本上就是之前引擎资源管理的设计。那么,这样的设计都有哪些问题?为什么要重构?重构的方案是什么?下面一一叙述。 问题 1.资源文件数量过多。每个资源都是单独的资源文件,这种方式看似灵活,实则管理麻烦。在实际开发中,美术要面对多种类型的资源文件,纹理、几何数据、材质、动画、骨骼、场景图节点,在 DE2 中,资源类型又增加了,美术就更头大。举个例子来说,一个骨骼动画模型在 max 导出时要导出至少 5 个文件,一个骨骼动画节点描述,一个骨骼文件、一个蒙皮几何文件、一个蒙皮节点描述文件,和至少一个动画文件。在开发中需要将导出的这些资源导出到引擎指定的目录下,并且提交到服务器中,如果漏掉任意一个,都导致资源不能正常加载。当资源越来越多时,资源文件数量爆炸性增长,文件管理上的复杂性就可想而知了。这对比较感性的美术们来说,是很痛苦的事情。即使是程序员的我,在实际的使用过程中,也感觉到非常麻烦。另外,之前设计方案中的资源文件复用的灵活性,其实在开发中也体现不明显,比如为了实现纹理的复用性,美术需要将几张贴图合并为一张,但是有这几张贴图可能属于完全不同的关卡,甚至不可能同时出现在一个场景中,这样导致渲染物体的显存资源浪费,还有像复用几何文件这种情况几乎就没有发生过。其实资源的复用完全可以通过编辑器中对象级别的复用来实现,这样资源文件级别的复用在开发中显得微不足道,甚至有时因为考虑资源复用增加了美术的资源管理的复杂度。 2.记录引用资源路径的方式不灵活。这种直接记录资源路径的方式,导致资源文件路径发生改变时,引用这个资源的资源很难做出相应的调整,进而无法加载改变路径后的资源。尽管我们可以要求美术在一开始就设定好资源存储路径,但是依然不能防止这种情况的发生,而且有时侯处于项目管理的考虑,资源路径的改变是很有必要的。虽然我用了一些实现技巧,但还是没有彻底解决。 3.不能预读引用的资源信息。由于在资源文件的数据中记录了引用的资源文件路径,只能在解析资源数据过程中(或者称之为 Build 资源对象的过程中)才能知道引用的是哪个资源,这种方式,外部无法控制资源的加载顺序。此外,这种方式还造成了异步加载资源时,外部无法设置引用资源的加载方式的问题(相对于当前的加载线程是同步还是异步?)。 4.没有实现统一资源管理,缺少统一的资源对象信息描述。由于每种类型的资源由自己的管理器管理,导致资源不能用统一的方式访问和操作。 5.多线程异步加载的实现复杂。比如要求在异步读取资源时,必须指定回调函数,接口上过于严格,应用程序显示的知道了多线程资源加载的实现机制,没有做到屏蔽细节,而且回调还打乱了游戏的代码流程,需要游戏客户端单独处理回调后的流程,导致客户端实现复杂。还有些异步加载资源时的特殊情况没有处理,例如当异步读取某个资源时,主线程同步的需要这个资源对象等等。另外,由于在多线程中创建资源对象,导致资源的管理必须是线程安全,线程同步过多,降低了多线程并行的能力,这一点在 MMO 开发中表现的非常明显。 Dream Engine2 资源管理解决方案 根据上述讨论,制定出新的资源管理解决方案。 目标 1.降低资源文件管理复杂度,减少资源文件数量。 2.提供灵活的资源文件管理,当资源路径发生变化时,引擎能自动识别变化并作出相应的调整。 3.提供灵活的资源引用机制,快速获取引用资源的信息,以及资源被引用的信息。 4.实现统一的资源管理,统一资源信息描述。 5.降低多线程资源加载复杂性,提高多线程加载性能,降低线程同步次数,对游戏层屏蔽实现细节,实现灵活的加载机制。 实现 1.使用统一资源数据存储文件:资源包。资源包内部可以容纳任何资源数据,多个资源可存在于一个包内,在资源包头记录包内资源的信息,以及包内资源引用的资源信息。包内资源可继续分组,类似于文件系统机制,通过名字区分。例如包名是A,资源是 texture1,这个资源的完整名字就是 A.texture1,如果在 Group1 分组下,则为 A.Group1.texture1,如果Group1下还有分组,则以此类推,在引用资源时直接使用这个资源名。由于资源存在于包内,资源的生成只能在引擎编辑器中完成,资源的存储控制就完全交由引擎编辑器完成,这样当引用的资源存储位置发生变化时,实际上是名字发生了变化,可以通过引擎编辑器自动完成相应的改变,实现了当资源路径发生变化时能自动识别变化并作出相应的调整,无需人工干预的目的。由于在包头记录了包内资源的基本信息以及包内资源的引用资源的信息,这样只需要解析包头就可获取相关的资源信息,也实现了快速获取引用资源信息的目的,游戏逻辑可以预读资源包信息即可构建完整的资源信息。同时,资源包的设计实现了降低资源文件管理的复杂度和减少文件数量的目的。 2.资源数据库。引擎中实现资源数据库机制。通过分析发现,资源和资源之间往往存在着引用关系,这种关系实际上是构成了 DAG(有向非循环图)这样的数据结构,每个资源节点都可以引用多个资源,同时又会被多个资源引用,但不会形成循环引用关系,所以我在引擎中实现了这样的资源数据结构,我称之为 Resource Info DAG,图中每个节点并不是资源对象,而是资源描述,因为资源可能随时被使用,资源描述可以一直存在于整个游戏过程中而不必被删除(当然想删也可以,只是需要这个资源时还要重新读一次资源包头)。资源信息是轻量的数据,只是记录了资源的类型、名字(我们实现了名字系统,一个整形即可代表一个完整的名字)、资源大小、在资源包中的数据偏移(用于资源加载)、所在的资源包、所引用的资源名字列表以及真正的资源对象指针。同时,为了快速索引到需要的资源,只有这样一个 DAG 是不够的,还需要一个名字和资源信息的映射列表,这样,Resource Info DAG 和资源映射表,就形成了引擎中的资源数据库。资源信息的出现实现了统一资源信息描述的目的,而且为实现统一资源管理铺平了道路。 3.改进资源的异步加载框架。异步资源加载可以是单线程也可以是多线程,无论在哪个线程,流程应该是一致的。在引擎中存在一个资源预读列表,以及资源预读管理器,当预读一个资源时,通过资源数据库可以立即知道资源是否存在,如果不存在则先加载其引用的资源(在资源信息中已经记录)。预读可以是阻塞的,也可以使分时间片的方式,具体的方式允许外部在预读资源时设置。时间片方式目前只是实现到资源的粒度,如果一个资源超过了读取限制的时间,则继续读直到完成为止,如果没有超过则预估下一个预读的资源时间,如果够则读,不够则进入到下一帧。游戏层可以自己设定预读资源,真正需要资源时直接通过资源数据库获取,而且往往此时资源已经被创建好,如果没有,则同步创建。应用层也可以预读一些基础资源,比如玩家相关的模型、动画等等,和逻辑数据相关的最好能预读,无关的即使需要时再读都可以,而且是完全异步,比如纹理、声音等。这样的改进带来的好处就是游戏层不需要关心资源加载的实现细节,甚至不需要向引擎提供回调(需要时也可以设置回调),需要资源时直接通过资源数据库接口获取,简化程序的复杂性。而且游戏层可以根据自己的需要预读资源,资源的调度上也很灵活。 资源的多线程加载只是完成从磁盘到内存的过程,并不包含资源对象的创建过程,引擎规定所有资源对象的创建都是在主线程中完成的,不允许在其他线程创建资源对象,这样就完全消除了资源创建中线程同步的问题。实际上我们知道,最耗费时间的往往是磁盘 IO 读取操作,而资源数据一旦进入到内存,资源的解析和构建过程是很快的,即使放在逻辑线程中同步创建资源对象,开销也不大。而且在渲染线程独立的情况下,逻辑线程的负担就更少了,同步创建游戏对象已经不是主要的性能瓶颈。多线程只是 IO 读取的话,实现上就简单了许多,首先就不需要考虑资源对象管理上的线程同步问题,其次可以利用引擎已有的基于 Ring Buffer 生产者/消费者线程机制实现一个完全无锁的多线程 IO 读取框架。不过有种情况还是要特殊处理:当多线程 IO 读取某个资源数据时,主线程同步的需要创建这个资源对象,这种情况下需要同步IO读取。但实际上这种情况属于低概率事件,即使偶尔发生用户也能接受。 10月7日 Dream Engine 2 开发近况这是参加 CGDC 之后写的,断断续续写了几个月,是我历史上跨时最长的日志了,最近很忙,十一长假才得空闲放上来,希望以后不要这样了。这段时间我们公司遇到了前所未有的困难,希望曾经在这里工作过的人和了解我们公司内部情况的人嘴下留情了,无论怎样,绩思思对待游戏、对待研发的态度是认真的,对待技术的态度是极其负责的,在此谢过。
1. 又是 3 个多月没有更新了,这段时间发生了许多事情,但DE2的开发依然有条不紊的进行着,其中渲染系统支持了 Lighting Pre-Pass(也称 Deferred Lighting),这是 《ShaderX7》中提出的渲染技术。其主要思想从 Deferred Shading (DS)演化而来,但同时改善了 DS存在的问题。为了更好的说明LPP,首先要了解 DS,简单来说 DS 是一种光照的后期计算,在每帧开始时先生成光照所需要的几何体的数据缓冲--屏幕空间区域的大小--我们称之为 Geometry-Buffer,也就是通常所说的 G-Buffer,接下来计算光照时,在 PS 阶段通过纹理采样的方式采样之前生成的 G-Buffer,用这个数据和光源参数计算每个屏幕象素的光照值,由于被光照的物体的光照数据已经生成在 G-Buffer 中,所以不需要重复绘制物体,只需将光源作为模型(点光源、聚光灯等)或者屏幕大小的四边形(方向光、天光等)绘制即可,在多光源的情况下,整个光照过程只渲染一次物体,极大的减少了绘制上的重复。由于G-Buffer 的数据比较多,例如 Diffuse、Emissive、Specular、Sp Power、Normal、Depth 等,需要多张RT来保存,这就意味着需要占用很多的显存来保存这些数据,即使通过一些合并数据的压缩技巧,对显存的占用量还是不小。为了解决这个问题,《Shader X》系列的编写者 Wolfgang Engel 提出了Light Pre-Pass Renderer技术,实际上就是 Pre-Pass Lighting,主要的思想是:在G-Buffer的生成阶段,只输出光照计算的必要参数,比如 Sp Power、Normal、Depth等,在光照阶段通过前一步生成的 G-Buffer和光源参数生成屏幕空间Lighting RT,最后再绘制一遍物体,在绘制物体时获取物体表面的材质的 Diffuse、Emissive、Specular Color,以及前一步生成的Lighting RT,计算最终的颜色。由于在 G-Buffer 阶段会生成 Depth Buffer,所以在渲染物体时可以利用图形硬件的 Early-z Culling 机制剔除大量的无效像素,提高一定的PS阶段的性能。相对于 DS,这个方案的优点是减少了显存的占用,缺点是多渲染一遍物体。在传统的 FS 和 DS 之间,这是个比较折中的方案。Crytek 在 CryEngine3中加入了这个技术 (presentation),用于在 Xbox360 这样显存资源比较稀缺的平台上实现多动态光照的效果。PS3 平台上的《抵抗2》是首款使用该技术的次世代游戏,这是它的技术文档Insomniac Prelighting - Game Developers Conference 2009。在实现的过程中有几个细节需要注意:
4月16日 Dream Engine 2 的一些事(二)最近在忙着优化游戏,除了用到的第一代引擎本身的优化外,美术也在资源制作上开始优化,减面、合并材质、模型、减关键帧、合理使用粒子模块...这些规则即使在我看来也不容易把握好,可苦了这些很感性的美术了。同时策划也尽可能做些优化工作,比如增加场景布局结构的合理性等,游戏客户端和服务器端也在优化,总之全民皆兵。可以预见到今后 CL 游戏场景将会更复杂,所以我也在积极推进美术工具的使用,以提高他们的生产效率,比如 Light Map Editor,CL 是基于静态光照来表现游戏场景的,之前都是美术在 3dsMax 中手工去调节整个场景的顶点色,费时费力,上周美术试用引擎提供的 LME 之后,提出一些反馈意见,我们也在改进之中。
说到静态光照,不得不说 DE2 的光照方案,在开始阶段我并没有确定开发 DE2 的静态光照技术,直到上个月的 GDC09,我了解到,UE3 这次的改进主要在静态光照系统:增加了称之为 LightMass 的系统,可以实现静态的全局光照,这样可以节省大量设置场景灯光的时间,使用这套系统可以可以告别以往一个 Level 要上百个光源的历史了。CryTeck 也推出了 CE3,实际上是 CE2 的低配置版,但游戏开发本身的功能也增强了。无论怎样,从这两款引擎的变化来说,引擎开发商们更务实了,渲染技术的发展也不像以前那样激进了,甚至还有些倒退,也许是考虑到金融危机的影响,PC的硬件发展将会放缓,从这方面来说是也许个正确的选择。根据这些信息,DE2 的静态光照系统也基本定型了:支持全局和局部静态光照,支持 Normaled LightMap 和普通的 Light Map,支持 Vertex LightMap 和 Texture LightMap。实际上 DE3D 早已经实现了局部静态光照,流程也已经完善,所以在此基础上开发 DE2 的静态光照系统是相对容易的。首先我实现的是 Normaled Light map,在翻阅了 HL2 渲染技术文档之后动手试了一下,基本都实现了,值得注意的是 bump basis 是在 tangant space 定义的,生成 lightmap 时要在 tangant space 生成光照信息,另外还要注意和引擎定义坐标系、Matrix 的行列优先级、旋转手相性要一致。Diffuse 光照好了,接下来是 Specular,HL2 中的静态 Specular 是通过 Envorment Map 来实现的,这种方法比较麻烦,而且需要预生成额外的纹理,我仔细研究了一下 UE3 的静态 Specular 的方式,通过简单的 Normal 和 bump basis 的 Dot 计算出 Specular 的系数,即可得到效果不错的静态 Specular,其实本质上也不算是静态,因为计算是实时的。接下来是全局光照,我希望用 Radiosity 来实现,参考了一篇 GPU 实现 Radiosity 静态光照的文章,完成了基本框架,已经交给同事去细化。
最近 Aion 挺火,我玩了一下,对其中可以生成 Alpha 物体的静态阴影很感兴趣,遂想如何实现,首先想到用 GPU 生成 LightMap 阴影,因为可以通过 texkill(clip) 过滤掉指定 alpha 值的像素。也曾想过用现在的 CPU 方式,但问题是第一是借用物理引擎的射线检测实现的阴影,当场景复杂光源较多时生成时间很长,第二就是很难生成 Alpha 物体的阴影(比如树叶),即使做到,实现上比较复杂,而且不灵活(比如无法获取美术在 Material Editor 中设定的自定义 Alpha Ref Value)。前段时间在 MSN 上和一位朋友 http://ixnehc.spaces.live.com/ 聊起这个话题,他与我分享了他的实现方案,给我很大启发:这是一种类似于 Deferred Shader 的方式,将需要光照计算的 LightMap Sampler 记录在浮点纹理上,通过 DS 方式计算光照,得到最后的 LightMap 数据,Shaow 采用和 Shadow Map 类似的方式生成,但存在精度问题,但仔细想会发现,对于方向光源,可以采用类似于分段 ShadowMap 的方式提高精度,对于点光源或者聚光灯,一般范围不会非常大,在大多数情况下也能满足要求。周一和同事们讨论了这个技术方案,认为可行,并制定了详细的技术实现细节,目前正在开发中。这样 DE2 将实现以 GPU 为主导、CPU 辅助的静态光照生成技术,将进一步提高静态光照的生成效率。另外,我们还讨论了 UE3 LightMass 系统的一些静态光照的技术细节,例如 Emmisive Lighting 等,这些也将在 DE2 中实现。
资源管理,整个三月份主要是在做这部分功能,由于之前的设计方案考虑到资源查找的优化,引入了资源ID 的概念,但这个优化增加许多复杂性,从结构到实现,从底层到高层,同时还牵扯出多人编辑资源的 ID 同步问题,以及 AutoPatch 时的 ID 同步问题,为了解决这些问题,我们还需要开发辅助的模块和工具来完成整个功能,甚至差点搞出个 C/S 结构的分布式编辑器模式。但即使这样,我们还是把整体设计方案确定了下来,包括所有的技术细节。某天晚上加班,引擎的同事问我:我们的资源管理是不是太复杂了?这让我陷入了沉思:我的初衷是什么?为什么现在会这样复杂?这样带来的性能提升和开发复杂度以及后期维护的成本是否平衡?经过一个周末的思考之后,痛定思痛,我决定去掉这个包袱,虽然性能没有提升,但是系统变得简单明了(Kiss 原则 )。周一我和同事们又仔细分析,最后决定丢弃之前的设计,轻装上阵。只是没想到这个决定得到了大家的一致认可,不约而同的都松了一口气。
逻辑编辑器,前几天我们讨论 DE2 的对象管理,讨论到最后就讨论到逻辑编辑器上来,由于之前对于网游逻辑的 Deploy 的问题一直悬而未决,使得我一直都在怀疑 Logic Editor 的实用性,因为这将改变既有的游戏开发模式,这种改变需要一段时间来适应。会议开完我的思绪仍然停不下来,抓住公司的 Server Engine 的主程序继续讨论,从开发模式的改变到实现的技术细节,我们讨论的比较深入,甚至可以肯定第一个用这种方式开发的游戏一定比传统的方式要慢,但从长远来看,是改进了游戏开发模式,提高生产率。所以,基本上确定了技术方案以及新的游戏逻辑开发模式,尽管还是有几个不确定因素。
上周把 DE2 的开发计划提交给了 CEO,这是一个粗略的计划,除了 DE2 的开发,还包含对 DE3D 的维护以及 Support 游戏项目的工作。DE2 的开发是我主动提出来的,作为公司的 CTO,我始终认为技术是游戏研发公司的核心竞争力,没有这个基础,在开发上很难走的更远。网游也在不断发展,玩家的需求会越来越多,游戏的功能也越复杂,对技术的要求也越高,虽然一款游戏的成功是多方面决定的,但是至少在技术层面,如果可以做好足够的基础保证,甚至走在业界的前端,将是保证公司持续竞争力的重要因素。
虽然,这条路难走,障碍很多,但我依然会继续前行。 2月24日 Dream Engine 2 的一些事(一)从今年初 DE2 开始开发到现在已二月有余,进度还算顺利,这次除了次世代的 3D 引擎之外,我们还开始了对象引擎和逻辑引擎的开发,同时对引擎的资源管理的设计也做了进一步的改进。实际上所谓的对象引擎就是基于 RTTI 的可扩展的对象系统,游戏可以根据引擎的 RTTI 规则来扩展自己的逻辑对象,无需重新编译引擎即可在引擎编辑器和游戏中使用游戏对象,同时这套机制也作为引擎核心对象的管理和反射机制。逻辑引擎实际上是一个可扩展的游戏逻辑框架结构,它基于对象引擎,在其内部存在一个逻辑驱动核心,这个核心主要的工作就是不停的更新当前的逻辑序列--更新每个逻辑序列的状态,每个逻辑序列由一系列的逻辑片段构成,逻辑片断通过某种机制关联在一起,形成完整的游戏逻辑流程,在每个逻辑片段内部都可以实现特定的逻辑,通过结合对象引擎的 RTTI 机制可以对游戏对象进行控制,这样就可以实现大部分的游戏对象间的交互。与此同时,通过提供可视化的逻辑编辑工具代替传统的手工编写游戏脚本逻辑,可以提高游戏开发效率,缩短迭代周期。实际上这种机制并不是什么革命性的技术,从本质上来说和传统的 脚本 + CPP = 游戏逻辑 的方式没有什么区别,只不过是提供了一个可扩展的框架结构,将所有用 DE2 开发的游戏的逻辑纳入其中来驱动。但是,目前还有些问题没有完全解决,比如:对于 Online Game 的 Server 和 Client 逻辑如何编辑和部署? 众所周知,网络游戏的逻辑分散在 S/C 两端,而且还不可避免的存在时序和验证问题,如果是单机游戏这种方式可以工作得很好,但是网络游戏的情况就比较复杂。而且这里还有两个细节的问题,一个是策划如何通过逻辑编辑器决定哪部分逻辑是运行在客户端,哪部分逻辑是服务器端?第二就是性能问题,RTTI 也好,逻辑引擎也好,天下没有免费的午餐,要做到可扩展,就要付出性能的代价,高度的抽象和游戏对象的运行时动态查找以及编辑势必带来性能开销,所以,在性能和扩展性上找到平衡点也是摆在我面前的一个难题。 3D引擎方面,DE2 重新设计的渲染架构在开发的过程中感觉到比 DE1 要好许多,因为它完全不需要关心渲染核心以外的数据结构,同时 SG 也实现了在只知道很少量的渲染信息的情i况下向渲染核心发送渲染请求的功能,这样 SG 和 Render Pipeline 就被隔离开来,实际上在系统中存在一个中间层,我们称之为 Render Primitive Manager, 它负责在 SG 和 Render Pipeline 之间沟通,也就是说这个 manager 同时知道两者,但它不需要太多细节就可完成渲染数据的构建工作。为了更好的适应今后的多核心平台,多线程渲染架构也是我一开始就考虑的功能。由于在开发之前做了充足的准备,以及在去年实现的多线程资源管理的基础之上,再加上新的 Render Pipeline 和 SG 的完全分离,我大概用了 2 天左右的时间就完成了开发及测试工作。为了做到和逻辑线程完全并行,渲染线程完全是 lock-free 模式,对渲染线程数据结构的操作完全是异步方式,同时提供方便的回调机制满足特定的渲染以及渲染结果查询需求。 材质系统是变化最大的系统之一,第一代引擎的材质系统完全被废弃,为了更好的扩展性,我们重新设计了 Shader 系统,这样做的前提是整个引擎的着色机制完全基于可编程管线,只保留 D3D 10 还保留的固定管线设置,在固定管线的数据结构实现上参考了 D3D10 的架构,这样将来在迁移到 D3D10 或者后续版本会更加容易。同时我们还设计了一套 Shader 编译机制,实现了在不同着色环境下的动态 Shader 生成,而且做到了和顶点生成结构无关,在这方面我们参考了 UE3 的材质系统和材质模板。目前已经开发完成了可视化的材质编辑器,基本上做到了 UE3 能实现的材质效果我们都可以实现。 12月31日 我的2008“从今天的结果来看,引擎的材质系统和核心渲染流程的调整比我们预想的要快。” “嗯,因为我们经过了详细的设计和思考,想的比较全面,所以实现起来也就比较快” “对,其实二年多的引擎开发过程也积累了很多经验教训,为这次第二代引擎的开发打下基础,虽然之前走了不少弯路,但也并不白做。” 以上是我和引擎开发组的同事在2008年12月31日晚上10点钟的对话,今天我们加班,明后天还要加班,加班的目的是尽快完成Dream Engine 2的材质系统和渲染核心,为全面开展第二代引擎的开发铺平道路。正是公司的新的游戏项目促进了第二代引擎的开发,次世代游戏的标准对引擎提出了更高的要求,由于第一代引擎兼容固定管线和低配置硬件,如果继续在其上开发次世代引擎,结构上会变得很复杂,实现上也很复杂,实际上从十月份开始的次世代渲染技术的尝试过程中已经显现出这一点,同时实现针对更高硬件平台特性的功能,比如新的图形硬件架构、多核架构等,原有的引擎更加难以适应。其实在几个月前我就已经有开发下一代引擎的想法,只是没有想到会这样快。实际上Dream Engine 2将是一款游戏引擎,图形引擎只是其中一部分,它将包含网络、物理、脚本等网络游戏开发相关的内容,它还会集成更多的可视化编辑工具,为游戏开发提供便利,成为次世代网络游戏的集成开发环境。程序同事们对开发次世代引擎是热情高涨,美术和策划同事们也希望能使用更强大的引擎编辑器来制作游戏,公司希望能制作出顶级的游戏,我也希望再一次挑战自己的极限,这些是我们的希望和期盼,是2009年的最重要的工作之一,这次的开发时间会很长,还有许多的技术需要研究和探索,我非常荣幸能有机会开发这样一款引擎,我相信我们能再一次超越自己,走向更加精彩的2009年! 8月27日 青.花.瓷 素胚勾勒出青花笔锋浓转淡
瓶身描绘的牡丹一如你初妆
冉冉檀香透过窗心事我了然
宣纸上走笔至此搁一半
釉色渲染仕女图韵味被私藏
而你嫣然的一笑如含苞开放
你的美一缕飘散
去到我去不了的地方
月色被打捞起云开了结局 如传世的青花瓷自顾自美丽 你眼带笑意 临摹宋体落款时却惦记着你 你隐藏在窑烧里千年的秘密 极细腻犹如绣花针落地 帘外芭蕉惹骤雨门环惹铜绿 而我路过那江南小镇惹了你 在泼墨山水画里 你从墨色深处被隐去 炊烟袅袅升起隔江千万里 在瓶底书刻隶仿前朝的飘逸 就当我为遇见你伏笔 天青色等烟雨而我在等你 月色被打捞起云开了结局 如传世的青花瓷自顾自美丽你眼带笑意 炊烟袅袅升起隔江千万里 在瓶底书刻隶仿前朝的飘逸 就当我为遇见你伏笔 天青色等烟雨而我在等你 月色被打捞起云开了结局 如传世的青花瓷自顾自美丽你眼带笑意 7月8日 生活 & 工作睡梦中被女儿的哭声惊醒,一阵安抚后,女儿睡去,抬头看已经快凌晨 5 点了,睡意竟全无。于是打开 blog,随便写点什么吧。
生活:
我有些时候选择性失明或者失聪,一方面不想让无谓的事情妨碍自己前进的脚步,另一方面其实也是无可奈何。其实这样没有什么不好,什么事情都较真才是和自己过不去,尤其是自己无法控制的事情。我选择引擎开发而非游戏开发也许就是在这种指导思想下的一个反映吧。回首引擎开发 2 年中,真是什么滋味都尝过了,从怀疑、质疑到信任、赞许这个过程我是历历在目、百感交集。不过,我认为这是非常正常的,人们在接受一个新事物(人)的时候都要有个心理路程,每个人都是如此,尤其在自己已经有了思维定势之后更是如此。前几天跟 CEO 吃饭时我对他说:我不在意职位,我在意我做的事情。其实职位只是个 Position,重要的是能否在所做的事情过程当中提升自己,超越自己。CTO 也好,程序员也罢,本质上都是要写程序的,都要以程序的思维去思考工作中的问题,在这一点上两者没有任何差别。一直到现在,我仍以程序员的标准来要求自己,而且我的情绪依然会受到程序的影响,不过另一方面作为管理者的角色,自我控制力也比以前强了许多,但是内心中的波澜还会被程序所控制,这一点我自己非常清楚。其实我多少有些抵触管理上的工作,琐碎、繁杂,而且难于控制,这一点我在7年前第一次做项目经理的时候就体会到了,但是也有好处,它会迫使你从纷繁复杂而又非常理性的程序中跳出来去解决一个完全不同领域的问题,看似无关其实也有些内在联系,这使得我在程序上很少走死路,尽可能尝试其他方法,在面对新知识的框架体系时不会慌乱不知所措,而是跳出来从整体的高度来审视自己面对的问题,这种时候往往能得到意外的收获。其实,最近一年最重要的是我的女儿,无论我面对多么头痛的问题,每当我回到家里坐在椅子上愁眉苦脸的思考时,女儿往往会站在书房的门口,拿着她心爱的“宝贝”朝我微笑,那天使般的笑容完全化解了我心中所有的烦恼和忧愁,我觉得那是世上最美好的事物,而那些所谓的问题也经常在女儿的笑容背后被我一一干掉了。也许同事们很奇怪我经常在早晨上班时兴奋的给大家讲开发上的新想法,其实他们不知道我有这样一个秘密武器。:)
工作:
拥有次世代渲染效果的引擎就是次世代引擎吗?回答当然是否定的。6月份我调整了 3D 引擎的开发计划,决定实现一些所谓的次世代渲染效果,从完成情况上看基本符合我的预期,另一方面也是让引擎组的开发人员认识到这样一个道理:写 shader 容易,整合难。事实确实如此,从调研实现方案到在引擎中实现出来2个研发小组总计花去了不到3周的时间,而整合这些渲染特效到引擎中并提供良好的使用接口则直到现在也没有完全结束,更别提优化了。一个不了解 3D 引擎开发的程序员往往会很羡慕引擎开发的工作,他以为做引擎开发天天接触的都是图形、shader、华丽的渲染特效,但事实上并不是这样。不妨举个例子,一个去年进入引擎开发的新人,在引擎开发的一年多的时间里参与了如下的开发工作:字体绘制、文件系统、序列化系统、粒子系统及编辑器、特效系统及编辑器、纹理管理、异步资源管理,在这个过程中他学到了 win32 编程、GUI 框架(wxWidget)、windows File System Management、粒子系统逻辑、特效逻辑、多线程、纹理格式解析等等。可以看出这些知识覆盖面比较广,每一个部分精通都不容易,都需要时间和经验,而这些其实就是引擎的本质,是引擎的基础,没有这些就不能称之为引擎,没有这些引擎也无法工作。在这背后他所学到的更多的是对软件开发的理解,这包括:如何设计人性化的编辑器UI?如何提供良好的使用接口?如何保证开发过程中系统结构的可扩展性?如何解决原有系统和新功能之间的冲突?如何用最简便的方式来实现功能?如何解决 bug?如何修改一个 bug 而不会因此产生新的 bug?如何多人协作开?如何与其他成员沟通交流?如何最大化的表达自己的想法而尽可能的减少信息丢失?这些是软件开发本身的东西,无论开发哪种领域的软件都要面临这些问题,但是这些确实非常重要的,会写 shader,会点图形学知识是远远不够的,3D 引擎是众多开发技术的混合体,这是个庞大的体系结构,图形渲染是上层结构,是最后的数据呈现,而驱动这些数据的却是那些底层的基础功能,没有这些基础功能的保驾护航,单单一个渲染 API 是没有任何意义的。
在开发中另一个头痛的问题是如何选择的问题,很多时候已经找到了解决之道,但存在着多种解决方案,如何选择就成为了一个难点,因为开发者都希望做出最优的决定,但系统在没有实现和被应用之前是很难判断出选择的对错。这时需要的就是经验的判断和对未来的假设,如果缺少了这一步所作的决定是比较危险的,当然,可以通过后期的重构来不断调整,但是重构并不是推翻重来,重构是基于原有系统的局部修改,使原有系统尽可能的适应新的变化需求。这几乎已经是一个固定的要求了。而且当系统本身被大量的使用的情况下,即使推翻重来也是需要决策的勇气的。我经常被困扰的就是选择的难题,现在我已经形成了习惯,在头脑中自己和自己辩论,看哪边更有说服力,最后做出一个决定。其实我更希望和别人去讨论,因为自己的思维也是有局限性的,不同的思考方式往往使自己辛辛苦苦建立起的思维体系有所变化,也许是更加完善,也许是完全推翻。即使自己被推翻也没关系,重要的是最后会得出一个结论,而这个结论在大多数情况下会更加接近真实。 5月26日 最近有点乱最近心情有些乱,原因众所周知.原计划上上周就该完成的多线程资源管理一直拖到了这一周才完成,自从地震之后,我整整一周都陷入其中无法自拔,甚至我怀疑是否因此得了抑郁症.广播\电视\网络\媒体无一不刺激着本来就脆弱的神经,所到之处都是和地震有关的信息,虽然避免不去了解灾区的信息,但是还是无法控制.在这些天里,甚至今天,我一个30岁的大老爷们居然流了比以往几年还多的眼泪,原因很简单:我也有孩子,我也是孩子的父亲。真正让我难以接受的正是那些在地震中遇难的孩子,每当女儿在我身边跑来跑去叫着爸爸的时候,我无法不想起那些孩子,我无法想像如果是我失去了孩子,我还能否镇定,还能否控制自己的情绪,还能否坚强而不崩溃....
看着灾后的一幕幕事件,有揭发贪官的,有举报红十字的,有为志愿者诉苦的...死者死矣,活着的人还得继续生活,工作也得继续。这周连续加班,终于把多线程资源管理搞定,还有些局部的小问题,希望下周能彻底完成。去年3D引擎组一直都是2、3个人,今年短短2个月的时间,已经5个人了,这周四面试的程序员也不错,真是要不就不来,一来就都来。
无意当中看到了以前盛大同事的 blog,想起那段时光。现在回想起来,一直都感觉无法融入盛大的环境中,这种感觉在以前的公司也有过,是一种直觉吧,无论从同事也好,还是公司也好,都感觉不够舒畅,也许是自己不想融入的关系吧。来到 JCC 已经快 2 年了,其中也有些让人不快的事情,但都已过去,而且同事们都很坦诚,即使是有过争吵,也可以对事不对人,我印象最深的是刚到公司时负责清洁工作的大姐(其实大家管她叫阿姨)每天早晨上班时都主动和我打招呼,其实我以前还真没有这个习惯,在她的影响下现在也养成了。
有一天在 qq 群上有个冒天下之大不韪的家伙说了些让大家很不爽的言论,自然成为了批判的对象,也成为大家发泄的对象,当然我也参与其中,其中他问我,地震死的人里有你的亲戚?我说没有,他说那你有啥悲伤的?其实他这个问题真的把我难住了,我也不知道为什么,也许是为了失去孩子的父母,也许是为了失去父母的孩子,也许是因为我也是一个孩子的父亲,也许我生来就多愁善感....也许在早几年,我不一定会如此悲伤,为了一些和自己完全不相关的人彻夜难眠。不过我知道,随着年龄和心态的成熟,我逐渐认识到生活的意义,和生命的意义,尤其是有了孩子之后,当看到自己的孩子慢慢长大,那种对生命的敬重油然而生。大自然是奇妙的,生命更是值得尊重和敬仰的,也许正是这样,才让我如此吧。灾后公司号召捐款,我也捐了一些,我知道那点绵薄不算什么,但是至少其中有一小份是我的力量,也许它不能全部用于灾民,但哪怕有一点点,也算是我的一份力量吧。
每到周一我都不在状态,写程序就好像脑子僵住了一样,半天反应不过来,我称自己为周一综合症,明天又是周一,好吧,让周一综合症来得更猛烈些吧! 5月19日 我祈祷我祈祷,那些在灾难中逝去的孩子,会永远得到快乐,因为他们,都已升入天堂,变成了天使;
我祈祷,那些在灾难中逝去的学生,会永远得到爱的围绕,因为在人间,有我们守护;
我祈祷,那些在灾难中逝去的兄弟姐妹,会永远得到安宁,因为在人间,有我们在超渡;
我祈祷,那些在灾难中逝去的长辈们,会永远得到幸福,因为在天堂,不会再有人间的疾苦;
我祈祷,那些在灾难中逝去的老师,会永远得到心灵的慰籍,因为她(他)的学生,不会辜负她(他)的付出和牺牲;
我祈祷,那些在灾难中逝去的士兵,会永远得到最高的荣誉,因为人民将永远把他的名字铭刻在心里;
我祈祷,那些在灾难中失去父母的孩子,能拥有更多的父爱和母爱;
我祈祷,那些在灾难中失去孩子的父母,能重新拥有天伦之乐;
我祈祷,那些失去家园的人们,能够拾起勇气,建设家园,重新点燃生活的希望;
我祈祷,不再有灾难,不再有余震,不再有瘟疫,不再有死亡;
我祈祷,我不再悲伤,我要为生命的奇迹而高兴,要为那些幸存的人而高兴......... 5月11日 Epic Games: Tim Sweeney InterviewØ GDC08 上展示的UE3 的新特性是否存在平台相关性 n 一句话,针对所有平台,不过不同平台的硬件限制可能会导致效果不同。 Ø UE3的版本问题 n 这个问题主要是针对 UE2 的 XBox 版本的,Tim的回答是 UE3 在设计当初就考虑到平台差异性,所以整个引擎在核心代码上都是完全相同的,针对不同平台的特性,实现略有不同,但基本核心完全相同。 Ø 关于演示中的高密度人群系统技术 n Tim回答说这个系统可以支持不同类型的角色数据,但是不同的类型越多,优化就越困难(Me:借助硬件的Instance技术都有这个毛病),他建议说,最好不要用这技术做交互性的功能,这个技术更适合视觉上的特效(Me:看了Gow2的演示,我非常相信这一点,也符合我之前的猜测。) Ø 关于《刺客信条》中的人群 AI技术 n 一句话,如果需要,UE3也可以做得到。 Ø SSAO n 由于是基于屏幕空间计算,SSAO和场景复杂度无关。之前的 UE3 的 Shadow 技术,是基于对象空间的,所以当有更多角色或者更多动态光,代价就会越大。(Me:至少产生阴影是基于Screen的吧?) Ø 实时全局光照还有多远? n 他提到了 hl2 ,简单说 hl2 业时全局光照计算,但是需要做大量的预处理工作,这样即使 dx7 级别的硬件也可以跑的起来。前提是静态场景和光照。同时他还提到了 Geometrics 团队,他们同样采用了预处理但是可以支持动态光照。(Me:那个演示我看过,确实不错。)Tim认为在2012年左右会进行下一次的主机换代(Me:这么久?)他认为下一代主机可以实现相当真实的光照计算,因为即使是现在也可以实现不错的光照效果,虽然是每场景只有几个光照而已。 Ø 关于UE4 n UE3将贯穿整个次世代主机时代,他希望能在2011、2012甚至更晚时还能看到用 UE3 作的游戏。Epic将全力的改进、优化和扩展 UE3。尽管不断的加入新的特性,但是引擎的整体架构不会变化,游戏开发人员可以很方便的移植到不同的UE3版本中(Me:真的这么方便吗?)目前在Epic 中有个UE4的研究团队,但是只有一个人,那就是Tim(Me:大权独揽啊。),目前进展非常慢,明年也不会有太多人加入这个团队,除非快要进入下个次世代。 Ø UE3如何决定加入新特性 n 基本上UE3的特性都是被内部的游戏开发项目所驱动的,比如《战争机器2》。目前物理破坏系统和柔体系统是当前优先级比较高的特性。对开发来说,困难的不仅是加入新特性,而是如何集成正确的特性并使之能正常运转起来。(Me:深有同感!)唯一的方法就是自己去使用这些新功能,并运用到游戏中去(Me:Gow2),而且要让美术日复一日的去使用,在对程序员不断的抱怨新功能有多么的糟糕的过程中,这些功能就会不断地在完善。整个过程都是基于内部的开发需求驱动的。(Me:说得太好了,说到底引擎也是个软件,也需要靠需求驱动,有什么样的需求,就有什么样的引擎功能。) Ø IBM Power架构和Intel、AMDx86架构的比较 n (这里说了许多,都是经济上的,总的来说就是性价比,虽然Intel Cpu 性能很好,但是不经济实惠,而Power芯片就好很多,未来的发展主要还是经济实惠型的。看来Tim很有经济头脑,并不是一个只研究技术的程序员。) Ø 多核技术 n 首先,多核架构的开发是困难的。但是硬件的发展趋势是阻挡不住的,因为多核是唯一能更经济的提升性能的方法(Me:又一次提到经济!)。多核下的引擎架构比单线程更有效率,但是也付出了更多的设计、实现、维护、调试….所有的成本都随着多核而变大,这是相当的昂贵,但是最后必须忍受。但是其它硬件架构比这更糟,比如 Cell,他们发现需要付出5倍的努力才能达到相同的效果。Tim希望能出现针对多核编程的语言,这样程序员在写程序时和写单线程一样,由编译器来完成多核的优化工作,可惜这种语言目前还在实验室中,还不能商用,他相信接下来的5年多核程序语言将会越来越成熟,那时程序员写多核程序就和写单线程程序一样简单。(Me:我也是这样想的,一直到现在我都觉得多核的程序优化应该是编译器的事情,不需要程序员进行过多的参与就可以实现针对多核程序设计。) Ø 其它: n Tim在20年前高中时就实现过自己的编程语言。 n Tim认为GPU和CPU 都可能被对方取代。 n GPU与CPU的Physic计算,Tim认为当前阶段GPU尚需努力,毕竟 CPU 已经发展这么多年,而且有完善的缓存机制,强大的分支预测能力等,这都是 GPU 的弱项。
原文奉上,有兴趣的自己看吧: http://interviews.teamxbox.com/xbox/2169/Epic-Games-Tim-Sweeney-Interview/p1/ 5月5日 引擎的资源管理线程化五一假期无所事事,唯一值得欣慰的是作出了一个重要决定:将引擎的资源管理线程化。今天写了一个基于 Ring buffer 的双线程同步器,和在此基础上的资源处理器。写好后准备进行资源管理的改造,但是遇上的第一个难题就是如何处理资源加载的异步IO,其实这个问题在上一次的设计中也有提到,当时的方案时没有异步IO,因为将来引擎线程化时,资源的IO处理都是在主逻辑线程,所以对Rendering线程来说,也算是异步IO了,不过今天我推翻了之前的想法,觉得还是应该要异步处理,毕竟游戏逻辑也是不容阻塞的。但问题关键不在如何异步IO,因为线程同步器已经写好,而且引擎的抽象文件系统和线程模型无关,所以这不是问题,问题在于如何处理异步IO对引擎使用者的影响。比如:创建一个角色动画对象,游戏使用诸如 CreateActorFromFile 之类的函数,如果在创建时指定异步IO,很有可能出现对象没有创建好就被使用的情况,比如调用创建函数之后立即设置一个角色动画等。在这种情况下不作处理一定会产生问题。这个问题我之前也想了很久,一直没有想出很好的解决方法。其实我希望能做到外部创建资源而不知道资源加载的视线细节,和同步创建出来的资源相同的使用方式而不需要做任何特殊的处理。但实际上这是不可能做到的,像上面的例子就无法解决。下班后,我在咒骂公交公司害我等了1个小时公交车的过程中的想出了如下的解决方案:为引擎的资源对象增加一个AsyncWaitForLoading函数,无论是否是异步IO创建资源,都可以调用这个函数,如果是异步创建,这个函数内部用傻重试的机制来不断的测试资源是否加载完成,如果是同步创建,则立即返回。这样,当此函数返回时,资源对象就一定可以正常使用。这个在本质上此方案也是让外部知道了引擎资源加载的一些实现细节,不过仅是个函数调用而已,在接口使用上也不会太脏,最主要的是解决了异步加载和对象使用的复杂问题,总体来说是利大于弊。另外从实际的游戏开发中来看,一般静态的关卡资源可以使用异步机制,比如地形、室内、静态物体等,动态对象大多数是同步机制,比如角色(也有例外,比如《苍天》,进入后主城你户会看到走来走去的身体零件,虽然异步,但玩家感觉不好,比较好的是 wow,淡入淡出),即便是异步机制,对于游戏来说也可以是作为预加载技术来使用。此外,还需增加一个查询 Loading 是否完成的函数,为外部提供方便的查询机制。 接下来就要细分资源的加载过程,我将资源的加载大体上分为4个阶段:磁盘IO读取-〉内存对象构建-〉设备资源创建-〉显存数据填充。这4个阶段是按照时间顺序执行的,理想的情况下每个阶段都是在不同的线程中被处理。磁盘IO读取可以是几个IO线程并行,设备资源的创建一定在渲染线程中,显存数据的填充可以另作一个线程,当然也可以直接放在渲染线程中。我希望各个阶段可以灵活配置,而不受于线程的限制。前面所说的资源创建完成的标志在第2个阶段设置完成时设置。这里有个细节问题还需要说明:引擎的资源之间存在着引用关系,所以当加载一个资源时,在构建其内存对象的过程中会解析出所引用的其他资源,比如角色资源中引用了骨骼资源,骨骼资源中又引用了动画资源等等,这种情况将出现在第2阶段中,由于我希望所有的IO处理都交给IO线程来完成,并且线程同步是基于Ring Buffer机制,所以当需要读取其他资源时,由内存对象构建线程向创建资源的线程投递IO请求,然后再创建资源线程投递IO读取请求,如此反复。 线程化是个比较复杂的工作,调试起来也比较麻烦,但这是引擎线程化的第一步,没有资源管理的线程化,引擎线程化就是空谈。粗略估算了一下工作量,这要耗掉我80%的时间,不过我已经有近3个月没有对引擎做大的改进了,希望能在5月份完成这个工作。 5月1日 路漫漫这阶段引擎的开发基本上是跟随着游戏开发的深入进行的,这种开发方式也是我认为比较合理的方式:根据游戏的实际需求来完善引擎.几乎很少有闲下来的时候,不过引擎的总体开发进度还是快于游戏的进度,因为在游戏项目计划的早期就及时订制了引擎的未来的功能需求.目前甚至可以确定了到今年底的开发计划:
1.优化,这部分包含的内容很多,比较大的有数学库的多媒体扩展指令集的优化,初步打算使用 mmx\sse 这两个国内市场普及的指令来优化。还包括动画系统的优化,重点是骨骼动画。还有粒子系统、场景图、文件系统等,大体定为2个月的优化时间。
2.兼容性测试和调整,在游戏正式 CB 之前要进行大范围的硬件兼容性测试,这对 3D 游戏很重要,所以这部分目前定为 2个月的测试和调整时间。
3.零碎地修修补补,整个引擎功能的细节修正和 bug 修改,这包括:引擎核心、渲染器、功能层部分、编辑器、导出插件,以及一些游戏的特殊需求,这是贯穿在今年的过程中了。实际上这1个多月来我一直都在忙着修修补补,一方面导出插件的功能有变化,另一方面随着引擎编辑器的开发和游戏的特殊需求,也暴露出引擎内部的一些结构和功能上的问题,还好问题都不大,都可以在现在的结构上进行改进。
上月末游戏发布 prototype 版本,策划提出了关卡模式的设计变化,目前还没有最后确认变更,不过这个变化需要引擎提供动态资源管理能力,这是我非常感兴趣的地方,之前的异步资源管理的设计都已经写好,但是由于更改比较大--如果做的话,是引擎开发以来最大的变化--而且还有特效和角色编辑器的需求,所以我一直没有动手,其实主要还是那时游戏并没有这样的需求,现在看来这种可能型还是比较大,因为关卡模式的变化的确会带来游戏更好地体验。
五月份的主要工作内容包含一个一引擎渲染系统的变化,问题还是由于游戏的需求引起的,这个需求让我决定改进渲染方式,其实在引擎开发的早期我已经发现问题,比如水面纹理的渲染、环境立方体贴图的生成,还有游戏中角色头像和物品在子窗口中的渲染等等。我跟同事讨论了一下,昨天定下来了设计方案,打算在 5 月中旬完成开发。由于游戏的人手紧张,我决定帮助游戏部门完成 avatar 系统和游戏的特效功能,这期间将正式使用4月份的开发成果,一定还会有许多问题。
现在我感觉引擎越来越成熟了,游戏开发已经有半年多的时间,这段时间引擎一直紧跟着游戏的实际需求,其中也作了几次大的重构和调整,这些都让引擎越来越稳定,功能越来越完善。如果没有游戏,是不可能做到这一点的。不过引擎的成熟和稳定都是相对的,和我心目中理想的标准比起来还要差得很远,但我知道这是一个漫长的过程,需要经过多个游戏项目才有可能达到这个目标,还有很多很多的事情需要做,我甚至已经把引擎的整体开发计划定到了 2010 年。 4月6日 一篇小学生作文 “六.一”前夕,五年级一班学生刘小华因患感冒请假。班主任指示班干部们自发组织到刘小华家里慰问。第二天,班里黑板报登出了一篇《本班新闻》, 全文如下: 本班讯 昨天上午,阳光明媚,鲜花斗艳。刘小华同学家里欢声笑语,人头攒动。 五年级一班班长赵大傻、副班长张小呆在体育委员欧阳猛南、文娱委员李美媚 陪同下,不远千米,深入到患感冒发低烧的班级成员刘小华家中,为他带去节曰的问候和良好的祝愿。赵班长与张副班长兴致勃勃地参观了刘小华的小房间,饶有兴 趣地玩了四盘“魂斗罗”游戏,与普通同学同乐。接着,班级领导与刘小华同学的双亲亲切地拉起了家常。赵班长还愉快地回忆起去年和刘小华开始一起作弊的往 事。 在交谈中,赵班长多次关心地强调:“刘小华生病了,就不要做作业了。好好休息,身体是革命的本钱嘛!” 刘小华激动地说:“感谢班领导的关心!我一定要 战胜病魔,克服一切困难,早曰回到暖的大集体中,回到亲爱的老师和同学中间!”接着,赵班长一行又在刘小华家门口兴致勃勃地踢起了毽子。蓝天如洗,鸟儿也受到这集体暖的感染,唧唧喳喳,歌唱美好的生活。中午,刘爸爸买来香喷喷好吃 看得见的某某牌牛肉干和清凉可口的鲜榨橙汁, 宴请赵班长一行。席间,宾主就小学生拉完屎能不能用树叶擦屁股等问题进行了深入愉快的双边会谈。 3月20日 关于引擎外存资源的管理问题定义:外存资源指的是通过内容创建工具,比如 max\maya\photoshop\engine/game editor 等创建出来的存储在磁盘、光盘等外部设备的资源文件。 问题:通常外存资源之间存在引用关系,比如模型文件引用贴图、动画等,目前引擎的管理方式是直接在资源文件中包含被引用资源的相对路径文件名,但这两天对游戏资源目录的调整暴露了这个方法的严重问题:当资源目录改变时无法正确读取资源。实际上这种做法对于做数据库开发出身的我是非常不认可的,我们知道关系数据库在建立数据之间的关系时是通过对数据编号,然后指定关联的数据的编号即可,这是关系数据库的基础,通过编号来建立数据之间的关系。如果采用这种方式建立资源间的关系,用编号来标识资源,即使资源目录发生变化,甚至资源名称发生变化,也可以正常读取。在引擎开发的初始我也曾想这样做过,但如果真的这么做,却遇到了实际游戏开发中的问题。 要做到对资源编号,就要求资源编号必定在统一的环境中生成,还是用关系数据库举例,数据库表中的数据的都是通过关系数据库系统的开发环境来创建的,创建的同时就指定了编号,所以可以做到通过编号引用数据。而游戏中的资源的创建却不是如此,如上面对外存资源的定义所说,资源是很多个不同的内容创建工具生成的,这种特殊的资源生成方式就决定了几乎无法做到统一的资源编号。当然,可以在每一个不同的工具中通过读取统一的资源编号列表文件来生成资源编号,但是资源往往是由多个人创建的,每个人都会修改这个统一的资源编号列表文件,这就会导致统一资源编号列表数据同步的问题,一旦产生同步错误,整个的游戏资源的管理就会陷入混乱。而且,在生成资源的同时,游戏开发团队中的其他人很可能会删除某些资源,为了保证资源编号的有效性,就必须要回收删除的资源编号,但这会更容易导致同步错误,所以这种方式不可取。那么主流的游戏引擎如何解决这个问题呢? Unreal:统一强大的游戏编辑器可以编辑游戏中所有资源,从这方面来说,它很容易做到资源统一编号,因为它有统一的数据生成环境。但是这种方式却不适合国内游戏的开发习惯,尤其不适合美术人员的使用习惯,通常 art 在 3dsmax \ photoshop 中做好 model \ texture 后通过导出插件导出到指定目录下就可以直接使用,但是 Unreal 却要求必须进行一次导入 Unreal 编辑器的操作,否则引擎无法直接使用导出资源。而实际的情况是 Art 希望导出资源后能立即看到渲染效果,然后对资源做相应的修改和调整。而 Unreal 本身的资源管理特性决定了无法做到这一点,要想看到最后效果,就必须导入 Editor。这种繁琐的操作对于 Art 来说是非常痛苦的。当然,如果经过一段时间的培训,不习惯也会习惯。 Gamebryo \ Renderware:导出时可以即时看到资源的渲染效果,这对 Art来说很方便,而且可以将多个资源导出成一个包。但是和我们的引擎存在同样的问题:使用资源的文件路径\名称来定位引用的资源。Gamebryo \ Renderware内部的 API 提供了设置 Resource Path 的功能,甚至 Gamebryo在内部还做了局部的容错处理:当引用的资源无法加载时,尝试在当前资源所在的目录下加载引用资源。但即使这样,还是无法从根本上解决问题,在我看来,这种做法其实是纵容了资源管理的任意性,大大增加了资源目录结构混乱的可能性。 从以上的分析似乎可以得出这样一个结论,要做到资源管理的灵活性,就要牺牲资源产生的便利性;要资源产生的便利性,就会失去资源管理的灵活性。也许这是无法调和的矛盾,或者,还有更好的解决办法? 1月16日 资源管理系统的初步想法及其它问题(续)显存资源管理
上次说到自己做显存资源管理,也就是说全部采用 D3DPOOL_DEFAULT 方式创建设备资源,当显存耗尽时,引擎使用 LRU 或者 MRU 算法来释放显存资源。这几天翻了翻国外网站,也看了一下主流的 3D 引擎的资源创建方式,大部分都倾向于采用托管的设备资源管理(D3DPOOL_MANAGED),理由有这么几点:
1.D3D 或者 Driver 对资源的管理更加高效,因为是在操作系统的内核模式下,所以当恢复资源时一定会比用户模式下更快。
2.由于 Managed 的含义是指托管给 D3D/Driver,则 D3D/Drvier 一定会根据自己的最优化方案来进行设备资源的管理。这一点要比自己做管理要灵活得多,自己管理的无论多么高效,总是不能放之四海而皆准,在不同的硬件平台下可能会产生完全不同的结果。
3.资源的管理结构简单、清晰,这是显而易见的。
基于以上几点,调整引擎的设备资源管理方案,除了一些必要的 Default 资源外,其余全部以托管的方式来创建,这样引擎不必管理设备相关的资源,当显存不够时,Driver 会自动清除一定的 Managed 资源;当渲染不在 GPU Local memory 的资源时,Driver 会自动 Upload 到 GPU 中,整个过程对引擎完全透明,流程清晰简单。不过还是有必要跟踪显存使用情况,为调试性能提供参考数据。
File Mapping
我之前关于 FM 的 MapViewOfFile 函数的理解是错误的。我以为在 MapViewOfFile 时操作系统会分配一段内存并将映射的文件数据拷贝到内存中,实际上并非如此,当 MapViewOfFile 时系统仅仅是做了内存地址的映射,并没有作任何文件数据的读取,但是当使用 MapViewOfFile 返回的内存指针访问/修改数据时,才会产生真正的文件读取操作。另外,使用 FM 不一定比读取普通文件更快,经过测试发现:当读取的数据在操作系统分配粒度(64k)以下时(包括64k),会有较明显的性能提升,但是当超过分配粒度大小时,性能会下降,读取的数据越大,性能下降的越厉害。对此种现象目前我没有找到任何解释答案,初步怀疑操作系统本身有缓存机制。最后测试 33k+ 的文件,总计 1.5G 以上,平均读取性能要快 30% 左右。
文件系统
在原有方案中修改包数据存储时将包中所有数据都读入内存,再存入硬盘,这样效率太低,虽然可以多次修改一次保存来提高效率,但是如果包更新时(比如网络游戏更新游戏资源)对于大文件的数据包,存储的时间还是难以让人忍受的,更大的问题是在长时间的存储过程中发生异常情况的几率会变大,发生异常时会造成整个包无法读取,影响用户感受。所以要采取一定的策略提高包保存的性能。
目前采取的策略:新增数据时,通过文件结构信息获取最适合的空间进行存储。在修改数据时,首先判断修改后的数据大小是否和原数据匹配,如果大于原数据,则将数据保存在文件尾处;删除数据时直接将文件结构信息中的文件信息删除,原有数据不作任何改动。
实际上这是一种 Append 方式的数据更新机制,势必会造成包中存在碎片,如果不经整理,包会越来越大,碎片会越来越多,因此,需要增加碎片整理机制,整理包的算法基本如下:通过文件结构信息获取最适合的空间安排,从前到后的进行数据迁移,保证不会进行多余的数据拷贝。接口提供整理级别,整理大小,整理范围等参数。
引擎的线程模型及资源管理的线程同步策略
初步打算将引擎的渲染作为单独线程,引擎的场景图更新放在另一线程,可以放在游戏逻辑主线程。渲染线程中使用 Command buffer 来处理所有请求,包括:设备资源创建请求、设备资源创建完成、渲染请求等等。设备资源数据的 Upload 由设备资源 Upload 线程完成。内存资源的创建由 IO 线程完成。这样引擎最多会同时存在 4 个线程:场景图线程、渲染线程、设备资源 Upload 线程,IO 线程。
当画完线程处理流程图之后我才发现,需要同步的地方太多了,虽然锁中完成工作并不复杂,但是频繁的锁定会严重影响性能,因为在锁定的同时会使其它线程挂起(这一点在多核平台下后果会更严重,会造成其它核心挂起),严重降低 CPU 的使用率(因为其它线程在等待),不能完全并行。更为重要的是可能会造成死锁,虽然可以从设计上避免这个问题,但还会存在潜在的危险。一开始我首先想到的是 LockFree,这是一种乐观同步机制:当修改数据时,假定没有并发操作发生,在真正提交数据时如果和之前的期望数据不一致,则继续循环直到一致为止。实际上 LockFree 还是基于 CPU 的原子操作 CAS ,这个指令很多 CPU 都已提供,x86 还提供了 CAS2 语义的指令,但是 LockFree 最困难的地方在于内存管理,当数据被修改时也可能被读取,所以不能立刻销毁数据对应的内存,但何时销毁就是个比较麻烦的事情了。由于资源管理线程基本上是一个生产者和一个消费者,并且访问是顺序的,所以可以使用 RingBuffer 来完成同步,这也是一种 LockFree 同步机制,不同的是它无需采用 CAS,因为两个线程不会修改同一数据(但是会访问同一数据,所以还要做重试机制)。 1月9日 资源管理系统的初步想法及其它问题 最近一直在思考动态资源加载的解决方案,上周末看了一下 d3dsdk november 2007 中的 content Streaming 的例子,对于动态多线程后台资源加载有了一些初步的认识和想法,结合最近已经开发了 80% 的虚拟文件系统,引擎的资源管理系统大体上有了一个设计。
大概的内容是这样的,对于 3D 游戏来讲,按照存储位置可以将资源分为三种类型:
三种类型的资源,采取不同的加载策略,同时设计为三个不同的层次,其中,外存资源的管理是最底层的,其加载和存储的管理由虚拟文件系统完成,内存资源和显存资源的管理则在引擎的资源管理系统层次上进行。先从外存资源也就是虚拟文件系统说起。 虚拟文件系统的核心思想是存在一个抽象文件系统的管理接口,有两个接口的实现,一个是针对 windows 文件的,一个针对打包文件的。因为引擎的资源文件在生成时往往是分散的小文件(比如从 max 导出的模型或者动画资源),这些小文件又会引用其他的资源文件,比如模型引用某张纹理,骨骼引用了某个动画等等。如果没有抽象文件接口,而直接采用资源包的形式读取资源,则势必要求资源在生成时也要打入某个资源包中,就需要在资源生成时进行资源的包处理,这样对于美术来讲使用并不方便,并且国内的美术也不习惯这种方式,不利于快速开发。如果能做到文件系统在读取某个资源时不需要关心这个资源的存储方式(也就是说不管它是一个零散的文件还是在某个资源包中)的话,我们就可以在游戏开发期使用零散的资源,而在发布游戏版本时打成资源包,说白了讲就是把多个资源文件打包成一个资源包文件,然后所有的资源读取和存储都在这个资源包中进行操作。有了抽象文件系统的接口,用它读取资源时,就不用关心资源的存储方式了。
对于 windows 的实现来说,就是普通的文件读写操作,用 api 或者 io stream 实现就可以。对于打包文件系统的实现有些麻烦,因为要做到接口抽象,所以在接口层次上不能暴露有关包的任何信息。在实现的过程中有两个需要注意的地方,一个是资源的快速查找,一个是资源数据的快速读取。查找资源的性能我还是比较在意的,因为性能是资源管理好坏的重要衡量指标之一,所以开始我没打算使用 stl::map,由于 stlext::hash_map 我早已用过,也在考虑之列,不过因为是使用资源的全路径来索引资源,所以总是避免不了要进行字符串的比较的,而且为了保存 string 的 key 占用的内存也不少,时空比上没有达到我比较满意的程度。以前曾经看过 blizzard 的 hash 算法,通过 3 次 hash 操作可以得到一个几乎不会重复的索引(当然还是有一定概率的,只是几百万亿分之一的可能),进行查找时几乎是 O(1) 的时间,更主要的是它无需保存 string 的 key,大大地减少了内存占用,我做了 hash_map 和 blizzard 的 hash 比较测试,30k 的不重复数据,随即查找 10k 数据,结果是在查找上 blizzard 稍有优势,但内存占用上却是 hash_map 的 1/4,这个时空比还是不错的,遂采用。
对于资源包中资源的快速读取,原以为不会有问题,结果这两天在测试读取性能的时候,结果却有不同:在频繁读取超过某个大小的数据时(比如1M),读取就会比普通的读取零散文件要慢,而且慢的比率比较大,有时甚至能达到 2 倍,这个过程排除了包管理内部的开销造成的影响,甚至我单独写了个测试程序,从 1G 和 256M 的文件中分别读取 256M 大小的数据,1G 的读取就是慢,这让我有些不解,从理论上来说从一个大文件频繁随即读取数据,因为只有一次 fopen 的开销,应该比频繁的 fopen 再 fread 会更快,但实际的结果却不是这样,难道是大文件随机读取数据时硬盘磁头读取扇区数据的方式和读取零散文件不同?这个无从得知,如果知道的朋友请赐教。后来我改用 File Mapping 来加速资源读取操作,效果还是不错,不过由于 File Mapping 本身的一些特性,在随机读取数据时,还需要额外的一些处理,不过这些处理的开销都是可以忽略不计的,整体的性能依然可以达到相当不错的程度。但需要对 98 操作系统进行测试,目前这个工作正在进行。
内存资源和显存资源的管理,目前引擎中也有基本的资源管理,这点在前一篇 blog 中也提到了,如果对内存和显存资源进行动态的管理,旧有的方式就完全不适合了。对于纯粹的内存资源来说,流程比较简单,当请求一个资源时,送入加载资源队列,在 IO 加载线程中由 virtual file system 进行资源读取,当读取完成后送入资源加载结束队列,游戏线程(可能和渲染线程不是同一个)使用这个内存资源。当物理内存或者指定的内存耗尽时,可以通过回收一些资源来释放内存空间,释放规则可以是最近最少使用等原则。对于显存资源,由于不能直接从外存资源加载,所以流程比较复杂,不过主要思想还是 content streaming 的设计思路,首先一定是渲染线程发起资源的请求,如果在资源管理器中没有找到资源,则视为未加载资源,并将请求加入到资源加载队列,在 IO 加载线程中由 virtual file system 进行资源读取,当读取完成后送入资源加载结束队列,渲染线程创建设备资源(比如 texture、vertex buffer、index buffer、shader 等),创建成功则进行映射数据操作,并送入资源处理队列,在资源处理线程中对设备资源的映射数据指针进行处理和拷贝,完成后送入资源处理完成队列,在渲染线程中就可以正式使用这个设备资源了。当显存或者指定的显存耗尽时,如果还有渲染资源需要上传,就必须要释放设备资源,释放规则可以是最近最少使用等原则。但释放时可以保留内存数据,以便下次请求时直接进行设备资源的处理和上传。关于这部分还有很多细节内容,这需要在实际开发中逐步解决,这里只是提出初步的设想和方案,下次完成之后再详细探讨。 1月1日 Dream3D 引擎开发状态(二)暨引擎的 2007 回顾与 2008 展望昨晚 msn 上有人问我引擎开发的如何,是否有截图。说实话,我觉得截图只能说明引擎的的表象功能,比如渲染、工具的界面等,而这些还需要看渲染性能的好坏、工具的稳定与否、功能是否强大等等,只有通过使用引擎真正的开发一款游戏,才能了解一个引擎的真正能力。如果一个引擎能用几张截图就能说明其全部功能的话,我相信这是一个功能不完善的引擎。这一年的引擎开发让我也对引擎有了更进一步的了解,对引擎开发的认识也与以往有些不同。引擎到底是什么?如果放在5、6年前,这个问题还比较容易解释,但是近几年的 3D 引擎的发展,已经逐渐外延了引擎的概念。比如说 Unreal3 引擎,我个人认为它已经超越了传统的引擎的范畴,事实上它应该是 3D 次世代游戏的集成开发环境(IDE),从这一点来说,可以预见未来的 3D 引擎的发展将是越来越强大的开发工具和开发环境,使次时代游戏开发变得更容易、更快捷,缩短开发时间,减少开发成本。图形硬件技术的突飞猛进和 CPU\GPU 技术的不断发展,使得游戏本身变得越来越复杂,交互能力越来越强大,这直接导致了玩家希望能玩到越来越真实和更高交互能力的游戏的需求,从游戏开发商的角度来说,不断的超越竞争对手获得更大的市场回报使得更多更新的开发技术加入到游戏中,从而刺激玩家的的需求和硬件的发展,这是一个良性循环。言归正传,先说 2007 的回顾。起初我把 Dream 引擎定位在图形引擎,渲染方面达到本世代级别(farcry\unreal2),部分达到次世代级别(doom3\unreal3),因为是 3D 图形引擎不是游戏引擎,一开始这样定位也比较正确。不过目前 Dream 引擎已经不再是纯粹的 3D 引擎,(我做了层次结构,核心层还是图形,只是从整体功能来讲已经包含了游戏引擎的部分基础功能),实际上引擎的渲染功能在去年就开发完成了,到了现在也没有大改过。但是只有渲染是远远不够的,为了能达到灵活的图形表现,我用了前后近3个月的时间开发了材质系统(横跨 2006 ~ 2007),目的是使引擎具有自适应的渲染材质的能力,这样引擎本身不再限制渲染,而是通过编写不同的材质来设定渲染能力,这给后来的游戏开发带来了很大的方便,我也试过几种次世代渲染技术,有了这个系统,都比较容易的嵌入到引擎中。渲染是需要资源的,这又产生了对资源的管理,而我当初在开发资源管理时为了尽快地测试图形渲染功能,没有用太多的时间设计和开发,只是做了简单的加载、引用计数等功能,这部分一直是我的心病,我知道早晚要补上,2008 年上半年的工作目标之一就是开发全新的资源管理系统,稍后再细说。有了渲染、材质和简单的资源管理,静态的图形表现基本没有问题了,接下来就是动态表现,所以开发动画系统就很自然的成为引擎的 2007 年的又一个开发重点,在完成了渲染部分之后,引擎从 2 月份开始开发骨骼动画系统,后来逐渐加入了顶点动画、纹理动画、位移动画、粒子系统,而这些功能的开发,都对牵扯到资源管理系统,几乎每一次功能的开发都要对资源管理进行扩展和重构,这是当初轻视资源管理所带来的恶果,也反映出我们的引擎开发经验不足。相反,材质系统和渲染系统由于之前下足了功夫,所以没有太大变化。动画系统这部分另一个比较耗费时间的,当属 max 导出插件的开发,和上面一样,没有统一的设计和缺乏相关的开发经验也导致了 max 导出插件的多次重构,从功能上来说我自己也不满意,这部分也将作为 2008 年引擎重构一个重点。在这里详细说一下粒子系统,粒子系统进行过两次开发,第一次参考了 farcry 引擎的粒子编辑器,第二次参考了 unreal3 引擎的粒子编辑器,第二次的开发完全抛弃了原有的版本,因为看到了 unreal3 粒子编辑器的强大。从结构上新的粒子系统也与原有不同,尽管有些性能上的损失,但是带来的却是功能上的灵活和强大。实际上这之前引擎是有编辑器的,但是无论对于功能还是界面操作我都不满意,在第二次开发粒子编辑器的时候,我采用了 wxWidget 来开发界面,不过这也是无奈之举,MFC 或者 ATIL\WTIL 也可以做,这些我也用过,但是它们的结构复杂性太容易把代码搞乱,尤其对新人来说更是如此,MFC 是宏封装消息机制,ATL\WTL 是模板+宏,而 wxWidget 是完全面向对象的架构,虽然在实际开发上还有些麻烦,但是比较容易理解,门槛不高,但是其在架构上保证了一定的简易性,不象 MFC 使用那么麻烦和复杂,用好也不容易,如果让新人来写 MFC 程序恐怕难以维护。毕竟引擎不是开发完就可以了的,是需要不断维护的,否则人来人往,最后很容易就死掉。事实证明,一个毫无 GUI 经验的新手,用 wxWidget 很容易上手并且编写出很好的界面,至此,引擎的编辑器算是比较正规了。关于引擎的编辑器,也是 2008 年开发重点,目前我给引擎定位在这样几个编辑器:材质编辑器、粒子编辑器、地形编辑器、动画编辑器,其他的编辑器根据游戏需要再决定。我根据中国的上古十大神器来给引擎的编辑器命名,目前粒子编辑器被称为“神农鼎”,接下来还会有“轩辕剑、盘古斧、炼妖壶”等。2008 年另一个打算要做的扩展就是实现树形骨骼动画系统的功能,并且支持部分骨骼的 IK,最好能实现骨骼物理属性。引擎的性能优化也是 2008 年的开发内容,原计划在 2007 年下半年作性能优化,但目前游戏对引擎的功能要求比性能更加重要,所以可以放在游戏开发的后期再作性能优化。这部分主要包括数学库的计算性能,使用 CPU 扩展指令进行数学运算,多核技术的优化也在考虑之列,但如果要充分发挥多核的能力,需要对程序结构做一定的调整以适应多核,所以这部分还需要进行探索。前面提到了资源管理,实际上这部分内容还是不少的,从渲染层次上来说,资源管理是上层功能,资源管理需要更底层的文件系统来服务,所以这又引出文件系统。我打算实现抽象文件系统,屏蔽掉文件管理的细节差异,实现跨操作系统和包文件系统,12 月份就在作文件系统的设计和开发,期望在春节之前能全部开发完成。有了这一层次,在资源管理和文件系统之间就是引擎对象的序列化(或称之为持久化)层次,当然我不做那种面向对象的序列化,而且我也不认为序列化仅仅是指数据从内存到硬盘,从硬盘到内存也可以认为是序列化,只不过是方向不同,甚至可以是从网络到内存\硬盘,所以这个层次就是序列化框架,有个序列化接口,细节完全透明,目前已经实现了两种格式的序列化器:xml 和 Bin。对于需要序列化的引擎对象来说,直接使用这个接口来进行序列化。这个层次的存储的功能是通过抽象文件系统来完成的,每个对象要负责自己的数据序列。要达到这种程度,引擎还需要进行较大改动,因为这涉及到了引擎的很多个核心对象,同时还包括导出插件。有了这两个层次,资源管理的底层支持就完备了。动态的资源管理一直是我想要实现的功能,关于这部分功能以前也是没有什么思路,后来看了 云风 blog 上 的几篇关于资源管理的文章,再加上自己的思考和实际的需求,有了初步的设计,不过这需要在实际开发中去验证,同时后台资源加载也是我想要实现的功能,这对大型 mmo 来说也是一个必要的功能。 2008 年有许多工作要做,在这些工作中最重要的是目前我们在开发中的游戏,我们的目标是今年内能有一款 JCC 自己研发的游戏上市,,也是公司对上海研发方面的要求。2008 年公司对我的要求是负责管理整个游戏研发的技术,这就使我不仅要负责引擎的开发,还要负责游戏的开发,对我个人来讲是一个更大的挑战,我期望我能完成上面所说的引擎开发的内容,同时更希望能看到游戏的成功上市。道路曲折,而我喜欢富有挑战的生活,2008,我们来了! 12月6日 不养儿不知父母恩女儿随父母回东北已经快一个月了,每每打电话问寒问暖,父母都主动的说起萱萱的情况,并且一再的要我们放心。每当我了解到女儿无恙之后,才想起应该问问父母的情况,身体如何,最后还是要说回到萱萱身上。不养儿不知父母恩这句话我从小听听到大,说实话,现在有了孩子其实还是不知父母恩,如果知的话,每次打电话的内容就不会都是女儿了。萱萱每晚跟母亲一起睡,母亲觉轻,有点动静就醒,萱萱随我,晚上睡觉不老实,总翻来覆去的,所以母亲的睡眠质量可想而知了。最近这半年多的照看萱萱,母亲明显老了许多,如果不是因为新房空气不好的话,我也不想再麻烦母亲,原定的是明年五一之前接回女儿,但是想想母亲太劳累,要她自己照顾到五一会非常辛苦,所以最近几次通话我极力要求母亲在春节前来上海,其实也是我们太想萱萱了。 11月22日 小试多核技术昨日A项目经理跟谈起多核技术,说对引擎来说也许有一定效果,实际上引擎加入多核支持早在年初的一次引擎开发评审中韩国技术总监就提出过这样的意见,我原打算放在引擎开发后期再去实现。今天翻了一下 OpenMP 相关的知识,决定小试一下,测试结果还令人满意。我只是测试了引擎的例子系统更新部分,在 Core2 6750 CPU 上更新 4k 个粒子有接近 200 fps 的提升,由于其它开发人员的机器并不是物理双核,是逻辑双核(及超线程技术),我以为 OpenMP 对逻辑双核无用,结果运行发现还是有一定作用,看来我对超线程有认识上的误区。不过提升幅度并不大,4k 粒子更新只有 10 fps 左右的提升,并且两个逻辑核心都几乎是满负荷运行,而物理双核却只有 30% 左右的负荷。不论怎样,使用 OpenMP 多核技术确实对引擎运行效率有提升,而且现在大部分的计算机中即使不是多核也基本上是超线程,所以使用多核应该没有太大问题。不过在测试中我也发现 OpenMP 的缺陷,它对运算结果没有相互依赖的并行计算支持很好,但是如果有依赖关系,就要更改代码的实现方式,这会造成最终发布应用程序上的差别,所以多核技术还需要成熟,最好不要给程序员带来代码实现上的限制,vs2008 即将推出,希望在支持多核开发上有更大的改进,使多核技术在应用上更加成熟、方便。 11月16日 Dream3D 引擎开发状态(一)好久没没有在 Blog 上更新关于 Dream3D 引擎的开发状态了,主要是一直在忙。目前引擎的核心部分已经完成了 3D 图形游戏的基本功能,包括场景图管理、渲染系统、动画系统、粒子系统、资源管理、材质系统,工具部分包括 max 导出插件、粒子特效编辑器、实体编辑器。以上各个系统具体的功能还有许多,比如动画系统支持骨骼动画、顶点动画、uv 动画、轨迹动画,其中骨骼动画还支持动画混合、动画过渡、子动画、多动画,uv 动画支持任意 texture 层等等。实际上上述功能在 7 月份就已纪基本完成,核心部分在 5 月份已经完成,其中骨骼动画部分在 5 月份进行了重构,导出插件部分在 6 月份进行了重构,7 月份又进行了资源管理的重构,目前正在进行的是粒子特效系统的重构,重构是为了能拥有更强的编辑和扩展能力。引擎的结构也进行了多次的大大小小的调整,目前调整部分比较少的就是场景图管理、渲染系统、材质系统。 从 8 月份开始,公司的游戏项目开始使用 Dream3D 引擎开发游戏 Demo,9月末 Demo 第一版开发完成。在图形表现上并没有特别复杂之处,加了 PostGlow 的全屏特效,Full Scene 的 ShadowMap,VS 实现的草被风动的效果,uv 动画,引擎的动画过渡技术使角色运动起来更平滑。游戏中动态物体比较多,有大量的碰撞会同时发生,不过从测试的效果上看,性能还是令人满意的,这归功于 Bullet 物理引擎的高效。另外,我们还为游戏 Demo 开发了关卡编辑器,在开发之前我希望在编辑器中实时切换到游戏状态,这会给策划提供很大的便利。为了这个目标,我搭建了一个编辑器和游戏应用的框架,最后做到了能在编辑器中编辑数据之后实时切换到游戏,效果还算令人满意。 在这期间我尝试了几种次时代 3D 技术,比如 Deferred Shadow Map,基本的功能目前已经完成,不过还需要一些完善才能达到实用,首先是软阴影的实现,目前我采用随机采样点实现软阴影的边缘,但是是基于屏幕空间,这样会出现无论物体远近,阴影的边缘都是相同粒度的现象,对于离 Camera 较远的阴影效果不好,所以我打算采用基于 View 空间来进行点采样,这也是 Crysis 和 UE3 采用的技术。其次是对于阴影物体的选择问题,因为是基于后期处理,所以对于不需要产生阴影和不需要被投影的物体进行过滤,我考虑可以使用 Stencil buffer,不过如果和其他渲染技术结合的话,有可能会出现争用 Stencil Buffer 的情况,所以最好是用单独的 RT 保存 Shadow Mask。 |
|
|