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 的数据比较多,例如 DiffuseEmissiveSpecularSp PowerNormalDepth 等,需要多张RT来保存,这就意味着需要占用很多的显存来保存这些数据,即使通过一些合并数据的压缩技巧,对显存的占用量还是不小。为了解决这个问题,《Shader X》系列的编写者 Wolfgang Engel 提出了Light Pre-Pass Renderer技术,实际上就是 Pre-Pass Lighting,主要的思想是:在G-Buffer的生成阶段,只输出光照计算的必要参数,比如 Sp PowerNormalDepth等,在光照阶段通过前一步生成的 G-Buffer和光源参数生成屏幕空间Lighting RT,最后再绘制一遍物体,在绘制物体时获取物体表面的材质的 DiffuseEmissiveSpecular 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。在实现的过程中有几个细节需要注意:

    1. G-Buffer 的格式,DreamEngine 2为了考虑到静态光照和动态光照的完美结合,我在 G-Buffer 阶段输出了4RT,分别是:DepthAmbient Color(Alpha 标识 Emissive)Normal & SPRGB-NormalAlpha-SP)、Light Mapping。所有RT都是32位格式,其中 Ambient Color 存储材质中的 Ambient Emissive Color,通过 Alpha 标识是否自发光。Normal & SP 存储静态物体的法线和材质中的高光系数,由于只有Alpha 通道存储SP,所以SP 的范围是从0~255,不过通过和美术的交流,发现这个范围一般也够用了。
    2. 性能,主要集中在光照阶段,剔除无用的像素。通过Stencil Buffer 来除不需要光照的像素,其实本质上是为了渲染的正确性,但由于需要先设置Stencil Buffer,这样在接下来的光照阶段可以利用图形硬件的 Early-Stencil Culling 技术剔除无效的像素,提高 PS 性能。对于方向光、天光则无法利用这个优势,只能全屏渲染。
    3. 光体的设置,Point Light 我采用球体计算光照,Spot Light 用四棱台代表光照范围。对于 View Frustum和光体的相交关系,一开始我使用判断球体和四棱台边界设置光体三角形的裁剪模式,但发现无论怎样设置,在 View Frustum的近截面和光体相交都会产生不正确的光照结果,毕竟光体使用多边形来描述的,几何上的精确无法在这里发挥作用,而且还导致代码中复杂的判断机制,最后索性全部使用无背面剔除渲染光体,在主流平台上经过测试发现性能并没有明显变化,代码也简化了许多。
    4. Shadow,对于阴影来说这个技术没有丝毫的改进,目前我依然采用每光源的阴影的后期处理,DirectionSkyLight采用 Deferred ShadowPoint\Spot 采用模板缓冲+渲染光体渲染阴影。实际上 DE中方向光\天光阴影生成一直都是最原始的 SM 做法,只是动态计算了 Shadow Caster 的包围盒作为 Light Space View Project Matrix 的依据。但场景规模较大时,Shadow 效果就差了,考察了当前比较流行的做法,PSMTSMLiPSMCSM 等,发现 PSMTSMLiPSM Camera 变化时阴影抖动比较严重,CSM 比较好的解决了抖动问题,CryEngine2 以及一些商业游戏使用了这个技术,但是却需要更多地 VRAM,考虑到我们目前项目的实际情况,视角相对固定,极少看到大范围的场景,所以打算在原始 SM上作些改进,目前主要是选取潜在投影体的问题,之前的做法是只要场景中的物件产生阴影,我们就忽略 Camera Fustum Culling,但这样的问题是产生的 Shader Caster BV 较大,直接导致 Shadow 的精度降低,所以这需要使用稍大些的 Frustum 来做 Culling,这样可以大大缓解这个问题,对我们目前的项目来说也够用了。
    5. Depth Buffer,由于引擎的多个渲染阶段都是基于 Image Space的,这就需要通过 Depth Buffer 还原世界空间位置,对于 LPP DS,更需要获得像素的世界空间位置来进行光照计算。之前引擎中的 Depth 格式采用的是齐次化后的世界空间 z,但这是需要进行一次 ViewProjInv矩阵变换才能还原出世界空间位置,对于后期处理来说开销较大。所以这部分的性能问题使我一直耿耿于怀,这次趁着 LPP的实现机会彻底改变了这种现状,现在我采用存储Normalized View Space z,也就是 View Space z/ Frustum ZFar,这是一个视空间 z 的线性比值,如果已知Camera Pos和视空间的Pos,简单的向量加法即可求得世界空间位置,视空间Pos可以通过 Frustum 的远截面的四个顶点(世界空间位置)到Camera Pos 的向量求得,以下简称FarPlaneCornerDir,在渲染屏幕四边形时,将FarPlaneCornerDir存储在矩形的四个顶点中,在 VS 中传给PS,在 PS中: World z = tex2d(DepthSampler, uv).r * FarPlaneCornerDir (插值后的)+ CameraPos, 这样就只需要一次乘法和向量加法就完成变换,大大提高还原时的性能。
Advertisements
此条目发表在DreamEngine开发手札分类目录。将固定链接加入收藏夹。

One Response to Dream Engine 2 开发近况

  1. Windy说道:

    性能怎么样呢? 我是觉得现在入门级的显卡跑pixel shader挺够呛

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  更改 )

Google+ photo

You are commenting using your Google+ account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

w

Connecting to %s