深入挖掘Molehill API的一些特征

几个月前,在洛杉矶举办的Max2010大会上我们介绍了有关Molehill API在Flash桌面平台和移动平台应用上的简单情况.具体情况可参考这里: Molehill” page .本文我想从AS开发者的角度详细阐述和Molehill有关的技术特征.

让我们开始吧

什么是Molehill?

“Molehill”是一个API 的项目名,这一系列API的作用是暴露给Flash平台上的ActionScript3.0语言一些可编程化接口,从而能让as3直接调用底层3D加速. 它能给Flash平台带来高质量的3D渲染.Molehill依靠Windows上的DirectX9,MacOS和Linux上的OpenGL1.3, 在手机平台比如Android,Molehill利用OpenGL ES2来渲染.从技术角度来说,Molehill是真正基于着色器的3D GPU编程(关于着色器的更多情况:http://goo.gl/wqKHs ),并给3D Flash开发者带来了他们想要的一些新特征,比如基于顶点着色器和片段着色器(在Direct3D中称为像素着色器,在OpenGL中成为片段着色器 )的编程,不但支持像顶点纹理处理这样的事情更包括原生深度缓冲,模板颜色缓冲,立方纹理等等(专 业术语太多,附上原文:to enable things like vertex skinning on the GPU for bones animation but also native z-buffering, stencil color buffer, cube textures and more. )

从性能角度考虑,对于FlashPlayer10.1版本,渲染数 个没有Z轴深度缓冲的三角化物体接近30Hz,通过新的3D ApI,开发者在全屏高清环境下渲染数 个有Z轴深度缓冲的三角化物体频率能达到60Hz! Molehill使为网络传输和绝大多数设备带来高质量3D体验成为可能.可以通过 这个 视频来了解Molehill的具体表现.

它工作的方式

在fp10版本中现有的2.5D API版本并没有被贬低,Molehill API将会提供一系列完全通过GPU加速的高级3D渲染.根据项目的具体情况你可以选择使用哪个API版本.

最近我们介绍的fp10.2中的 “Stage Video” 测试版已经可以通过Adobe labs下载了.

StageVideo使用同样的设计,利用GPU加速渲染高清影像.通过该渲染引擎,FP不是通过将视频帧或者3D缓冲加入显示列表靠CPU计算而是加入到舞台旁边的纹理处理器并通过GPU渲染(原文:but inside a texture sitting behind the stage painted through the GPU ).这个允许FP直接调用显卡资源处理屏幕上的显示内容.处理是单向的,显卡将处理好的内容直接交给CPU,CPU将数据加入到显示列表最后依靠FP来显示(No more read back is required, to retrieve the frames from the GPU to push them on screen through the display list on the CPU )

由于3D内容处在FP的舞台旁边并且不属于显示列表的内容,Context3DStage3D 并不是显示对象,所以记住你不能像使用DisplayObject 类那样使用它们,rotation,遮罩,滤镜以及其他很多效果都无法应用.

以下这样图演示了我们的想法

当然,正如你看到的,2D内容可以包围3D的内容,但是反过来却不行.然而如果需要的话我们提供了一个API允许你将3D内容以Bitmapdata方式呈现.作为一个开发者,我们的绝大多数工作是和Context3D 以及Stage3D 打交道,当你需要一个3D内容时创建一个Context3D对象.也许你现在会想,如果GPU驱动不兼容怎么办,难道我将静默的得到一个黑屏?

FP依然会返回给你一个Context3D,只是此时的内容是软件内部模拟产生的.所以你依然可以使用全部的Molehill API只是此时渲染是通过CPU来计算罢了. 为了验证我们我们所说的,我们依靠TransGaming公司的一个名为”SwiftShader”的显卡渲染增强软件(CPU rasterizer), 好消息是即使使用软件模拟SwiftShader的渲染速度依然快于FP10倍,所以你可以运行软模拟来预计一些严重的性能差异提升.

“Molehill”API的好处 在于你不需要了解它的内部是怎么工作的.我现在运行的是DirectX,OpenGL还是SwiftShader?我是否该使用不同的API当运行的平台 不一样的时候?不,所有的都会转换成同一种编程模式同一个API接口,FP将会在内部自己处理这些不同.

需要谨记的是, Molehill API只使用programmable pipeline而不使用fixed function pipeline(关于两者的更多情况 http://goo.gl/HHPNN ),这意味着你需要使用三角顶点和像素来表现所有需要显示的对象.于此,你将能传给显卡你的着色器当作低级别的AGAL(”Adobe Graphics Assembly Language”)形式的二进制比特流(原 文;For this, you will be able to upload on the graphics card your shaders as pure low-level AGAL (“Adobe Graphics Assembly Language”) bytecode as a ByteArray ),身为一个开发者,你有两种工作方式,第一种使用底层方法编写你的渲染器,这需要对渲染器的工作方式有清晰的了解或者使用高级语言比如Pixel Bender 3D里面集成的脚本语言那样帮助我们转换成AGAL字节码.

为了表现你的三角图元,你需要了解VertexBuffer3DIndexBuffer3D 对象,通过传入三角顶点坐标以及索引来构造,一旦你的顶点着色器和像素着色器准备完毕,你需要通过Program3D 对象来将它们传输给显卡处理.基本上说顶点着色器负责处理你绘制的三角形的顶点坐标而像素着色器负责逐像素计算颜色以及凹凸纹理映射.

下面这幅图阐述了两种着色器间的不同

就像之前说的一样,Molehill不是用fixed function pipeline,因此开发者可以自由的创建他们自定义的着色器并且完全控制渲染流水线.那么让我们来研究下Molehill下的顶点着色器和像素着色器吧

深入了解顶点和像素着色器

为了阐明这个观点.先来展示一个使用低级别的渲染装配编写的三角形并用到了像素级Molehill.让我们准备好吧.假设我们将要编写一个用低级别着色器渲染出来金属材质,如果你对此感到讨厌,不要担心可以使用类似于PixelBender3D这种高级别的语言来编写.

为了创建一个能给显卡处理的着色单元,我们需要使用一个顶点着色器(Context3DProgramType.VERTEX )来至少输出夹式空间( clip-space :视锥体裁剪之后的空间)的位置坐标.为了实现这个,我们需要用va0 (每个三角顶点的位置属性)乘以vc0 (常量 0),我们的投影矩阵存储在这个索引里并且通过op (output position的缩写)关键字来输出结果:

// 用汇编创建一个顶点程序
var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();

vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
"m44 op, va0, vc0 \n" // 4x4 的从0(顶点位置)变换到输出夹式空间的矩阵
);

你可能会疑惑这个m44是什么,他从哪里来?

这其实是一个4×4的矩阵转换,它延伸至我们之前定义的矩阵投影.我们可以编写一个着色器利用如下计算方法来手动计算每个点属性乘积,但是m44 指令(在同一行里执行一个4×4的矩阵所有属性变换)远远短于前面的.


// create a vertex program - from assembly

var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler( ) ;

记住vc0(顶点常量 0)实际上只是我们存储在序列里的投影矩阵,作为早些时候通过setProgramsConstantMatrix方法传递给Context3D对象的常量:

context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, modelMatrix, true );

