代码改变世界

Stage3D学习笔记(六):旋转动画效果

2014-11-05 15:22  阿诚de窝  阅读(596)  评论(0编辑  收藏  举报

我们这节在上一篇代码的基础上再进一步,让显示的纹理进行旋转。

 

实现旋转,只需要每帧修改_modelViewMatrix的旋转角度即可,我们需要一个变量来记录旋转:

1 //旋转度数
2 private var _rotation:Number = 0;

 

每帧修改后的数据需要重新提交到GPU,而已经提交的数据不改动则不需要重新进行提交,所以我们修改一下render方法即可:

 1 private function render(event:Event):void
 2 {
 3     //没有变动的数据就不需要进行重复的上传了
 4     //每帧进行旋转
 5     _rotation += 1;
 6     //重置模型矩阵
 7     _modelViewMatrix.identity();
 8     //移动模型到舞台中心
 9     _modelViewMatrix.prependTranslation(stage.stageWidth / 2, stage.stageHeight / 2, 0);
10     //应用新的旋转角度
11     _modelViewMatrix.prependRotation(_rotation, Vector3D.Z_AXIS);
12     //设置模型大小
13     _modelViewMatrix.prependScale(128, 128, 1);
14     //设置模型的注册点为中心, 注意这里使用的是百分比, 不要使用 -64 哦
15     _modelViewMatrix.prependTranslation(-0.5, -0.5, 0);
16     //同样需要再次上传我们的矩阵数据用于运算
17     var mvpMatrix:Matrix3D = new Matrix3D();
18     mvpMatrix.append(_modelViewMatrix);
19     mvpMatrix.append(_projectionMatrix);
20     _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mvpMatrix, true);
21 //省略
22 }

 

对于上传后的数据的销毁,Texture、VertexBuffer3D、IndexBuffer3D和Program3D都有名为dispose的方法,我们如果向GPU上传了数据,调用该方法会清除上传到GPU的数据。

 

