我们这节在上一篇代码的基础上再进一步,让显示的纹理进行旋转。
实现旋转,只需要每帧修改_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(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(" "), fragmentArr.join(" ")); 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 }