3D游戏引擎相关的一些随笔(1)
渲染状态排序机制方面,应该尽可能的包装所有的渲染状态,优化时会用到。可以对状态进行分类,对不同分类分别求hash,再对总体状态包求hash。进行状态排序的时候,首先用状态包的hash对比,如果不同再分部分对比hash。为了防止hash碰撞,可以同时使用两三个不同的hash方法,减少碰撞概率。暴雪对于文件包都敢用这种方法,对于容错度更高的渲染状态,又为何不可呢。
Technique -> Pass式的材质脚本系统被证明是非常有效的。Technique里可以添加一个属性来让程序能够找到需要的Technique。比如延迟渲染的时候,程序会自动找到有GBufferPass属性的Technique。这样便将前向渲染和延迟渲染统一起来。
延迟渲染不等于全部都用相同的材质,即便是延迟渲染,也有很多不同情况,比如有无边缘光、是否有Detail贴图、是否有Parallax Occlusion Mapping之类的。
Material中应该有两类参数,Shared的参数和Mutable的参数。对Shared参数进行赋值的时候,会影响到这个Material所有的实例。如果不想产生这种影响,则需要Break Instance,即克隆一个参数相同的材质,这么做会让材质失去与资源的联系。对于Mutable的参数,则是与每个Instance关联的,不会产生全局影响。大部分参数都是Shared的,比如普通的材质参数如高光强度、Diffuse贴图之类的。而通常一些由程序自动赋值的参数则应该是Mutable的,如LightMap相关参数、Shadow Map相关参数等等。
Shader模板概念挺好。可以通过一组宏参数来裁剪Shader,使Shader尽可能高效。不过Shader模板的宏参数应该和其他Material参数处于并列的地位,而不是更高。一个Shader应该对应一种材质,而不是多种材质。宏参数和值参数的区别应该被埋在底层。
在使用Shader模板的情况下,宏参数通常很多,要将所有的宏参数对应的Shader预编译出来是不可能的。因此Shader在其被使用到的时候才编译。然而,这样在运行时因为编译Shader而产生剧烈的“卡”的现象。因此引入ShaderCache机制,即将运行时编译的Shader储存下来,再以后启动的时候读入。另外,编译Shader的时候使用外部程序进行编译,然后适当使用代理模式,应该能解决“卡”的问题。
资源更新机制很重要。应该建立一套统一的资源更新机制,把符合条件的东西都纳入资源的范畴。被当做资源的条件如下:在外存中有其原始形式的数据存在;外存中的数据与某种运行时对象有对应关系;运行时对象的一部分数据可以通过外存中的数据进行更新而不影响其运作。因此资源可以使材质、贴图和模型等等。可以在运行时重载单个资源,而不用重新创建这个资源的运行时对象。
Shader的值参数应该通过变量名指定,而不是通过寄存器。对于引擎的使用者来说,指定寄存器编号是不友好的方式。
3D引擎总是需要一套基本的碰撞检测机制,比如射线、几何体、包围和之间的相交检测。
一个引擎的工具链中可能会有很多编辑器,像什么场景编辑器、角色编辑器、动画编辑器、特效编辑器等等。这些编辑器应该有一套统一的资源管理与更新机制。编辑器之间尽可能采用数据耦合,即通过文件监控与资源更新机制完成编辑器之间的配合。如角色编辑器对角色进行编辑以后保存,文件监控机制发现角色资源被修改,于是启动资源重导入流程,并且发送消息给场景编辑器要求其更新资源,场景编辑器更新资源。不要每个编辑器包含自己的文件更新机制,这样会出现资源导入逻辑不清晰的情况。简单说就是:资源监控系统对Asset进行“拉”(Asset导入)操作,并且把导入生成资源“推”到相应的编辑器中。