正如我们的矩阵不变,va0(顶点属性0)的位置,需要加以界定,而且我们也通过Context3D对象的setVertexBufferAt 方法来实现的。

context3D.setVertexBufferAt (0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3 );

在我们的例子中,顶点着色器将顶点颜色(va1)通过v0和MOV指令传给像素着色器以便实际用来绘制我们的三角形图像.要实现这一点我们可以写如下内容:

// create a vertex program - from assembly var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler(); vertexShaderAssembler.assemble( Context3DProgramType.VERTEX, "m44 op, va0, vc0 \n" + // 4x4 matrix transform from stream 0 (vertex position) to output clipspace "mov v0, va1 \n" // copy stream 1 (vertex color) to fragment shader );


正如你想象的那样,va1(顶点属性1)的颜色是通过setVertexBufferAt定义,为了暴露我们在着色器里的像素颜色(float 3)

context3D.setVertexBufferAt( 1, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_3 );

我们的顶点位置和颜色被定义为我们的VertexBuffer3D对象:

// create a vertex buffer
// format is (x,y,z,r,g,b) = 3 vertices, 6 dwords per vertex
vertexbuffer.uploadFromVector ( Vector.<Number>([
-1,-1,0, 255/255,0,0, // red
0,1,0, 193/255,216/255,47/255, // green
1,-1,0, 0,164/255,228/255 // blue
]),0, 3 ); // start at offset 0, count 3

我们有了定义好的顶点着色器,现在我们需要确定并上传我们的像素着色器(Context3DProgramType.FRAGMENT),这个想法是检索每个传入的顶点颜色(复制va1给v0)并且通过oc操作码输出颜色.

var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
"mov oc, v0" // output color
);

你可以想象,一个像素着色器应该总是输出颜色。然后,我们需要上传所有这一切给Context3D对象:

// upload the AGAL bytecode
program = context3D.createProgram();
program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode );

如果我们编译和运行这些着色器,我们会得到以下结果:

现在,假设我们需要反转每个像素的颜色,那将是很容易。由于这次行为只是针对像素的颜色,我们将只需要修改我们的像素着色器,并使用sub操作码减去颜色:

var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
"sub ft0, fc1, v0 \n" + // subtract the color ( 1 - color)
"mov oc, ft0" // output color

context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, Vector.( [ 1, 1, 1, 1 ] ) );

最后一个像素的颜色被储存在一个临时的片段寄存器(ft0)并作为最终输出的色彩.

通过使用这种方式修改的像素着色器,我们最终结果如下:

