GamerClass Shading系统设计【三】 需求-实用性

 

实用性:这个东西必须足够简单

用户如果能用简单的代码,就能够进行必要的渲染,那是最好的。

扩展性之类的话题我们先不考虑,首先,做引擎如果不能给引擎的调用者带来方便,那么引擎你做它干嘛?

易用性永远是第一位的,因为,如果引擎已经自成体系,那么多数用户不会去扩展已有的系统,而是会使用当前系统的部分假设,尽可能通过绕的方式,绕过引擎不合适去做的部分,进而达到目标。比如,一个引擎对动态加载的支持比较差,那么用户就可以选择是否使用这个引擎,或者当使用了这个引擎的时候,做一大堆一大堆的关卡,通过关卡的加载、卸载来模拟动态加载。当然,这个只是拿来做例子,如果哪个引擎没有提供方案解决动态加载的问题的话——灭了它!

如果一件事情,用户需要N个相互关联、相互影响的步骤才能够完成,那么这个设计就并非是简洁的。比如,某个调用如下:

A.MustDoBeforeBTask1();

B.DoMyTask1();

A.MustDoAfterBTask1();

C.DoMyTask();

B.DoMyTask2();

A.MustDoAfterBTask2();

……

这个对用户就是一种折磨。

我们发现,在易用性的选择上,必然的结果是,让用户调用34个以内的调用就可以解决所有的问题。函数调用越多,之间的关系就可能越复杂。也意味着这个关系发生在多个模块未经处理的结合部位,这是很危险的。如果模块A到模块B只有单一的,或者两三个数据通道(从A输入到B,从B输入到A),那么问题并不会特别复杂。但如果数据通道的数量达到了一定数量,那就会牵扯到通道之间先后顺序的影响。这种情况下,最好能将调用对用户黑箱。也就是设计模式中的Façade门面模式。

但是,本来我就必须要做那么多事情,怎么让调用减少呢?

比如,在一次渲染中,我必须做下面的事情:

1设置Shader

2根据Shader的输入需求,选择Vertex BufferIndex Buffer

3决定Primitive TypeVertex StartIndex Start等。

4决定材质和受光属性。

5提交Shader相关的参数。

6提交Auto Shader相关的参数。(如,World MatrixView Proj Matrix、当前视点位置、当前用户区大小……)

最后,开始渲染。为了进行一次渲染,必须要完成6步,而且,这6步还必须按照顺序运行。那么,对于用户而言,如果把6个函数顺次调用,就会是一个比较繁琐的工作。我就是要画个Box,给了你材质,给了你光照属性,我管你怎么渲染呢?!引擎的作用,就是用户告诉你,我要做什么,你就帮人家把事情搞定——悄悄的搞定。

这就必须要具体问题具体分析了,简化流程,首先需要简化概念。在上面的例子中,与Shader直接相关的有1256,与数据直接相关的有23456。它们的概念涉及到:ShaderVertex Buffer、材质、光照、Shader参数,自动Shader参数,Device……

结果是,为了进行一次渲染,我必须查阅7个系统的接口或者声明。对用户这是不公平的,因为我可能就只是想渲染出来一个方盒子。

我们可以试图简化一下这个模型。6可以由Device的数据查询,这样,因为信息我们都能取出来了,因此这个可以优先被封闭掉,不让用户看到。剩下的部分,主要描述了三个问题:Shader、数据、Shader和数据的流通,其中包括Shader到数据的流通(从Shader的输入需求,查询选择Vertex Buffer),也包括数据到Shader的流通。

这样,这6个步骤,我们可以让用户用下面的描述来表示:

ShaderProvider.Apply( DataProvider );

Shader Provider是什么?Data Provider是什么?是引擎提供给上层调用的,可扩展的模板。因为用户的需求虽然看似无穷,但对于每个项目,却总是可以穷举的。一个项目,决定了使用静态光影,那么渲染的模式一定都跟它有关,多也不过十数种,数十种。如果一个项目决定了使用动态光影,也是一样,渲染的模式是可穷举的。因此,只需要我们提供Shader ProviderData Provider的接口,由模板的提供者实现,那么,用户就只需要一句话就可以完成之前的6个步骤。

那么,这些Provider由谁提供呢?这个就是扩展性的问题了。

根据上面的描述,我们应该可以发现,易用性一个显而易见的原则就是:

将非用户考虑的事情装箱,交给引擎的扩展来处理。

如何判断一件事情究竟是否该用户考虑,这就会有很多的权衡了,无论如何,这种权衡不应该是由程序员来想的,或者说,不应该站在程序员的角度来考虑。因为用户不是程序员,也不希望做一个程序员。引擎说白了就是个工具,工具给人的第一感觉和第一印象,应该是强大,好用,简单。个人感觉,给用户考虑的事情越少,接口的设计就有可能越好。同时,应该把用户强制分成两个组分——一个组分是最终的使用者们,另一个组分是,对引擎的功能提供扩展的实现者们。引擎针对两者,应该是两套完全不同的政策。

回到GC的问题上来,Gamer Class遇到的问题,最大的方面就体现在易用性上,因为用户对于编辑器的需求非常重要。而在上层编辑器层次,不能有太多的概念,否则会冲淡编辑器本身的概念,而且还会引入很多由于顺序而导致的不确定性,增加处理的成本。Gamer Class希望,在制作的过程中,随时可以添加新的渲染物体和渲染模式,添加的过程可以足够简单。最好是继承出一个类,随便改改,然后就一切正常。什么Shader,什么算法,都滚一边去,剩下的最好只是纯粹的概念:勾上了动态光的选项,就要计算动态光,勾上了Light Map的选项,则就要计算Light Map

进行易用性的考虑之外,还需要对扩展性有清晰的认识,因为需求总是在变的,易用性和扩展性是相辅相成的,如果不考虑扩展性,需求一改,调用就需要修改,流程就需要修改,那么易用性就无从谈起。接下来,我们再说点废话,来看看GC所面临的扩展性的问题……

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值