Molehill的基本架构
如何使用Molehill底层API在舞台上绘制一个三角形:
理解Stage3D
在过去几年里,开发者在Flash中创建了一些应用3D的精彩项目。3D引擎例如Papervision3D,Away3D,Alternativa3D以及所有使用这些引擎的了不起的应用都表明人们对在Flash中(运用)实时3D渲染的强烈需求。
以前,Flash中的3D渲染没有通过3D硬件加速来实现。事实上,在Flash Player 11前的所有版本中的3D渲染都是通过依赖CPU的软件模式来实现的。软件模式缓慢且不能用来渲染复杂的3D场景。直到现在,合成像我们在现今的3D游戏中所见的高端图形效果依然是不可能实现的。
随着Flash Player 11的发布,时机成熟了。开发者可以利用3D硬件加速而不是依赖计算机的CPU来做渲染。新的渲染模式将Flash的3D内容交由辅助处理器——计算机的视频硬件的一部分——称作图形处理器(GPU)来处理。GPU是一个完全被用以对物体进行3D渲染的硬件。
使用Stage3D
Stage 3D是Adobe最近发布的新的Flash API。它致力于实时3D渲染。使用Stage3D,你可以在Flash中充分利用用户电脑GPU的硬件加速能力。
Stage3D的发布对于Flash开发者是一个重大事件。在Flash中使用3D加速使许多之前对于Flash游戏和Flash应用来说的不可能成为了可能。
如果你回想3D硬件加速刚刚出现在本地平台上的时候,你会记得它是如何永久性地改变3D编程世界的。由于能够在复杂模型、逼真效果以及反应性游戏中应用其必须的lightning-fast渲染,游戏的质量和复杂度都得到指数级的增长。
Flash Player是普及的,有着98%以上的市场渗透率。用Flash开发的游戏和应用可以立即在世界上任何一台电脑上运行。用户只需在浏览器中输入游戏的链接就可以马上开始游戏。除了Flash Player,它已经安装在大多数浏览器中,用户不需要安装任何其它东西:不需要专门的实时运行库,OS版本,或者专门的硬件。
Flash Player的普及和3D硬件加速是一种强力的组合,这或许能够永久的改变在线游戏,就像硬件加速3D的引入给电脑游戏带来的飞速变革一样。
想想当前常见的Flash网站:2D体验和内容。再考虑下在不久的将来这些会发生的改变,作为一个3D世界的3D体验,用户可以全身心的沉浸其中就像他们在亲身探索它一样。在未来,大多数网站可能会像一个与3D对象交互的电子游戏,而不是一个简单的2D体验。
这就是关于Stage3D的一切。利用任何一台当今计算机都有的3D硬件来构建3D游戏和3D互动网站,使每一台联入互联网的电脑都能轻易地体验它。
使用3D硬件加速
在这个部分,你会对使用3D硬件加速渲染Flash内容有大概的理解。
3D硬件加速使用一块非常先进的硬件,GPU,所有现代的计算机都装有GPU。GPU完全致力于渲染3D内容。
使用这项设置,软件,也就是你的Flash应用,将仅限于指定一个3D场景的定义。而将3D场景的数据交给GPU,这样硬件就能处理数据来渲染场景。这个过程比使用CPU的软件模式来渲染3D内容要快很多。
花点时间来比较下硬件渲染与软件渲染的区别。
大体来说,一个3D场景被定义为一组3D几何体(网格)。每个几何体又细分为一组三角形,每一个三角形又由一系列的顶点组成。这样,简而言之,定义一个3D场景就意味着定义一系列的顶点,并最终添加一些相关的渲染信息——比如纹理或者顶点颜色。
如果你使用传统的软件模式,一个类似Away3D的3D引擎会接收到这些顶点的数据流。它将会计算三角形的坐标定位然后指示Flash Player通过一系列的“fill”操作去一个接一个地渲染这些三角形。
虽然已精巧地编码于引擎中,这个过程却十分缓慢。在某些情况下,渲染结果不是特别的精确。内容是以每个三角形为单位渲染的,而不是以每个像素。这导致了深度排序错误。三角形有时会在错误的深度被渲染。
为了比较,Flash Player 10中的软件模式通常渲染最多4000个三角形来满足可以接受的表现效果。
现在考虑使用Stage3D的可能性。使用3D硬件加速,软件只简单地定义几何体然后将它们交给计算机的GPU。几何体定义被上传给位于显卡上供GPU使用的显存。GPU收到数据并处理它,完全接管了渲染3D内容的工作。
软件能更有效率的工作因为它只需将渲染所需参数传给GPU。比如,软件指定眼点(3D相机)在3D场景中的位置,设置场景中光源的位置,以及其它关于3D对象和场景效果的细节。
GPU收到所有这些数据。它开始分析这些定义的顶点并开始逐个三角形地渲染这个场景。GPU生成最终渲染完毕可以在屏幕显示的图像。
GPU的渲染过程要比软件模式快很多。GPU被设计为针对一个特别具体的任务:它只计算顶点并渲染三角形。就这样。
因为GPU被特别设计来针对这一具体任务,所以硬件加速的3D渲染过程就极其有效率。
相反,CPU是一个通用处理器。它没有为渲染三角形的任务而被特别优化。所以,在渲染操作时它就非常的低效,正如你使用软件模式渲染Flash 3D内容所经历的那样。
在数量上进行比较,渲染一个包含多于一百万个三角形的场景对于使用硬件加速来说是常见的。这与软件模式的4000个三角形渲染来比较是一个明显的提高。
对3D渲染管线的分析
3D渲染管线有助于基于硬件的3D渲染。“管线”(pipeline)一词指的是将渲染过程分解为一组基础操作。GPU由一系列的单元模块组成,每个模块负责上述基础操作中的一种。这些模块被设置成一个级联,这样每一个模块的输出正好成为下一个模块的输入。
最早发布的3D图形加速管线被称作固定功能管线。固定一词是指管线是无法被编程的。固定功能管线有些死板,因为它们只将接收到的几何体数据作为输入,通过管线单元处理这些数据,然后将生成的最终渲染图像作为输出。以固定功能方式管线工作,你需要给GPU输入下列信息:几何体的描述(顶点和三角形),应用于几何体的纹理,几何体在3D场景中的位置和方向,眼点(3D照相机)的位置和方向,光源(有多少,什么颜色,在什么位置),以及其它额外的用于指定渲染如何进行的参数。
换句话说,使用旧的系统,你提交给硬件一系列顶点/三角形/纹理的数据以及一串参数,3D硬件再来渲染它。
固定功能管线包括一个转换(transform)和光照(lighting)模块,用以将本地(模型)空间转换成顶点再投射给屏幕。它同样对每个顶点进行光照操作。这个模块后面紧跟一个视口剪裁(viewport clipping)模块,该模块负责将投射的内容剪裁成实际可见的内容,从而满足屏幕视口(viewport)的格式。
被投射和剪裁的数据接着由管线传递至光栅阶段,进行纹理贴图。最终还要进行雾化、α混合,并完成用适当的深度排序顺序渲染三角形像素所需的深度缓冲测试。
现在,固定功能管线已经被使用了很多年,但是随着时间的推移,这种渲染过程越发显得死板。尤其是对于光照,渲染仅限于使用标准的阴影模型(shading model),比如基本的Goraud和Phong。如果开发者要创新性地增添乐趣和有趣的效果,固定功能管线就显得不够灵活了。
那么,就要引入可编程图形管线。可编程图形管线的一个模块图示阐明了这种新的渲染过程。新系统最大的不同就是引入了两个叫做顶点着色 (Vertex Shader)和片段着色(Fragment Shader)的新模块。而这两个模块是可以编程的。当你编写一个使用可编程管线的应用时,你会写一段代码,称为着色器(Shaders),用以影响渲染管线中发生的事情。
管线中这看似很小的变动,着色器的引入,使3D渲染世界中的每件事都发生了改变。
通过编写程序来影响顶点如何变换和修改(顶点着色器)以及三角形像素颜色如何渲染(片段着色器),现在有可能创建之前不可能渲染出的惊艳效果。比如,使用着色器,你可以应用所有种类的光照技术,这些技术现在可以被自定义 为一个专门的应用,而不必使用管线提供的默认光照。着色器使添加阴影、硬件加速的骨架系统以及许多其它很酷的效果成为可能。
在该系列教程之后的部分,你会了解到如何使用着色器以及可编程渲染管线。但现在,最重要的是明白Stage3D是完全基于可编程功能管线的。你甚至没法选择使用固定功能管线。这将使你受益良多,因为着色器的可编程性将使你可以利用显卡的全部功能。你可以在Flash 3D项目中创建出令人叫绝的效果。
与能力相伴的是责任。为了运行即便是最简单的渲染,你也要编写自己的着色器。使用可编程渲染管线,你不会像之前使用固定功能管线那样仅仅设定一些参数就来渲染一个物体。
比较使用Stage3D的优势和限制
在这个部分,你会了解到与标准的本地平台3D APIs相比使用Stage3D的优势:OpenGL和DirectX。OpenGL和Direct X已经出现超过十年。它们是在现代计算机上创建大游戏的主流方法。
首先,使用DirectX和OpenGL创建一个3D应用不是一个简单的任务。渲染一个简单的三角形是相当容易的,但是在C++中创建一个完善的3D应用却是需要一些技巧的。这种任务显然不适合一个没经验的程序员。
建立一个本地3D应用的困难之一是使用标准DirectX和OpenGL APIs。为了给开发者提供显卡的全部功能,所开发的项目必须提供完整的硬件相关选项。本地APIs是真正贴近硬件的。开发包括应用运行时处理特定GPU的性能。还经常有必要调整代码,使其能充分利用最强大的硬件。这是开发者的福音,因为你会在最先进的硬件上创建最震撼的效果。但是这同样意味着应用必须要针对不同的硬件做调整和测试。
如果使用Stage3D就不是这样的情况。你只需按照Stage3D的概念编程,比如Context3D和Program3D。你建立的应用就可以在每个支持Flash Player(或者AIR runtime)的平台上运行。
总之,Stage3D比本地APIs要更抽象一些也更远离硬件本身。所以,Stage3D更易用。
当然,这也有一些缺点,因为你不是为了发挥每个平台特有的能力而专门针对某个硬件开发的。你只是为虚拟的Stage3D平台编写通用的代码,Stage3D就像一个介于你的代码和真实的硬件之间的中间层。
Stage的一大好处是你可以在一个应用中将3D硬件加速代码同2D Flash易于创作、功能强大的特点结合了起来。
当创建一个本地3D应用时,2D UI的建立一般需要自制解决方案,不像Flash的创作工具那么灵活。
使用Stage3D,正常的Flash 2D内容与Stage3D内容共存。所以,你既有Flash的全部功能又有3D加速内容。你可以选择在背景上建立一个主体3D情景或者嵌入一些小的3D事项将其作为2D体验的一部分。
Stage3D应用可以在众多平台上运行。你可以将Stage3D应用编译成AIR应用也可以用Flash Player来播放它们。这项功能意味着你可以使用Stage3D创建桌面3D应用,类似标准的桌面3D游戏。最终,也有可能以同样的代码将应用配置到移动平台,例如iOS和Android。想想安装了Flash Player群体数量的巨大穿透力,就会很容易明白Stage3D应用的潜在传播范围会有多么广泛。
认识Stage3D的限制
主要的缺点是基于对多个平台开发一个应用的能力。用一个统一的API来同时面向所有的平台,Stage3D无法利用多数强大的3D显卡中的先进功能。
为了保证一个应用适合所有平台,Stage3D需要根据图形处理能力在其所面向的所有平台中抽象出一个3D硬件设备。
比如,当今的GPU硬件支持Shader Model(用于顶点和片段着色器的标准)版本4.0。但是Stage3D却面向版本2.0的Shader Model。
这意味着当你在Stage3D中编程时,你会遇到一些在更高端的终端硬件中不会遇到的限制。比如,有权使用诸如AGAL阴影语言的着色寄存器数量是十分有限的。你最多能使用8个临时寄存器,而当使用Shader Model 4.0时,GLSL中有4096个编码寄存器可用。
Stage3D 着色器长度只能包含最大200个Op码,而Shader Model 4.0支持4096个。Stage3D 着色器不支持条件操作或者循环,而Shader Model 3.0和4.0支持。
换言之,与针对先进硬件和Shader Models的代码不同,Stage3D中的着色器是一个非常简单的程序。因此,一些在当今AAA游戏中采用的使用着色器的先进特效可能无法通过Stage3D来实现。
Stage3D: “舞台”背后的舞台
在这一部分,你会了解Stage3D是如何满足Flash的显示模式的。
Flash是基于舞台(Stage)的概念设计的。每一个在Flash中显示的物体都是一个添加到舞台上的DisplayObject。所以,舞台是一个盛放所有被显示事物的容器。它是所有2D事项的根。
当Adobe在Flash中引入3D渲染时,他们加入了一系列新的专门针对于3D的特殊舞台。这些特殊舞台被称为Stage3D。有一系列Stage3D舞台,它们被定位在主Flash舞台后面。这意味着你通过Stage3D创建的3D内容在每一个具体的Stage3D的矩形视口中被渲染,然后正常的2D Flash内容覆盖在它们上面。你可以看到你是如何以这种方式获得完美的两个层次:你使用硬件加速渲染了你的3D场景,并且把2D内容(诸如一个游戏UI)附于其上。可以利用强效而灵活的Flash来建立这个UI,而不是靠那些惯用的UI创建工具。
你有许多Stage3D可用。每一个Stage3D都有其自身的矩形视口。这意味着你可以在屏幕的一部分建立一个矩形3D区域而在别的什么地方添加另一个,并且将Flash 2D物体放在它们两个之上。各种Stage3D和StageVideo层可以部分(甚至完全)重叠。但是,第一版发布的Stage3D不支持混合图层(blending between layers)操作。因此,在一个Stage3D层覆盖另一个Stage3D层的地方,你只能看见最上面的一层。
用ActionScript访问Stage3D
为了使用ActionScript代码访问Stage3D,你要声明众多Stage3D舞台中的一个。这些舞台可以构成一个数组充当主Flash舞台中的一部分。
你可以编写类似这样的代码:
var stage3D:Stage3D = stage.stage3Ds[0];
Stage3D API的主类不是Stage3D本身。而是一个叫做Context3D的类,它大致上是一个渲染界面。它包含你要在其上渲染3D对象的界面,还包括实施渲染所需要的所有方法和属性。Context3D是你使用Stage3D时用到的主体。
使用Stage3D对象时你首要要做的是请求一个Context3D,像这样:
stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initStage3D );
stage.stage3Ds[0].requestContext3D();
使用Vertex Buffers和Index Buffers定义几何图形
在Stage3D中,你渲染的3D场景是由一系列的几何图形(3D网格)组成的。每个几何图形被定义为一系列的三角形。而每一个三角形又被定义为一系列的顶点。 所有描述几何体的顶点被包装在一起组成一个叫做Vertex Buffer的结构。这个结构包含所有与顶点相关的数据。将所有这些数据包装在一起是有用的,因为它们可以一次性的被上传至GPU内存。
这是定义一个Vertex Buffer的例子:
复制代码
首先,将所有顶点定义到一个向量(Vector)中。每一个顶点不仅包含顶点位置,还包含额外的用于指明这个顶点具体属性的数据条目。顶点结构中的这些部分被称作顶点属性(Vertex Attributes)。
顶点属性中包含的有关几何体的数据将指明几何体在渲染管线中如何被渲染。比如,为一个几何体制定的顶点颜色或者纹理的UV坐标都被视作顶点属性。
在上面的列子中,vertices 向量定义了4个顶点。每一行是一个顶点,并且每一个顶点包括两个顶点属性:顶点坐标(每行的前3个项)和顶点颜色(每行接下来的3个项)
创建完这个向量后,你要立即建立一个VertexBuffer3D。VertexBuffer3D是Stage3D API中的一个类,用以将Vertex Buffer数据打包到Stage3D API中从而将Vertex Buffer上传至GPU内存。
这是你会用来完成上述操作的代码:
复制代码
在Stage3D中,指定一个Vertex Buffer不足以定义一个几何体。这是因为Stage3D中的3D网格被定义成索引网格。
为了定义一个三角形,你必须指定它的三个顶点。这样,为了定义几何体三角形,你还需要一个额外的结构来从Vertex Buffer中提取顶点并把它们组装成三角形。这个结构就叫做Index Buffer。
如果你考虑下上面的Vertex Buffer定义了四个顶点,想象下最终是要定义一个由两个三角形组成的正方形。从Vertex Buffer中得到的四个顶点就不足以定义两个三角形从而定义正方形。
使用下述代码建立一个额外的Index Buffer:
复制代码
在这个代码范例中,indices 向量中的前三项,0、1、2,指定了第一个三角形由上述Vertex Buffer中的顶点0、1、2组成。接着,Index Buffer依旧从Vertex Buffer中生成了第二个三角形,由顶点2、3、0组成。
使用Index Buffer,你可以高效地定义三角形,从而创建几何体。
如同用Vertex Buffer,Index Buffer必须被打包成其专门的Stage3D结构,称作IndexBuffer3D。你可以使用IndexBuffer3D来将你的index buffer上传给GPU,像这样:
复制代码
这就是全部所需的,现在几何体被定义好了。