stage3D 搭建2d图形引擎 (五)不同纹理的显示对象

在进入主题之前,我们先来回顾一下之前所走过的思路:

1.首先是为了绘制多个四边形,并且为了节省效率,我们将多个四边形的数据集中到一起,一次上传到缓冲,并且只执行一次绘制,一次呈现。

2.但是当需要纹理贴图时,我们发现颜色填充的四边形和贴图的四边形无法公用shader program,所以我们不得不将这两者分开,执行多次绘制,最后一次呈现。

在这篇文章中我们会延续以上的思路,继续探索下去,来处理多纹理的情况。

如果你仔细研究了前一篇文章中的ImageRender类,你会发现有一个很严重的问题,那就是一个ImageRender对象只有一个纹理对象,因而它只能批量渲染那些拥有相同纹理的Image对象。但是实际的问题中,你的应用越丰富,你所用到的纹理肯定也会越多。因此如果一个ImageRender对象只针对一个纹理,那么我们可能要创建很多的ImageRender对象才能满足实际的需求。

看起来很不应该,不是吗?还会有更好的方案吗?

这个问题并不是很好回答,按照我们目前的这种情况,答案是肯定的,我们没有别的选择,只能针对具有相同纹理的显示对象来进行批处理。但脱离我们之前的一些设定(主要是我们规定一个QuadVertex对象最多有8个数据),方法还是有的,也就是说可以同时批处理拥有不同纹理的显示对象,不过这不是本篇的主题。可以看下面的批注来了解大概内容。

当然单个纹理的批处理也不至于那么糟糕,虽然我们可能需要针对每一个纹理对象来制定一个ImageRender(它的本质是一个shader program,和有限的纹理样本寄存器),但是我们完全可以将具有关联性的纹理素材做在一张图片上,然后对不同的显示对象设置纹理坐标来获取正确的纹理内容。

说到这里也有必要跟Starling做一下对比,在Starling中,通过QuadBatch对象来渲染四边形组,这里所说的四边形当然也包括Image对象。因此,我们这里的QuadRender和ImageRender类似于Starling中的QuadBatch。区别只是在于QuadBatch这种设计更加的抽象,但并不是所有的四边形对象都是仅由一个QuadBatch来做渲染管理的,一个QuadBatch对象只能包含相似的四边形对象,这里相似的定义可以从RenderSuppor::batchQuad()这一方法中看出来:

 1 public function batchQuad(quad:Quad, parentAlpha:Number, texture:Texture=null, smoothing:String=null):void
 3 {
 4      if(mQuadBatches[mCurrentQuadBatchID].isStateChange(quad.tinted, parentAlpha, texture, smoothing, mBlendMode))
 6      {
 7            finishQuadBatch();
 8      }
 9             
10      mQuadBatches[mCurrentQuadBatchID].addQuad(quad, parentAlpha, texture, smoothing, mModelViewMatrix, mBlendMode);
12 }

我们看到,结束一个QuadBatch的条件是满足QuadBatch::isStateChange()执行结果为true,这个方法定义了四边形的“相似”。而我们看到,其中便有texture这一项。本篇文章只会涉及texture和smoothing两项,其他的以后涉及到再说。

话说回来,QuadBatch所做的抽象是有代价的,因为他要同时处理颜色填充四边形和贴图四边形,再加上其他的一些特性,他所处理的情况要复杂的多(当然也不是特别多,只是有这种倾向,呵呵),我们从学习的角度来看,完全可以将这些复杂性进行分离,为此我们建立一个抽象基类RenderBase:

 1 package psw2d.render
 2 {
 3     import flash.events.Event;
 4     
 5     import psw2d.Sparrow;
 6     import psw2d.core.Context2D;
 7 
 8     public class RenderBase
 9     {
10         public function RenderBase()
11         {
12             Sparrow.instance.addEventListener("contextCreated",onContextCreated);
13         }
14         
15         protected function onContextCreated(e:Event):void
16         {
17             
18         }
19         
20         public function render():void
21         {
22             
23         }
24     }
25 }

这个类将会作为QuadRender从而也是ImageRender的基类。这个类的重点在于render()这一方法上。其他的函数包括构造函数将在后面解释。

接下来修改ImageRender类。我们前面说每个ImageRender都针对一个Texture对象,因此我们在ImageRender的构造函数中加入一个Texture类型的参数:

1 public function ImageRender(texture:Texture)
2 {
3     super();
4     _texture = texture;
5     _texture && _texture.uploadData();
6 }

另外每一个Render对象(QuadRender or ImageRender)都对应着特有的shader program,而这些program在反复的绘制过程中是可以重复利用的,因此我们不应该每次都重复创建,应该将他们缓存起来,这样每个Render对象都应该有一个注册shader program的方法:

1 private static function registerPrograms():void
2 {
3 }

具体地可以参考Starling中的设计。这一方法的执行应该在Context3D建立之后(因为只有有了Context3D对象之后我们才能请求Program3D实例),因此我们可以在Render对象的构造函数中侦听相关的事件。于是就有了RenderBase中的构造函数的内容和侦听函数。

在下面放出的源码中,我们还调整了其他的一些内容,比如仿照Starling的设计,我们用一个叫做Sparrow,:),的类作为整个2d引擎的入口。同时我们用一个Context2D类来对Context3D进行了封装。

截图留念

注:

上面所说的,在一次批处理中处理多个纹理对象的问题,可以通过扩展顶点数据来实现,比如我们在原来的u,v之后再加上u1,v1这一坐标用来对应第二个纹理寄存器fs1中的纹理。

其实有了RenderBase这样的一种抽象,我们就可以走的更远一些,比如我们可以扩展五边形的批处理,即使是四边形,我们也可以用另外的一种批处理方式来渲染,比如增加每个顶点的数据个数来处理多贴图的批处理。

源码

posted @ 2012-11-30 21:32  Joe Physwf  阅读(909)  评论(7编辑  收藏  举报