前一篇文章简单地构建了一个四边形,当然这个四边形还不完善,接下来我们将继续完善它。
我们看到,1,这个四边形的尺寸并不是真正的视觉尺寸,2,还有无法通过改变四边形的坐标来移动它,3,也不能对他进行缩放。下面我们就从这三个方面来完善它。
首先是尺寸,我们希望在给四边形指定的宽高数值就是他在屏幕上显示的像素大小。我们知道我们在屏幕上看到的图像实际上是经过一个矩形变换过的,在QuadRender的setMatrix方法中:
1 var pm:PerspectiveMatrix3D = new PerspectiveMatrix3D();
2 pm.perspectiveFieldOfViewLH(1,1,1,10000);
3 _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,pm,true);
我们创建了一个透视矩阵,并给他设定了一些数据。但实际上这是没有必要的,透视矩阵是在3D空间中才会发挥作用的东西,因为3D空间中有z轴方向这一自由度,但这一个自由度在2D空间中被固定死,所以我们认为这个自由度不存在,实际上我们需要的只是一个普通的正交投影矩阵,所谓正交投影,说白了其实就是忽略三维中的一维,取一个物体的某个剖面。我们将setMatrix方法修改如下:
1 public function setMatrix(sW:Number,sH:Number):void
2 {
3 var pm:Matrix3D = new Matrix3D(Vector.<Number>(
4 [
5 2/sW,0,0,0,
6 0,-2/sH,0,0,
7 0,0,0,0,
8 1,-1,0,1
9 ]));
10 _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,pm,true);
11 }
其中sW,sH是通过外部传递的参数,代表舞台的宽和高,而我们知道一个宽高都为2(-1,1)的四边形在不经过任何变化之后投影到屏幕上时,应该是正好充满整个屏幕的,因此为了将其转换成2个像素的大小需要除以舞台的宽高。
至此矩形的尺寸问题就解决了。另外我们还调整了2D空间的y轴方向(-2/sH),以及舞台的原点(最后一行的1,-1)。这样渲染出来的显示对象同传统的flash显示对象就有了相同的特征了。
接下来,我们讨论三角形的移动问题。
我们知道能够影响显示对象的位置的有两个因素,一个是顶点数据另一个是变换矩阵,但是考虑到不同的显示对象可能具有不同的变换矩阵,所以无法通过统一的变换矩阵来处理移动问题。事实上,我们是通过变换矩阵来处理初始化给定的顶点数据最后形成新的顶点数据来达到改变显示对象的位置的目的的。同时矩阵不仅能处理位置,还能处理缩放,于是我们将Quad类修改如下:
1 package psw2d
2 {
3 import flash.geom.Matrix;
4
5 /**
6 * 0 - 1
7 * | / |
8 * 3 - 2
9 * @author Physwf
10 *
11 */
12 public class Quad
13 {
14 private var _x:Number;
15 private var _y:Number;
16 private var _width:Number;
17 private var _height:Number;
18 private var _scaleX:Number = 1;
19 private var _scaleY:Number = 1;
20 private var _color:uint;
21
22 private var _modelMatrix:Matrix;
23 private var _isMatrixDirty:Boolean = false;
24
25 private var _vertexData:QuadVertex;
26
27 public function Quad(w:Number,h:Number,color:uint=0)
28 {
29 _x = 0;
30 _y = 0;
31 _width = w;
32 _height = h;
33 _color = color;
34
35 _vertexData = new QuadVertex();
36 _vertexData.setPosition(0,0,0);
37 _vertexData.setPosition(1,_width,0);
38 _vertexData.setPosition(2,_width,_height);
39 _vertexData.setPosition(3,0,_height);
40 _vertexData.setColor(0,0xFF0000);
41 _vertexData.setColor(1,0x00FF00);
42 _vertexData.setColor(2,0x0000FF);
43 _vertexData.setColor(3,0xFF00FF);
44
45 _modelMatrix = new Matrix();
46 }
47
48 public function set x(value:Number):void
49 {
50 if(_x==value) return;
51 _x = value;
52 _isMatrixDirty = true;
53 }
54
55 public function get x():Number
56 {
57 return _x;
58 }
59
60 public function get y():Number
61 {
62 return _y;
63 }
64
65 public function set y(value:Number):void
66 {
67 if(_y == value) return;
68 _y = value;
69 _isMatrixDirty = true;
70 }
71
72 public function get width():Number
73 {
74 return _width * _scaleX;
75 }
76
77 public function set width(value:Number):void
78 {
79 if(width == value) return;
80 _scaleX = value / _width;
81 _isMatrixDirty = true;
82 }
83
84 public function get height():Number
85 {
86 return _height * _scaleY;
87 }
88
89 public function set height(value:Number):void
90 {
91 if(height == value) return;
92 _scaleY = value / _height;
93 _isMatrixDirty = true;
94 }
95
96 public function get color():uint
97 {
98 return _color;
99 }
100
101 public function set color(value:uint):void
102 {
103 _color = value;
104 }
105
106 public function get scaleX():Number
107 {
108 return _scaleX;
109 }
110
111 public function set scaleX(value:Number):void
112 {
113 if(_scaleX == value) return;
114 _scaleX = value;
115 _isMatrixDirty = true;
116 }
117
118 public function get scaleY():Number
119 {
120 return _scaleY;
121 }
122
123 public function set scaleY(value:Number):void
124 {
125 if(_scaleY == value) return;
126 _scaleY = value;
127 _isMatrixDirty = true;
128 }
129
130 public function get modelMatrix():Matrix
131 {
132 if(_isMatrixDirty)
133 {
134 _modelMatrix.identity();
135 _isMatrixDirty = false;
136 _modelMatrix.translate(_x,_y);
137 _modelMatrix.scale(_scaleX,_scaleY);
138 }
139 return _modelMatrix;
140 }
141
142 public function get vertexData():QuadVertex
143 {
144 return _vertexData;
145 }
146 }
147 }
新的类中我们增加了_modelMatrix和_vertexData两个字段,前者是显示对象的模型矩阵,所谓模型矩阵指的是依附于模型的,同模型的位置尺寸形状相关的一个矩阵。后一个字段是对顶点数据的封装,他是QuadVertex类型:
1 package psw2d
2 {
3 import flash.geom.Matrix;
4
5 public class QuadVertex
6 {
7 public static const ELEMENTS_PER_VERTEX:int = 6;
8 private var _rawData:Vector.<Number>;
9
10 public function QuadVertex()
11 {
12 _rawData = new Vector.<Number>(4*ELEMENTS_PER_VERTEX,true);
13 }
14
15 public function setPosition(index:uint,x:Number,y:uint):void
16 {
17 var offset:int = ELEMENTS_PER_VERTEX * index;
18 _rawData[offset] = x;
19 _rawData[offset+1] = y;
20 _rawData[offset+2] = 0;
21 }
22
23 public function setColor(index:uint,color:uint):void
24 {
25 var offset:int = ELEMENTS_PER_VERTEX * index;
26
27 var r:uint = (color&0xFF0000)>>16;
28 var g:uint = (color&0x00FF00)>>8;
29 var b:uint = (color&0x0000FF);
30 r/=0xFF;
31 g/=0xFF;
32 b/=0xFF;
33 _rawData[offset+3] = r;
34 _rawData[offset+4] = g;
35 _rawData[offset+5] = b;
36 }
37
38 public function get rawData():Vector.<Number>
39 {
40 return _rawData;
41 }
42
43 public function set rawData(value:Vector.<Number>):void
44 {
45 _rawData = value;
46 }
47
48 public function transformVertex(modelMatix:Matrix):void
49 {
50 var x:Number,y:Number;
51 for(var i:int=0; i<4; ++i)
52 {
53 x = _rawData[i*ELEMENTS_PER_VERTEX];
54 y = _rawData[i*ELEMENTS_PER_VERTEX+1];
55 _rawData[i*ELEMENTS_PER_VERTEX] = modelMatix.a * x + modelMatix.c * y + modelMatix.tx;
56 _rawData[i*ELEMENTS_PER_VERTEX+1] = modelMatix.b * x + modelMatix.d * y + modelMatix.ty;
57 }
58 }
59
60 public function copyTo(target:QuadVertex):void
61 {
62 for(var i:uint;i<_rawData.length;++i)
63 {
64 target.rawData[i] = _rawData[i];
65 }
66 }
67 }
68 }
这个类目前封装不是很好,建议参考Starling中的VertexData类。
接下来需要修改QuadRender类:
1 package psw2d
2 {
3 import com.adobe.utils.AGALMiniAssembler;
4 import com.adobe.utils.PerspectiveMatrix3D;
5
6 import flash.display3D.Context3D;
7 import flash.display3D.Context3DProgramType;
8 import flash.display3D.Context3DVertexBufferFormat;
9 import flash.display3D.IndexBuffer3D;
10 import flash.display3D.Program3D;
11 import flash.display3D.VertexBuffer3D;
12 import flash.geom.Matrix3D;
13
14 public class QuadRender
15 {
16 private var _context3D:Context3D;
17 private var _quads:Vector.<Quad>;
18
19 private var _vertexBuffer:VertexBuffer3D;
20 private var _indexBuffer:IndexBuffer3D;
21 private var _vertexData:QuadVertex;
22
23 public function QuadRender(context3D:Context3D)
24 {
25 _context3D = context3D;
26 _quads = new Vector.<Quad>();
27 }
28
29 public function addQuad(quad:Quad):Quad
30 {
31 _quads.push(quad);
32 rebuildBuffer();
33 return quad;
34 }
35
36 public function rebuildBuffer():void
37 {
38 _vertexBuffer && _vertexBuffer.dispose();
39 _indexBuffer && _indexBuffer.dispose();
40 var numQuads:uint = _quads.length;
41 if(!numQuads) return;
42 var vertexData:Vector.<Number>=new Vector.<Number>();
43 var indexData:Vector.<uint>=new Vector.<uint>();
44 _vertexData = new QuadVertex();
45 for(var i:int=0;i<numQuads;++i)
46 {
47 _quads[i].vertexData.copyTo(_vertexData)
48 _vertexData.transformVertex(_quads[i].modelMatrix);
49 trace(_quads[i].modelMatrix);
50 vertexData = vertexData.concat(_vertexData.rawData);
51 indexData.push(i*4+0,i*4+1,i*4+2,i*4+0,i*4+2,i*4+3);
52 }
53 _vertexBuffer = _context3D.createVertexBuffer(numQuads * 4,6);
54 _indexBuffer = _context3D.createIndexBuffer(numQuads * 6);
55
56 _vertexBuffer.uploadFromVector(vertexData,0,numQuads * 4);
57 _indexBuffer.uploadFromVector(indexData,0,indexData.length);
58
59 _context3D.setVertexBufferAt(0,_vertexBuffer,0,Context3DVertexBufferFormat.FLOAT_3);
60 _context3D.setVertexBufferAt(1,_vertexBuffer,3,Context3DVertexBufferFormat.FLOAT_3);
61 }
62
63 public function setMatrix(sW:Number,sH:Number):void
64 {
65 var pm:Matrix3D = new Matrix3D(Vector.<Number>(
66 [
67 2/sW,0,0,0,
68 0,-2/sH,0,0,
69 0,0,0,0,
70 -1,1,0,1
71 ]));
72 _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,pm,true);
73 }
74
75 public function setProgram():void
76 {
77 var vertexSrc:String = "m44 op,va0,vc0 \n" +
78 "mov v0,va1";
79 var fragmentSrc:String = "mov oc,v0";
80 var vertexAssembler:AGALMiniAssembler = new AGALMiniAssembler();
81 var fragmentAssembler:AGALMiniAssembler = new AGALMiniAssembler();
82 vertexAssembler.assemble(Context3DProgramType.VERTEX,vertexSrc);
83 fragmentAssembler.assemble(Context3DProgramType.FRAGMENT,fragmentSrc);
84 var program:Program3D = _context3D.createProgram();
85 program.upload(vertexAssembler.agalcode,fragmentAssembler.agalcode);
86 _context3D.setProgram(program);
87 }
88
89 public function render():void
90 {
91 rebuildBuffer();
92 _context3D.clear();
93 _context3D.drawTriangles(_indexBuffer);
94 _context3D.present();
95 }
96 }
97 }
改变的地方已经标示出来。值得说明的是,由于需要及时的知道现实对象的信息,所以我们在帧循环里面重复的重建缓冲,重建的过程是,先拷贝原始的顶点数据在将这一份拷贝通过变换矩阵进行变换得到一份合成的顶点数据,然后将合成数据上传到顶点缓冲之中,绘制渲染。
接下来,在QuadTest的onEnterFrame()方法中增加一句代码:
1 private function onEnterFrame(e:Event):void
2 {
3 q.x++;
4 qRender.render();
5 }