本文参考来源:
http://www.adobe.com/devnet/flashplayer/articles/how-stage3d-works.html
http://www.ashan.org/post-267.html
http://www.pixelbender.cn/?p=381
1.Stage3D简介
Stage3D是Adobe为了提高的Flash 3D应用程序渲染性能而开发的项目,之前代号为MoleHill。由于Stage3D能够直接调用底层的GPU,因此大幅度提高了3D渲染速度和性能,同时兼具Flash Player跨平台的特性,可以在Windows,MacOS,Android,iOS等平台上得到广泛的应用。目前很多第三方的Flash 3D引擎都加入了对Stage3D的支持,极大的提高了Flash 3D应用程序的运行效率。很多2D的Flash游戏,想利用Stage3D来进行硬件加速从而提高运行效率,大都采用第三方的2D引擎Starling。如果想开发纯3D的Flash游戏,推荐使用Away3D,据报道Adobe已经为Away3D注资,因此我们有必要关注跟踪这个3D引擎。
图1 Stage3D层级结构
Stage3D使用了AGAL语言,这是Adobe开发出来的图形汇编语言,同时在调用的时候遵循3D图形渲染的过程,因此Stage3D的使用是有一定难度的,这也是第三方的3D引擎封装它的原因之一。我们先来看一下Stage3D在Flash Player中的位置,
图2 Stage3D在舞台上的位置
图中的最右边的Stage层是Flash Player显示列表中我们目前应用的最底层容器,而Stage3D层则还在Stage层的下面,换句话来说,Stage3D上面的3D对象可能会被Stage层遮住,当然Stage层是透明的。所有的Stage3D层都是不透明的,都可以设定自身的大小,这意味着它们可以被层层遮盖,但是如果各自的大小不同,则可能同时展现在屏幕的各个部分,有点类似电视上的分屏效果。在最底层则是StageVideo层,是留给硬件加速视频对象(区别于普通的Video对象)用的,这也意味着硬件加速视频层极易被遮住。目前Stage3D层默认的只有4个,从Stage[0]到Stage3D[3],一般我们只用到Stage3D[0]就可以了。
2.3D渲染的基本概念
这里先简单的说一下3D渲染的过程,弄清楚这个过程对我们使用Stage3D大有裨益。3D渲染包括3个方面,一是被渲染的物体本身,二是灯光,三是视角。想象一下在一个封闭的房子里面,有个角落有一束阳光照射进来,观察者是处在正面还是侧面,上面还是下面,所看到的画面是不一样的,因此灯光和视角会决定最终渲染出来的画面。被渲染物体的本身则包括它的轮廓也就是几何形体,以及它的颜色或材质这两方面。现在我们来看看GPU具体是怎样处理这个渲染过程的,图3为固定管道(pipeline)的渲染过程,
图3 GPU渲染固定管道
(1) Transformation and lighting: 转换和灯光处理
(2) Triangle Assemble: 以三个点作为一个三角形为单元进行组合
(3) Viewpot: 根据视图端口进行裁剪获取范围
(4) Rasterizer: 将渲染的几何体进行栅格化
(5) Texture Stages: 将栅格化的格子进行材质贴图
(6) Frame Buffer: 帧缓冲,最终生成图片
上面是固定管道的渲染过程,目前GPU也支持程序化管道渲染过程,
图4 GPU程序管道渲染
整个渲染过程可以通过程序操控,与图3相比,第一步的转换与灯光直接变成了顶点着色,第五步的材质贴图变成了片段着色。可编程的渲染管道就是将顶点着色VertexShader和片段着色FragmentShader这两个开放给程序员,让他们自由发挥。下面的图更直观的展现了渲染过程,
图5 GPU渲染示例
通过上面的讨论,渲染过程最关键的几步包括,一是几何体顶点或端点的输入,二是以三角形为单元形成待渲染的面,三是栅格化处理,将待渲染的面分成一堆小格子,四是对格子逐个进行着色渲染,可以是颜色,也可以材质。这里最重要的是GPU一定是以三角形为单元进行渲染处理的,也就是说所有的几何形体,包括四边形,也是被分成三角形来处理的。渲染的基本要素是顶点和着色两部分,三个顶点形成一个面,一旦顶点和面进行了着色处理,渲染的物体就展现出来了。
3.Stage3D的调用过程
(1)首先需要取得Stage3D对象和Context3D对象,这是待显示渲染的容器,类似于画布。
var stage3D:Stage3D = stage.stage3Ds[0];
context3D = stage.stage3Ds[0].context3D;
实际上需要监听Stage3D的Event.CONTEXT3D_CREATE事件后才能获得Context3D,这里省略一下,让过程更清晰些。
(2)其次创建与顶点相关的两个缓冲buffer,
VertexBuffer:所有待渲染的顶点的集合
IndexBuffer:具体渲染时顶点的顺序集合,会以三个点为单元进行自动分割
VertexBuffer创建过程,
var vertices:Vector. = Vector.(
[ -0.3,-0.3,0, 1, 0, 0, // x, y, z, r, g, b
-0.3, 0.3, 0, 0, 1, 0,
0.3, 0.3, 0, 0, 0, 1,
0.3, -0.3, 0, 1, 0, 0]);
// 4 vertices, of 6 Numbers each
vertexbuffer = context3D.createVertexBuffer(4, 6);
// offset 0, 4 vertices
vertexbuffer.uploadFromVector(vertices, 0, 4);
IndexBuffer创建过程,
var indices:Vector. = Vector.([0, 1, 2, 2, 3, 0]);
// total of 6 indices. 2 triangles by 3 vertices each
indexBuffer = context3D.createIndexBuffer(6);
// offset 0, count 6
indexBuffer.uploadFromVector (indices, 0, 6);
上面的VertexBuffer里面包含4个顶点,每个顶点含有位置x,y,z和颜色值r,g,b,然后IndexBuffer取其中的0,1,2和2,3,0两种组合,也就是4个顶点变成了两个三角形,供GPU进行渲染。
(3)最后就差着色这一步了。着色分为顶点着色和片段着色两种,这里就要用到AGAL语言了。
顶点着色过程,
// compile vertex shader
var vertexShader:Array = [
"m44 op, va0, vc", // 4x4 matrix transform from va0 and vc0 to output position
"mov v0, va1" // copy rgb color from va1 to v0
];
var vertexAssembler:AGALMiniAssembler = new AGALMiniAssembler();
vertexAssembler.assemble(Context3DProgramType.VERTEX, vertexShader.join("\n"));
片段着色过程,
// compile fragment shader
var fragmentShader:Array = ["mov oc, v0"];
var fragmentAssembler:AGALMiniAssembler = new AGALMiniAssembler();
fragmentAssembler.assemble(flash.display3D.Context3DProgramType.FRAGMENT, fragmentShader.join("\n"));
这里的着色过程开始看到AGAL汇编语言了,一定要经过AGALMiniAssembler进行转换成机器码,才能被GPU执行。
(4)最终启动执行,Program3D登场亮相,
program3D = context3D.createProgram();
program3D.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
// vertex position to attribute register 0
context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// color to attribute register 1
context3D.setVertexBufferAt(1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
// assign shader program
context3D.setProgram(program3D);
context3D.drawTriangles(indexBuffer); //根据顶点索引缓冲绘制三角形
context3D.present(); //渲染展示
到现在为止,Stage3D调用过程就完结了。这样一路看下来,好像很简单,其实还是比较复杂的,如果我们不清楚3D渲染过程,直接上Stage3D的调用代码,那将是让人非常痛苦的过程。这样看下来,Stage3D的代码很多,做的事情却很少,最终只能看到一个四边形。因此我们需要深入下去,才能更好掌握Stage3D。