stage3D 搭建2d图形引擎 (四)静态文理贴图

接下来我们该构建带有贴图的显示对象了。

为了清楚的阐释问题,我们还是从最基本的程序开始,在本博的第一篇文章中介绍了最基本的构建一个四边形的程序,现在我们对其进行稍作修改即可让其显示贴图:

  1 package test
  2 {
  3     import com.adobe.utils.AGALMiniAssembler;
  4     import com.adobe.utils.PerspectiveMatrix3D;
  5     
  6     import flash.display.Bitmap;
  7     import flash.display.Sprite;
  8     import flash.display.Stage3D;
  9     import flash.display3D.Context3D;
 10     import flash.display3D.Context3DProgramType;
 11     import flash.display3D.Context3DTextureFormat;
 12     import flash.display3D.Context3DVertexBufferFormat;
 13     import flash.display3D.IndexBuffer3D;
 14     import flash.display3D.Program3D;
 15     import flash.display3D.VertexBuffer3D;
 16     import flash.display3D.textures.Texture;
 17     import flash.events.Event;
 18     import flash.geom.Matrix3D;
 19 
 20     public class TexturePlane2DTest extends Sprite
 21     {
 22         private var stage3D:Stage3D;
 23         private var context3D:Context3D;
 24         private var vertexBuffer:VertexBuffer3D;
 25         private var indexBuffer:IndexBuffer3D;
 26         
 27         [Embed(source="../texture/flower.png")]  28         private var Flower:Class;  29         
 30         public function TexturePlane2DTest()
 31         {
 32             super();
 33             stage?onAddToStage(null):
 34             
 35             addEventListener(Event.ADDED_TO_STAGE,onAddToStage);
 36         }
 37         
 38         private function onAddToStage(e:Event):void
 39         {
 40             stage3D = stage.stage3Ds[0];
 41             stage3D.addEventListener(Event.CONTEXT3D_CREATE,onContext3DCreated);
 42             stage3D.requestContext3D();
 43         }
 44         
 45         private function onContext3DCreated(e:Event):void
 46         {
 47             context3D = stage3D.context3D;
 48             context3D.configureBackBuffer(stage.stageWidth,stage.stageHeight,2,true);
 49             vertexBuffer = context3D.createVertexBuffer(4,5);//只要五个数据  50             indexBuffer = context3D.createIndexBuffer(6);
 51             
 52             var vertexData:Vector.<Number>;
 53             var indexData:Vector.<uint> ;
 54             /**    1 - 2
 55             *      | / |
 56             *      0 - 3
 57              **/
 58             vertexBuffer.uploadFromVector(Vector.<Number>([
 59                 -1,-1,5, 0,1,
 60                 -1,1, 5,  0,0,
 61                 1,1, 5,   1,0,
 62                 1,-1,5,  1,1
 63             ]),0,4);
 64             indexBuffer.uploadFromVector(Vector.<uint>([
 65                 0,1,2,2,3,0
 66             ]),0,6);
 67             
 68             context3D.setVertexBufferAt(0,vertexBuffer,0,Context3DVertexBufferFormat.FLOAT_3);
 69             context3D.setVertexBufferAt(1,vertexBuffer,3,Context3DVertexBufferFormat.FLOAT_2);
 70             
 71             var flower:Bitmap = new Flower() as Bitmap;
 72             var texture:Texture = context3D.createTexture(flower.width,flower.height,Context3DTextureFormat.BGRA,true);
 73             texture.uploadFromBitmapData(flower.bitmapData,0);
 74             context3D.setTextureAt(0,texture);
 75             
 76             var vertexAssembler:AGALMiniAssembler = new AGALMiniAssembler();
 77             var fragmentAssembler:AGALMiniAssembler = new AGALMiniAssembler();
 78             var vertexSrc:String = "m44 op,va0,vc0 \n" +
 79                 "mov v0,va1";
 80 
 81             var fragmentSrc:String = "tex ft0,v0,fs0 <2d,nearest> \n" +
 82                 "mov oc,ft0";  83             vertexAssembler.assemble(Context3DProgramType.VERTEX,vertexSrc);
 84             fragmentAssembler.assemble(Context3DProgramType.FRAGMENT,fragmentSrc);
 85             
 86             var program:Program3D = context3D.createProgram();
 87             program.upload(vertexAssembler.agalcode,fragmentAssembler.agalcode);
 88             context3D.setProgram(program);
 89     
 90             addEventListener(Event.ENTER_FRAME,onEnterFrame);
 91         }
 92         private var modelView:Matrix3D = new Matrix3D();
 93         
 94         private function onEnterFrame(e:Event):void
 95         {
 96             context3D.clear(0,0,0,1);
 97             
 98             var pm:PerspectiveMatrix3D = new PerspectiveMatrix3D();
 99             pm.identity();
100             pm.perspectiveFieldOfViewLH(1,stage.stageWidth/stage.stageHeight,1,10000);
101             context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,pm,true);
102             
103             context3D.drawTriangles(indexBuffer);
104             context3D.present();
105         }
106     }
107 }

