前面已经学习了“常见的3D显像术语”与“常见的3D编码术语”。今天所要探讨的是Molehill。
什么是Molehill:
Molehill是Adobe官方预计推出的一套底层3D渲染引擎,该引擎能够调用GPU,借助GPU强大的浮点运算能力实现开创新的Flash3D技术。
2010年10月,Adobe在MAX大会中公布了关于Flash Player的新项目,名为Molehill的新API集可以让开发者借助GPU大幅提升Flash的3D渲染能力。
目前现有的Flash Player 10.1可以在30Hz下渲染几千个非Z缓冲三角形,使用Molehill和图形芯片则可以在60Hz左右的高分辨率显示器中全屏渲染近百万的Z缓冲三角形。这些新的3D API还将同时支持Flash Player和Adobe AIR,为用户提供全新的3D体验。
Adboe介绍称,Molehill是新推出的支持GPU硬件加速底层API的总称,支持Z缓冲、模版色缓冲、碎片和顶点着色、立方体纹理等特性。借助这些3D API,开发者可以在任何情况下调用GPU,如果出现GPU不兼容的情况则会调用CPU作为图形渲染处理器。
Flash Player10中现有的3D API将可以继续使用,Molehill主要面向高级开发人员用来构建复杂的3D模型,开发者可以按照实际需要选择使用这两种API。Adobe表示他们将会为不同的平台提供这些新的开发工具,在Windows系统中将会依赖于DirectX 9,在Mac和Linux下则依赖于OpenGL 1.3,未来还将会借助OpenGL ES 2.0支持Android和Linux Mobile等移动平台。
Molehill的结构:
对Flash而言,舞台就是画到屏幕上的东西,而各式各样的可视元素(位图、矢量图、文本、UI组件)被附加到了舞台上。同样,基于Molehill的Flash动画也被附加到了一个名为Stage3D的特殊对象上。你的舞台可以拥有一个以上的Stage3D,而每个Stage3D都有一个Context3D对象。Context3D就是引擎,它就是Molehill对象。
Stage
这是常规的Flash舞台,任何将会显示在屏幕上的东西都要放在这里。
Stage3D
是出于Flash和GUP之间的对象,不像诸如Sprite这样的常规Flash2D显示对象,Stage3D是不会出现在显示列表中的。这就意味着你不能在Stage3D对象上进行addChild()方法。Stage3D对象位于所以常规Flash内容的后面,它总是位于舞台的最底层。
Context3D
stage3D对象包含了一个Context3D,就像Bitmap 对象包含 BitmapData一样。当你处理Bitmap的时候。花的大部分时间其实是在处理BitmapData.同理,在Molehill中你也很少会直接用到Stage3D.取而代之的,你将使用Context3D进行工作。你可以把Context3D当做“3D引擎”,它是所有3D数据的对象。当你想把顶点数据作为顶点数据上传到显卡时候,你会把一个数字数组送入Context3D.
VertexBuffer3D
3D顶点缓冲。3D世界中的模型由成千上万个顶点组成的。每个顶点定义了一个网格“定角”的X,Y,Z坐标。而VertexBuffer3D就是一个用于这些坐标的数组。在初始化3D世界的时候。我们需要将VertexBuffer3D对象交给Context3D,以此来把这庞大的数据数组送给Molehill.接下来Context3D会把这些数据直接上传到你的显存中,以备渲染时使用。
IndexBuffer3D
3D索引缓冲 FLash终归还是需要知道每一块是从什么地方开始又到什么地方结束的,而索引缓冲就是用来告诉Flash哪部分数据是属于哪个顶点的。
Program3D
3D着色器 顶点着色器和片段着色器被上传到显卡,这样一来它们就能在GPU中运行了。着色器使用一种类似于汇编语言的特殊语言进行编译,它们将告诉GPU如何去绘制上传好的网格模型。
在初始化期间,所有的缓冲和编译好的着色器都将送人显卡,这个过程只有一次,一旦完成,Flash就可以反复的绘制这些模型到新的位置上了。
说了这么多理论我们开始动手来写一个简单的demo 来帮助消化下这些知识:
package
{ import com.adobe.utils.AGALMiniAssembler; import com.adobe.utils.PerspectiveMatrix3D; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.display.Stage3D; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.display3D.Context3D; import flash.display3D.Context3DProgramType; import flash.display3D.Context3DTextureFormat; import flash.display3D.Context3DVertexBufferFormat; import flash.display3D.IndexBuffer3D; import flash.display3D.Program3D; import flash.display3D.VertexBuffer3D; import flash.display3D.textures.Texture; import flash.events.Event; import flash.geom.Matrix; import flash.geom.Matrix3D; import flash.geom.Vector3D; [SWF(width=1024,height=590)] public class lesson1 extends Sprite { [Embed(source = "assset/bricks_a_spec.jpg")] private var myTextureBitmap:Class; /** * 舞台的宽度 */ public const stageW:int = 1024; /** * 舞台的高度 */ public const stageH:int = 590; /** * 纹理大小 */ public const textureSize:int = 512; /** * 3D图形窗口 */ private var context3D:Context3D; /** *定义着色器 */ private var shaderProgram:Program3D; /** * 定义一个顶点缓冲 */ private var vertexBuffer:VertexBuffer3D; /** * 定义一个索引缓冲 */ private var indexBuffer:IndexBuffer3D; /** * 定义网格的顶点数据 */ private var meshVertexData:Vector.<Number>; /** * 定义网格的索引数据 */ private var meshIndexData:Vector.<uint>; /** * 影响模型位置和相机的角度的矩阵 */ private var projectionMatrix3D:PerspectiveMatrix3D = new PerspectiveMatrix3D(); /** * 定义一个模型的矩阵 */ private var modelMatrix:Matrix3D = new Matrix3D; /** * 定义一个视图的矩阵 */ private var viewMatrix:Matrix3D = new Matrix3D; /** * 定义一个投影 */ private var modelViewProjection:Matrix3D = new Matrix3D; /** * 用于动画的帧计数器 */ private var t:Number = 0; /** * 创建纹理 */ private var myTextureData:Bitmap = new myTextureBitmap(); private var myTexture:Texture; public function lesson1() { this.stage ? addEventListener(Event.ADDED_TO_STAGE,init) : init(); } private function init(event:Event = null):void { hasEventListener(Event.ADDED_TO_STAGE) && removeEventListener(Event.ADDED_TO_STAGE,init); stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE,onContext3DCreate); stage.stage3Ds[0].requestContext3D(); } private function onContext3DCreate(event:Event):void { removeEventListener(Event.CONTEXT3D_CREATE,onContext3DCreate); var stage3D:Stage3D = event.target as Stage3D; context3D = stage3D.context3D; if(!context3D) { return; } context3D.enableErrorChecking = true; initData(); } private function initData():void { //定义一个正方形的顶点信息 meshVertexData = Vector.<Number>([ -1, -1, 1, 0, 0, 0, 0, 1, 1, -1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, -1, 1, 1, 0, 1, 0, 0, 1 ]); // 定义一个正方形的索引 meshIndexData = Vector.<uint> ([ 0,1,2, 0,2,3 ]); // 3D缓冲区的像素尺寸 context3D.configureBackBuffer(stageW,stageH,0,true); //创建一个顶点着色器 var vertexShader:AGALMiniAssembler = new AGALMiniAssembler(); vertexShader.assemble ( Context3DProgramType.VERTEX, // 创建一个4X4矩阵乘以相机角度 "m44 op, va0, vc\n"+ //告诉片段着色器X,Y,Z的值 "mov v0, va0\n"+ //告诉片段着色器u,v的值 "mov v1, va1\n" ); // 创建一个片段着色器 var pixelShader:AGALMiniAssembler = new AGALMiniAssembler(); pixelShader.assemble ( Context3DProgramType.FRAGMENT, "tex ft0, v1, fs0 <2d,repeat,miplinear>\n"+ // 结果输出 "mov oc, ft0\n" ); shaderProgram = context3D.createProgram(); //上传网格索引 shaderProgram.upload(vertexShader.agalcode,pixelShader.agalcode); indexBuffer = context3D.createIndexBuffer(meshIndexData.length); indexBuffer.uploadFromVector(meshIndexData,0,meshIndexData.length); // 上传网格顶点数据 // 因为包含X,Y,Z,U,V,nX,nY,nZ 所以每个顶点各占8个数组元素 vertexBuffer = context3D.createVertexBuffer(meshVertexData.length/8,8); vertexBuffer.uploadFromVector(meshVertexData,0,meshVertexData.length / 8); //产生MIP映射 myTexture = context3D.createTexture(textureSize,textureSize,Context3DTextureFormat.BGRA,false); var ws:int = myTextureData.bitmapData.width; var hs:int = myTextureData.bitmapData.height; var level:int = 0; var transform:Matrix = new Matrix(); var tmp:BitmapData = new BitmapData(ws,hs,true,0x00000000); while(ws >=1 && hs >=1) { tmp.draw(myTextureData.bitmapData,transform,null,null,null,true); myTexture.uploadFromBitmapData(tmp,level); transform.scale(0.5,0.5); level++; ws >>=1; hs >>=1; if(hs && ws) { tmp.dispose(); tmp = new BitmapData(ws,hs,true,0x00000000); } } tmp.dispose(); // 创建透视矩阵 projectionMatrix3D.identity(); // 设置45度视角,1024 / 590 长宽比,0,1的近裁剪面,100的远裁剪面 projectionMatrix3D.perspectiveFieldOfViewRH(45,stageW / stageH,0.01,100); // 创建一个定义相机位置的矩阵 viewMatrix.identity(); // 为了看到Mesh,把相机往后移; viewMatrix.appendTranslation(0,0,-4); // 准备就绪 开始渲染 addEventListener(Event.ENTER_FRAME,onRendering); } protected function onRendering(event:Event):void { context3D.clear(0,0,0); context3D.setProgram(shaderProgram); // 创建变换矩阵 modelMatrix.identity(); modelMatrix.appendRotation(t*0.6,Vector3D.X_AXIS); modelMatrix.appendRotation(t*0.7,Vector3D.Y_AXIS); modelMatrix.appendRotation(t*0.8,Vector3D.Z_AXIS); modelMatrix.appendTranslation(0,0,0);t+=2.0;
//重置矩阵 modelViewProjection.identity(); modelViewProjection.append(modelMatrix); modelViewProjection.append(viewMatrix); modelViewProjection.append(projectionMatrix3D); //把矩阵传给着色器 context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,0,modelViewProjection,true); // 着色器处理顶点数据 context3D.setVertexBufferAt(0,vertexBuffer,0,Context3DVertexBufferFormat.FLOAT_3); // 着色器处理纹理 context3D.setVertexBufferAt(1,vertexBuffer,3,Context3DVertexBufferFormat.FLOAT_3); context3D.setTextureAt(0,myTexture); context3D.drawTriangles(indexBuffer,0,meshIndexData.length / 3); context3D.present(); } }}