关注flash的同学肯定都知道Adobe推出了新的基于硬件加速的API Molehill。它可以在60Hz左右的高分辨率显示器下全屏渲染近百万的Z缓冲三角形。就算你的显卡不兼容,也会通过一个叫SwiftShader软件来利用CPU进行渲染。
我们熟悉的2D显示列表位于Molehill的Stage3D上面,而Stage3D又位于StageVideo上面。实际上3D内容可以通过bitmapdata呈现在2D显示列表中。
下图表示了Molehill的基本架构
下面的代码在Stage3D中创建了一个三角形,从中我们可以略微熟悉一下这套API的用法
package { import com.adobe.utils.AGALMiniAssembler; 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.Context3DTriangleFace; import flash.display3D.Context3DVertexBufferFormat; import flash.display3D.IndexBuffer3D; import flash.display3D.Program3D; import flash.display3D.VertexBuffer3D; import flash.events.Event; import flash.geom.Matrix3D; import flash.geom.Rectangle; [SWF(width="980", height="570", frameRate="60")] public class MoleHillTest extends Sprite { //Context3D 是渲染发生的地方,它和Stage3D的关系就像bitmapdata和bitmap的关系 private var context3d:Context3D; //VertexBuffer3D将存储顶点数据 private var vertexBuffer:VertexBuffer3D; //这里定义顶点的渲染顺序 private var indexBuffer:IndexBuffer3D; //Program3D是用来包含两个着色器的 private var program:Program3D; //这是一个矩阵,我们的顶点着色器将使用它来修改顶点的位置 private var model:Matrix3D=new Matrix3D(); public function MoleHillTest() { //监听flash什么时候给我们返回一个Context3D对象,因为当你请求Context3D的时候,flash不一定会马上回应 stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onGotContext); //请求一个Context3D对象 stage.stage3Ds[0].requestContext3D(); } protected function onGotContext(ev:Event):void { //获得Stage3D对象,它将给我们Context3D var stage3d:Stage3D=Stage3D(ev.currentTarget); //我们获得了Context3d context3d=stage3d.context3D; //如果没有获得,那我们就跳出函数 trace(context3d) if(context3d==null) return; //当发生问题的时候,我们可以获得error信息。请在正式发布的时候设为false以提高性能 context3d.enableErrorChecking=true; //基本上,你可以看成是 new BitmapData(980, 570),表示渲染的大小。而4表示反锯齿的强度,可以设置的值为0,2,4,16. context3d.configureBackBuffer(980, 570, 4, true); //这里是说当三角形没有对着我们的时候,就不要渲染它,还有可能的值是Context3DTriangleFace.FRONT,Context3DTriangleFace.FRONT_AND_BACK,Context3DTriangleFace.NONE。 context3d.setCulling(Context3DTriangleFace.BACK); //创建一个顶点缓冲,我们将有3个顶点(三角形),每个顶点包含6个元素,分别表示 //x坐标,y坐标,z坐标,红色,绿色,蓝色。 vertexBuffer=context3d.createVertexBuffer(3, 6); //为顶点创建一个索引缓冲来为三角形排序,我们有一个三角形,因此这里有3个顶点,因此有3个索引 indexBuffer=context3d.createIndexBuffer(3); //顶点数据包含每个顶点的位置信息以及颜色信息 //x==-1表示屏幕的左边 //x==1表示屏幕的右边 //x==0表示屏幕的中央 //y也是同样的道理。1,0,-1分别表示屏幕的上中下 //颜色值在0-1之间,比如1,0,0表示纯红色 //下面的数据描述了一个三角形,它左下角的顶点是红色,中间上面的顶点是绿色,右下角的顶点是蓝色 var vertexData:Vector.<Number>=Vector.<Number>( [ -1, -1, 0, 255/255, 0, 0, //<- 1st vertex x,y,z,r,g,b 0, 1, 0, 0, 255/255, 0, //<- 2nd vertex x,y,z,r,g,b 1, -1, 0, 0, 0, 255/255 //<- 3rd vertex x,y,z,r,g,b ] ); //这个是我们渲染顶点的顺序,0是第一个顶点,1是第二个,2是第三个。 var indexData:Vector.<uint>=Vector.<uint>([0, 1, 2]); //将我们的顶点数据传递给顶点缓冲 vertexBuffer.uploadFromVector(vertexData, 0, 3); //将我们表示顺序的索引值传递给顶点缓冲 indexBuffer.uploadFromVector(indexData, 0, 3); //AGAL(Adobe Graphics Assembly Language)是Adobe开发的图形汇编语言,可以操控机器硬件比如可编程显卡 //AGALMiniAssembler是Adobe官方提供的编译器类,它通过字符串指令来获得一个AGAL二进制流,再通过context3D上传给显卡的编程管线。最后由显卡来处理顶点以及片段的运算 //在此我们创顶点和片段两个‘编译器’,来处理顶点和片段 var agalVertex:AGALMiniAssembler=new AGALMiniAssembler(); var agalFragment:AGALMiniAssembler=new AGALMiniAssembler(); //m44 op, va0, vc0 表示应用一个4x4矩阵到我们的顶点并输出到屏幕 //mov v0, va1 表示取出我们顶点的颜色值,并传给片段着色器 var agalVertexSource:String="m44 op, va0, vc0\n" + "mov v0, va1\n"; //将我们的颜色输出到屏幕 var agalFragmentSource:String="mov oc, v0\n"; //编译AGAL源字符串,这个过程中将产生一个ByteArray供着色器使用 agalVertex.assemble(Context3DProgramType.VERTEX, agalVertexSource); agalFragment.assemble(Context3DProgramType.FRAGMENT, agalFragmentSource); //创建一个Program3D来使用我们的着色器,包括顶点着色器和片段着色器,Program3D是显卡管线上的程序 program=context3d.createProgram(); //传递编译过的着色器给我们的Program3D使用 program.upload(agalVertex.agalcode, agalFragment.agalcode); //定时触发渲染 addEventListener(Event.ENTER_FRAME, onRenderLoop); } protected function onRenderLoop(event:Event):void { //清除颜色和透明度 context3d.clear(0.5, 0.5, 0.5, 1); //设置会渲染到屏幕上的Program3D context3d.setProgram(program); //现在我们为顶点缓冲定义两个属性val0和val1,val0表示位置,val1表示颜色 //第一个参数是属性的id,0代表位置val0,1代表颜色val1;第二个参数vertextBuffer提供了运算对象;第三个参数表示该属性要从什么位置开始取值;第四个参数可以理解为取几个值 context3d.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3); //颜色值是从3开始的,所以从3取值,如果使用的是带alpha的颜色值,可能最后一个参数就该使用 Context3DVertexBufferFormat.FLOAT_4 context3d.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3); //重设矩阵 model.identity(); //将三角形在x,y,z上各缩放0.5倍;你还可以通过model.appendScale,model.appendTranslation来旋转和移动三角形 model.appendScale(0.5, 0.5, 0.5); //通过矩阵定义一个常量 context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, model); //将三角形绘制到屏幕,0表示起始位置,1表示我们只画一个三角形 context3d.drawTriangles(indexBuffer, 0, 1); //将三角形显示到屏幕上 context3d.present(); } } }
其中最核心的是Context3D,它是一个三维空间的处理环境,负责创建并处理三维对象的各个要素比如顶点、片段、透视等,并将处理的结果使用AGAL(Adobe Graphics Assembly Language)传递给显卡进行运算,运算结果最终被传给CPU并呈现在舞台上。
如果你对上面的代码感到疑惑和陌生,这很正常,毕竟这套API是为底层3D开发者准备的,幸好大家所熟知的ActionScript 3D第三方框架如Alternativa3D, Away3s, Flare3D都开始支持Molehill了,因此对于AS开发者来说,我们可以在更高的层次去开发3D应用而不用去过多了解底层的实现。同时还有人开发了一些东西让Stage3D来加速2D图形的渲染,比如M2D框架。
最后,如果你想测试上面的代码,请配置好你的运行环境使其能支持molehill,具体的配置方法请google ‘Molehill配置’。