变化的部分我已经突出显示。可以看出与之前的程序不同的是,我们用纹理坐标(两个数据)取代了原来的颜色值(三个数据),因此一个顶点最多需要5个数据。

另外一个关键的改变是在片段着色器中我们加入了一个新的运算:

1 "tex ft0,v0,fs0 <2d,nearest> \n" 
2 "mov oc,ft0";

tex是纹理采样运算,第一行的运算是将纹理采样寄存器fs0中的数据采样到临时纹理寄存器ft0中,括号里的是一组flag,用来底层要如何采样。更多

显示结果:

stage3D 搭建2d图形引擎 (四)静态文理贴图_第1张图片

总体来说,这不算一件困难的事。但是当我们要将这些加入2d引擎中,就需要考虑的更多了。最关键的一点是纹理贴图有其针对的片段着色器程序,这同普通的颜色填充的显示对象是不能共享的,因此需要创建不同的program。

在starling2d中,静态纹理贴图对象Image被处理成Quad的子类,换句话说,Image只是一个贴了位图数据的四边形。这样的处理有一个好处就是,他和普通的Quad对象公用一个顶点缓冲,因此只需一次上传操作,从而避免了额外的效率开销。

一下是我们简单构建的Image类:

 1 package psw2d.display
 2 {
 3     import psw2d.texture.Texture;
 4     /**
 5      * 
 6      * @author joe
 7      * 
 8      */
 9     public class Image extends Quad
10     {
12         private var _texture:Texture;
14 public function Image(texture:Texture) 15 { 16 if(texture) 17 { 18 var w:Number = texture.width; 19 var h:Number = texture.height; 20 21 super(w,h,0xFFFF0000); 22 23 _vertexData.setTexCoords(0,0.0,0.0); 24 _vertexData.setTexCoords(1,1.0,0.0); 25 _vertexData.setTexCoords(2,1.0,1.0); 26 _vertexData.setTexCoords(3,0.0,1.0); 27 } 28 else 29 { 30 throw ArgumentError("参数不能为空!"); 31 } 34 _texture = texture; 35 } 36 37 public function get texture():Texture { return _texture; } 38 } 39 }

 这里面我们省略了许多staling中的细节,为的是突出核心问题。与Image类相关的两个类分别为QuadVertex和Texture。其中QuadVertex需要在原有的基础之上修改:

 1 package psw2d
 2 {
 3     import flash.geom.Matrix;
 4 
 5     public class QuadVertex
 6     {
 7         public static const ELEMENTS_PER_VERTEX:int = 8;//每个顶点的数据个数  8         public static const POSSION_OFFSET:int =0;//位置坐标的偏移量  9         public static const COLOR_OFFSET:int = 2;//颜色的偏移量 10         public static const TEX_COORDS_OFFSET:int = 6;//纹理坐标的偏移量 11         
12         private var _rawData:Vector.<Number>;
13         
14         public function QuadVertex()
15         {
16             _rawData = new Vector.<Number>(4*ELEMENTS_PER_VERTEX,true);
17         }
18         
19         public function setPosition(index:uint,x:Number,y:uint):void
20         {
21             var offset:int = ELEMENTS_PER_VERTEX * index + POSSION_OFFSET;
22             _rawData[offset] = x;
23             _rawData[offset+1] = y;
24         }
25         
26 public function setTexCoords(index:uint,u:Number,v:uint):void 27 { 28 var offset:int = ELEMENTS_PER_VERTEX * index + TEX_COORDS_OFFSET; 29 _rawData[offset] = u; 30 _rawData[offset+1] = v; 31 } 32         
33         public function setColor(index:uint,color:uint):void
34         {
35             var offset:int = ELEMENTS_PER_VERTEX * index + COLOR_OFFSET;
36             var alpha:Number = (color >> 24 ) & 0xFF;
37             var r:Number = (color >> 16) & 0xFF;
38             var g:Number = (color >> 8) & 0xFF;
39             var b:Number = (color & 0xFF);
40             
41             r/=0xFF;
42             g/=0xFF;
43             b/=0xFF;
44             alpha/=0xFF;
45             
46             _rawData[offset] = r;
47             _rawData[offset+1] = g;
48             _rawData[offset+2] = b;
49             _rawData[offset+3] = alpha;
50         }
51         
52         public function get rawData():Vector.<Number>
53         {
54             return _rawData;
55         }
56 
57         public function set rawData(value:Vector.<Number>):void
58         {
59             _rawData = value;
60         }
61 
62         public function transformVertex(modelMatix:Matrix):void
63         {
64             var x:Number,y:Number;
65             for(var i:int=0; i<4; ++i)
66             {
67                 x = _rawData[i*ELEMENTS_PER_VERTEX];
68                 y = _rawData[i*ELEMENTS_PER_VERTEX+1];
69                 _rawData[i*ELEMENTS_PER_VERTEX] = modelMatix.a * x + modelMatix.c * y + modelMatix.tx;
70                 _rawData[i*ELEMENTS_PER_VERTEX+1] = modelMatix.b * x + modelMatix.d * y + modelMatix.ty;
71             }
72         }
73         
74         public function copyTo(target:QuadVertex):void
75         {
76             for(var i:uint;i<_rawData.length;++i)
77             {
78                 target.rawData[i] = _rawData[i];
79             }
80         }
81     }
82 }