作为另一个练习,让我们处理一个棕褐色的滤镜。

为了实现这一目标,我们需要首先转换为灰度,然后为褐色。我们会用如下的像素着色器

var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler(); fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT, "dp3 ft0, fc1, v0 \n" + // convert to grayscale "mul ft1, fc2, ft0 \n" + // convert to sepia "mov oc, ft1" // output color );

像之前一样,我们使用setPrograConstantsFromVector定义的常量:

// grayscale context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, Vector.<Number>( [ 0.3, 0.59, 0.11, 1 ] ) ); // sepia context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, Vector.<Number>( [ 1.2, 1.0, 0.8, 1 ] ) );

使用这样一个像素着色器,我们最终会得到以下结果:

你可以想像,这将给你带来很大的权力,并允许你更加深入的使用顶点或像素着色器处理像灯光,雾,甚至是带有皮肤顶点的动画和其他事物.

好了,最后一个,现在让我们为一个三角形通过BitmapData应用一个纹理,要做到这一点,我们需要从顶点着色器传递uv 值给像素着色器然后用这些值来应用于我们的纹理.

要传递uv值,我们需要通过这种形式来修改我们的顶点着色器:

vertexShaderAssembler = new AGALMiniAssembler(); vertexShaderAssembler.assemble( Context3DProgramType.VERTEX, "m44 op, va0, vc0 \n" + // 4x4 matrix transform from stream 0 to output clipspace "mov v0, va1 \n" // copy texcoord from stream 1 to fragment program );

现在我们的uv坐标从va1复制到v0的,随时准备传递给像素着色器.请注意我们并没有传递顶点着色器给像素着色器,只是uv坐标.

正如预期的那样,我们通过setVertexBufferAt 方法将va1值作为uv值传递给每个顶点(float 2)

context3D.setVertexBufferAt( 1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2 );

我们的顶点位置和uv值定义到VertexBuffer3D对象中:

vertexBuffer.uploadFromVector( Vector.<Number>( [ // x,y,u,v -1,-1, 0,1, 0,1, 1,0, 1,-1, 1,1, ] ), 0, 3 );

然后我们检索在像素着色器和简单纹理中的值:

fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT, "mov ft0, v0 \n"+ "tex ft1, ft0, fs1 <2d,clamp,linear> \n"+ // sample texture 1 "mov oc, ft1 \n" );

要定义我们的纹理,我们实例化我们的BitmapData,上传进一个纹理对象并把该对象上传到GPU:

texture = context3D.createTexture( 256, 256, Context3DTextureFormat.BGRA, false ); var bitmap:Bitmap = new MolePeopleBitmap(); texture.uploadFromBitmapData( bitmap.bitmapData );

然后从fs1访问它,我们设置它:

context3D.setTextureAt( 1, texture );

通过使用这种修改,最后的结果如下:

我将在以后的教程中提到一些新效果比如每个像素雾化或者热度纹理特征等等.

当然.我们只是在这里介绍Molehill如何工作.要控制你的图像你需要定义三角形和顶点和索引值,对于这一点,你需要其他如VertexBuffer3D和IndexBuffer3D这样的对象的帮助.

图说明了所有对象之间的联系:

正如你所看到的,Molehill的API都是低级别的,并且暴露了一些特征给高级3D开发者,当然,一些开发者会更喜欢使用高层次的框架,这些框架正准备公开API,并且我们对此比较关心.

当作一个非常重要的儿戏

我们知道很多ActionScript 3的开发者\使用灯光,镜头,工作平面,而不是使用顶点缓冲和像素字节码.所以为了确保每个人都能享受Molehill带来的强大,我们己经在一些现在的 框架比如Alternativa3D, Flare3D, Away3D, Sophie3D, Yogurt3D 等咱开移植工作.如今大多数的框架都兼容Molehill并且将在下一个版本中可用.

从这些框架的开发者大多是在今年MAX大会上表明他们如何在各自的框架上撬起这个Molehill.我们期望开发建立在Molehill的引擎的高层次上,因此高级三D开发者和高级开发人员都能从Molehill中受益.

我希望你能通过这篇文章轻微驾驭Molehill,期待更多和Molehill相关的消息吧:-)


);

在这里,我们倒置的将现有的颜色和1相减(1-color)。用来相减的白色的像素被我们存储在一个固定的像素着色器中(fc1),可以通过setProgramConstantsFromVector 方法来改变.


vertexShaderAssembler.assemble ( Context3DProgramType.VERTEX ,
"dp4 op.x, va0, vc0 \n " + // 4x4 matrix transform from stream 0 (vertex position) to output clipspace
"dp4 op.y, va0, vc1 \n " +
"dp4 op.z, va0, vc2 \n " +
"dp4 op.w, va0, vc3 \n " +
) ;

你可能感兴趣的:(编程,框架,数据挖掘,Flash,FP)