下面给出完整的代码:

  1 package
  2 {
  3     import com.adobe.utils.AGALMiniAssembler;
  4     
  5     import flash.display.Bitmap;
  6     
  7     import flash.display.Sprite;
  8     import flash.display.Stage3D;
  9     import flash.display3D.Context3D;
 10     import flash.display3D.Context3DProfile;
 11     import flash.display3D.Context3DProgramType;
 12     import flash.display3D.Context3DRenderMode;
 13     import flash.display3D.Context3DTextureFormat;
 14     import flash.display3D.Context3DVertexBufferFormat;
 15     import flash.display3D.IndexBuffer3D;
 16     import flash.display3D.Program3D;
 17     import flash.display3D.VertexBuffer3D;
 18     import flash.display3D.textures.Texture;
 19     import flash.events.ErrorEvent;
 20     import flash.events.Event;
 21     import flash.geom.Matrix3D;
 22     import flash.geom.Vector3D;
 23     
 24     [SWF(width=550, height=400, frameRate=60)]
 25     public class Matrix3DTest extends Sprite
 26     {
 27         [Embed(source="img.png")]
 28         private var IMG_CLASS:Class;
 29         
 30         //3D 场景对象
 31         private var _stage3D:Stage3D;
 32         //3D 上下文渲染对象
 33         private var _context3D:Context3D;
 34         
 35         //顶点缓冲数据
 36         private var _vertexBuffer:VertexBuffer3D;
 37         //索引缓冲数据
 38         private var _indexBuffer:IndexBuffer3D;
 39         //纹理数据对象
 40         private var _texture:Texture;
 41         
 42         //着色器对象
 43         private var _program3D:Program3D;
 44         
 45         //正交矩阵
 46         private var _projectionMatrix:Matrix3D;
 47         //模型矩阵, 通过操作该矩阵来变换纹理的显示
 48         private var _modelViewMatrix:Matrix3D;
 49         
 50         //旋转度数
 51         private var _rotation:Number = 0;
 52         
 53         public function Matrix3DTest()
 54         {
 55             addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
 56         }
 57         
 58         private function addedToStageHandler(event:Event):void
 59         {
 60             removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
 61             
 62             //3D 场景存在, 一般存在 4 个 3D 场景对象
 63             if(stage.stage3Ds.length > 0)
 64             {
 65                 //使用最下层的 3D 场景
 66                 _stage3D = stage.stage3Ds[0];
 67                 //请求 3D 上下文渲染对象
 68                 _stage3D.addEventListener(ErrorEvent.ERROR, stage3DErrorHandler);
 69                 _stage3D.addEventListener(Event.CONTEXT3D_CREATE, context3DCreateHandler);
 70                 _stage3D.requestContext3D(Context3DRenderMode.AUTO, Context3DProfile.BASELINE);
 71             }
 72         }
 73         
 74         private function stage3DErrorHandler(event:ErrorEvent):void
 75         {
 76             trace("Context3D对象请求失败:", event.text);
 77         }
 78         
 79         private function context3DCreateHandler(event:Event):void
 80         {
 81             initContext3D();
 82             initOrthographicProjection(stage.stageWidth, stage.stageHeight);
 83             initBuffer();
 84             initTexture();
 85             transformMatrix();
 86             initProgram();
 87             
 88             //每帧进行渲染
 89             addEventListener(Event.ENTER_FRAME, render);
 90         }
 91         
 92         private function initContext3D():void
 93         {
 94             //获取 3D 渲染对象
 95             _context3D = _stage3D.context3D;
 96             //设置后台缓冲区
 97             _context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 2);
 98         }
 99         
100         private function initOrthographicProjection(width:Number, height:Number, near:Number = -1.0, far:Number = 1.0):void
101         {
102             //创建正交矩阵的实例
103             _projectionMatrix = new Matrix3D();
104             //设置正交矩阵数据, 这个公式记死即可
105             var coords:Vector.<Number> = new <Number>
106                     [
107                         2.0 / width, 0.0, 0.0, 0.0,
108                         0.0, -2.0 / height, 0.0, 0.0,
109                         0.0, 0.0, -2.0 / (far - near), 0.0,
110                         -1.0, 1.0, -(far + near) / (far - near), 1.0
111                     ];
112             _projectionMatrix.copyRawDataFrom(coords);
113         }
114         
115         private function initBuffer():void
116         {
117             //顶点数据, 因为只需要显示一张图片所以这里的顶点数据是可以写死的, 同时可以去掉 z 轴
118             //的数据, 因为不需要使用到 z 轴, 我们按照下面的规则来排列顶点:
119             //0 - 1
120             //| / |
121             //2 - 3
122             //有趣的是 uv 的数据和顶点数据其实是一致的, 所以 uv 的数据也可以去除, 不过我们这里
123             //先留着, Starling 框架中 uv 数据是已经去掉的
124             var vertexData:Vector.<Number> = Vector.<Number>(
125                     [
126                     //  x, y, u, v
127                         0, 0, 0, 0,
128                         1, 0, 1, 0,
129                         0, 1, 0, 1,
130                         1, 1, 1, 1
131                     ]);
132             
133             //创建顶点缓冲对象, 参数设定存在几组数据和每组数据的个数
134             _vertexBuffer = _context3D.createVertexBuffer(vertexData.length / 4, 4);
135             //上传顶点数据到GPU, 参数设定从第几组数据开始上传和上传多少组数据
136             _vertexBuffer.uploadFromVector(vertexData, 0, vertexData.length / 4);
137             
138             //索引数据
139             var indexData:Vector.<uint> = Vector.<uint>(
140                     [
141                         0, 1, 2,
142                         1, 2, 3
143                     ]);
144             
145             //创建索引缓冲对象, 每个索引对应顶点数据中的相对应的一组数据, 
146             //每3个索引组成一个会被绘制出来的三角形, 参数指定索引的长度
147             _indexBuffer = _context3D.createIndexBuffer(indexData.length);
148             //上传索引数据到GPU, 参数设定从第几个数据开始上传和上传多少个数据
149             _indexBuffer.uploadFromVector(indexData, 0, indexData.length);
150         }
151         
152         private function initTexture():void
153         {
154             //创建位图
155             var bitmap:Bitmap = new IMG_CLASS() as Bitmap;
156             //创建纹理, 注意尺寸必须是 2 的幂数
157             _texture = _context3D.createTexture(128, 128, Context3DTextureFormat.BGRA, true);
158             //上传纹理到 GPU, 第二个参数表示该纹理的 mipmap 级别, 级别零是高级全分辨率图像
159             _texture.uploadFromBitmapData(bitmap.bitmapData, 0);
160         }
161         
162         private function transformMatrix():void
163         {
164             //设置纹理的转换矩阵
165             _modelViewMatrix = new Matrix3D();
166             //设置矩阵的位置
167             _modelViewMatrix.prependTranslation(0, 0, 0);
168             //设置矩阵的旋转
169             _modelViewMatrix.prependRotation(0, Vector3D.Z_AXIS);
170             //设置矩阵的尺寸
171             _modelViewMatrix.prependScale(128, 128, 1);
172             //设置纹理的中心点
173             _modelViewMatrix.prependTranslation(0, 0, 0);
174             
175             //将纹理的转换矩阵和正交矩阵结合就得到了最终需要的上传到 GPU 进行运算的矩阵数据
176             var mvpMatrix:Matrix3D = new Matrix3D();
177             mvpMatrix.append(_modelViewMatrix);
178             mvpMatrix.append(_projectionMatrix);
179             //将我们的最终矩阵作为常量传递到 GPU 中, 指定其是名称为 vc0 的那个寄存器
180             _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mvpMatrix, true);
181         }
182         
183         private function initProgram():void
184         {
185             //顶点着色器代码, 每个上传的顶点前都会执行一次该代码
186             var vertexArr:Array =
187                     [
188                         //op 代表位置输出寄存器, 无论对顶点进行多少次的运算最终都要将结果
189                         //赋值给他, 这里和我们的正交矩阵进行相乘的运算
190                         "m44 op, va0, vc0",
191                         //片段着色器需要用的数据要在这里通过 v0 中转一下, 因为片段着色器不
192                         //能直接读取 va0 和 va1 的数据
193                         "mov v0, va1"
194                     ];
195             
196             //片段着色器代码, 每个可以显示的像素都会执行一次该代码
197             var fragmentArr:Array =
198                     [
199                         //对纹理 fs0 进行取样, 通过 v0 代表的 uv 坐标来获取对应的像素点颜
200                         //色, 将该颜色值存储到 ft0 中
201                         "tex ft0, v0, fs0 <2d,repeat,linear,nomip>",
202                         //oc 代表颜色输出寄存器, 每个顶点的颜色数据都要赋值给他
203                         "mov oc, ft0"
204                     ];
205             
206             //使用 Adobe 自己提供的编译器编译代码为程序可使用的二进制数据
207             var assembler:AGALMiniAssembler = new AGALMiniAssembler();
208             _program3D = assembler.assemble2(_context3D, 1, vertexArr.join("\n"), fragmentArr.join("\n"));
209             
210             //----- 这段代码是从 render 里搬过来的, 因为不会进行改动就不放在帧循环中了 -----
211             
212             //指定着色器代码的 va0 代表的数据段, 表示顶点的 x, y 坐标
213             _context3D.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
214             //指定着色器代码的 va1 代表的数据段, 表示顶点的 u, v 数据
215             _context3D.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
216             //指定上传的纹理由 fs0 表示
217             _context3D.setTextureAt(0, _texture);
218             //指定当前使用的着色器对象
219             _context3D.setProgram(_program3D);
220         }
221         
222         private function render(event:Event):void
223         {
224             //没有变动的数据就不需要进行重复的上传了
225             //每帧进行旋转
226             _rotation += 1;
227             //重置模型矩阵
228             _modelViewMatrix.identity();
229             //移动模型到舞台中心
230             _modelViewMatrix.prependTranslation(stage.stageWidth / 2, stage.stageHeight / 2, 0);
231             //应用新的旋转角度
232             _modelViewMatrix.prependRotation(_rotation, Vector3D.Z_AXIS);
233             //设置模型大小
234             _modelViewMatrix.prependScale(128, 128, 1);
235             //设置模型的注册点为中心, 注意这里使用的是百分比, 不要使用 -64 哦
236             _modelViewMatrix.prependTranslation(-0.5, -0.5, 0);
237             //同样需要再次上传我们的矩阵数据用于运算
238             var mvpMatrix:Matrix3D = new Matrix3D();
239             mvpMatrix.append(_modelViewMatrix);
240             mvpMatrix.append(_projectionMatrix);
241             _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mvpMatrix, true);
242             
243             //清除已绘制过的 3D 图像
244             _context3D.clear();
245             //通过顶点索引数据绘制所有的三角形
246             _context3D.drawTriangles(_indexBuffer);
247             //将后台缓冲的图像显示到屏幕
248             _context3D.present();
249         }
250     }
251 }