修改的部分已经突出显示。首先我们定义了几个常量,我们看到现在每个顶点的数据个数已经增加到了8个,前两位为坐标x,y,接下来是颜色值r,g,b,a,最后是纹理坐标u,v。另外,增加了一个设置纹理坐标的方法:setTexCoords(index:uint,u:Number,v:Number):void。

至于Texture类则是一个新的类,它是一个抽象类(在starling中,这个类除了作为抽象基类之外,还作为创建纹理的工厂来使用):

 1 package psw2d.texture
 2 {
 3     import flash.display3D.Context3DTextureFormat;
 4     import flash.display3D.textures.Texture
 5 
 6     public class Texture
 7     {
 8         private var isRepeat:Boolean;
 9         
10         public function Texture()
11         {
12         }
13         public function uploadData():void {}
14         
15         public function get repeat():Boolean { return isRepeat; }
16         
17         public function get width():Number { return 0; }
18         
19         public function get height():Number { return 0; }
20         
21         public function get format():String { return Context3DTextureFormat.BGRA; }
22         
23         public function get base():flash.display3D.textures.Texture { return null; }
24         
25         public function get mipMapping():Boolean { return false; }    
26     }
27 }

可以看到,它很简单,在本篇文章中,需要着重注意两个方法一个是uploadData(),另一个则是base属性。

针对于位图数据我们构建了Texture的一个子类BitmapTexture:

 1 package psw2d.texture
 2 {
 3     import flash.display.Bitmap;
 4     import flash.display.BitmapData;
 5     import flash.display3D.Context3DTextureFormat;
 6     import flash.display3D.textures.Texture
 7         
 8     public class BitmapTexture extends psw2d.texture.Texture
 9     {
10         private var _data:BitmapData;
11         private var _mipMapping:Boolean;
12         private var _width:Number;
13         private var _height:Number;
14         private var _base:flash.display3D.textures.Texture;
15         
16         public function BitmapTexture(base:flash.display3D.textures.Texture,data:*,mipMapping:Boolean)
17         {
18             if(data is Bitmap)
19             {
20                 _data = (data as Bitmap).bitmapData;
21             }
22             else if(data is BitmapData)
23             {
24                 _data = data as BitmapData;
25             }
26             else
27             {
28                 throw "无效的位图数据格式!";
29             }
30             _base = base;
31             _mipMapping = mipMapping;
32             _width = _data.width;
33             _height = _data.height;
34         }
35         override public function uploadData():void
36         {
37             _base.uploadFromBitmapData(_data,0);
38         }
39         override public function get base():flash.display3D.textures.Texture { return _base; }
40         override public function get format():String { return Context3DTextureFormat.BGRA; }
41         override public function get width():Number { return _width; }
42         override public function get height():Number { return _height; }
43         override public function get mipMapping():Boolean { return _mipMapping; }
44     }
45 }

它接受一个原生的Texture对象和一个Bitmap或者BitmapData的数据做为参数。uploadData()方法实际是调用原生Texture的uploaFromBitmapData方法来讲位图数据上传至显卡。

至此纹理部分我们基本准备好了,虽然表面上我们做了很多,但是所有的这些依然都是围绕一个渲染流程在走。而渲染纹理和渲染颜色填充的根本区别就是:颜色填充只是单纯的将颜色搬到顶点输出中(并计算中间差值,一般为线性),即:

1 “mov oc,v0”

而纹理渲染,则要多考虑一个因素,那就是纹理坐标,虽然也是将数据搬到oc中,但是它首先是根据纹理坐标在纹理数据上找到相应的位置,再把该位置的数据搬到输出中:

"tex ft0,v1,fs0 <2d,nearest> \n" +
"mov oc,ft0";

因此相对于之前的Quad渲染,Image的渲染要做的只是改变片段着色器程序(当然,相对于Quad,Image多了纹理和纹理坐标的数据,这也需要考虑进去),因此我们可以将QuadRender修改一下:

 1 package psw2d
 2 {
 3     import com.adobe.utils.AGALMiniAssembler;
 4     
 5     import flash.display3D.Context3D;
 6     import flash.display3D.Context3DBlendFactor;
 7     import flash.display3D.Context3DCompareMode;
 8     import flash.display3D.Context3DProgramType;
 9     import flash.display3D.Context3DVertexBufferFormat;
10     import flash.display3D.Program3D;
11     
12     import psw2d.display.Image;
13     import psw2d.texture.Texture;
14     
15     public class ImageRender extends QuadRender
16     {
17         private var texture:Texture;
18         
19         public function ImageRender(context3D:Context3D)
20         {
21             super(context3D);
22         }
23         
24 public function addImage(image:Image):Image 25 { 26 texture = image.texture; 27 texture.uploadData(); 28 return addQuad(image) as Image; 29 } 30         
31 override public function rebuildBuffer():void 32 { 33 super.rebuildBuffer(); 34 _context3D.setTextureAt(0,texture.base); 35 _context3D.setVertexBufferAt(2,_vertexBuffer,QuadVertex.TEX_COORDS_OFFSET,Context3DVertexBufferFormat.FLOAT_2); 36 } 37         
38         override public function setProgram():void
39         {
40 var vertexSrc:String = "m44 op,va0,vc0 \n" + 41 "mov v0,va1 \n" + 42 "mov v1,va2"; 43 var fragmentSrc:String = "tex ft0,v1,fs0 <2d,nearest> \n" + 44 "mov oc,ft0"; 45             var vertexAssembler:AGALMiniAssembler = new AGALMiniAssembler();
46             var fragmentAssembler:AGALMiniAssembler = new AGALMiniAssembler();
47             vertexAssembler.assemble(Context3DProgramType.VERTEX,vertexSrc);
48             fragmentAssembler.assemble(Context3DProgramType.FRAGMENT,fragmentSrc);
49             var program:Program3D = _context3D.createProgram();
50             program.upload(vertexAssembler.agalcode,fragmentAssembler.agalcode);
51             _context3D.setProgram(program);
52         }
53         
54 override public function render():void 55 { 56 setProgram(); 57 rebuildBuffer(); 58 _context3D.drawTriangles(_indexBuffer,0,_quads.length * 2); 59 60 _context3D.setTextureAt(0,null); 61 _context3D.setVertexBufferAt(0,null); 62 _context3D.setVertexBufferAt(1,null); 63 _context3D.setVertexBufferAt(2,null); 64 } 65         
66     }
67 }

 

事实上我们是继承了QuadRender来创建了ImageRender,在新的类中,我们增加了addImage方法,重写了rebuildBuffer(),setProgram()和render()方法,特别需要注意的是render()方法,在重写之后,我们去掉了context3D.clear()和context3D.present()(我们将它转移到QuadTest中),并增加了一些清除数据的操作,这一点很重要。你会发现这里有很多的不合理,但现在我们的目的只是先让Image显示出来。

为了让颜色填充的四边形和贴图的四边形能够同时显示,我们也需要对QuadRender做同ImageRender类似的调整,这里就不把代码贴出来了,可以从源码中下载。

现在来看QuadTest类:

 1 package test
 2 {
 3     import flash.display.Bitmap;
 4     import flash.display.Sprite;
 5     import flash.display.Stage3D;
 6     import flash.display3D.Context3D;
 7     import flash.display3D.Context3DTextureFormat;
 8     import flash.events.Event;
 9     
10     import psw2d.ImageRender;
11     import psw2d.QuadRender;
12     import psw2d.display.Image;
13     import psw2d.display.Quad;
14     import psw2d.texture.BitmapTexture;
15     import psw2d.texture.Texture;
16     
17     public class QuadTest extends Sprite
18     {
19         private var stage3D:Stage3D;
20         private var context3D:Context3D;
21         private var qRender:QuadRender;
22         private var imageRender:ImageRender;
23         
24         [Embed(source="../texture/flower.png")]
25         private var Flower2:Class;
26         
27         public function QuadTest()
28         {
29             stage?onAddToStage(null):
30             addEventListener(Event.ADDED_TO_STAGE,onAddToStage);
31         }
32         
33         private function onAddToStage(e:Event):void
34         {
35             stage3D = stage.stage3Ds[0];
36             stage3D.addEventListener(Event.CONTEXT3D_CREATE,onContext3DCreated);
37             stage3D.requestContext3D();
38         }
39         private var q:Quad;
40         private var q2:Quad;
41         private var image:Image;
42         
43         private function onContext3DCreated(e:Event):void
44         {
45             context3D = stage3D.context3D;
46             //关闭深度测试,,如果想要开启,必须调用setDepthTest接口来开启alpha混合
47             context3D.configureBackBuffer(stage.stageWidth,stage.stageHeight,2,true);
48             
49             qRender = new QuadRender(context3D);
50             
51             q = new Quad(100,100,0x7FFF0000);
52             q.x = 100;
53             q.y = 100;
54             qRender.addQuad(q);
55             q2 = new Quad(100,100,0x7F00FF00);
56             q2.x = 100;
57             q2.y = 100;
58             qRender.addQuad(q2);
59             
60             var flower:Bitmap = new Flower2() as Bitmap;
61             imageRender = new ImageRender(context3D);
62             var tex:Texture = new BitmapTexture(
63                 context3D.createTexture(flower.width,flower.height,Context3DTextureFormat.BGRA,false),
64                 flower,false) as Texture;
65             image = new Image(tex);
66             image.x = 200;
67             image.y = 200;
68             imageRender.addImage(image);
69             imageRender.setMatrix(stage.stageWidth,stage.stageHeight);
70             
71             qRender.setMatrix(stage.stageWidth,stage.stageHeight);//只需要执行一次设置正交投影矩阵
72 
73             addEventListener(Event.ENTER_FRAME,onEnterFrame);
74         }
75 private function onEnterFrame(e:Event):void 76 { 77 context3D.clear(0,0,0,1); 78 79 q.x++; 80 q2.x+=0.5; 81 82 image.x++; 83 84 qRender.render(); 85 imageRender.render(); 86 87 context3D.present(); 88 } 89     }
90 }

在这个类中我们实例化了一个ImageRender和一个Image,并将后者添加到前者中,重点看onEnterFrame方法,将clear()方法和present()方法转移到了这里,这也就是说QuadRender和ImageRender所做的其实只是在缓冲里面绘制,最后在所有的Render渲染结束之后,统一调用present()方法将缓冲数据上传到显示中。

最后给出一个Demo图:

stage3D 搭建2d图形引擎 (四)静态文理贴图_第2张图片

源码

你可能感兴趣的:(stage3d)