首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Flash >

Flash 3D年代,Molehill学习日记

2012-10-07 
Flash 3D时代,Molehill学习日记molehill incubator版本已经发布有些日子了。发布当天已经有好几篇文章详解

Flash 3D时代,Molehill学习日记

molehill incubator版本已经发布有些日子了。发布当天已经有好几篇文章详解了了hello world。如果你还不知道molehill是什么,你就out了。

它就是下一代Flash,这个WebGame事实标准的开发技术即将要迎来它的3D时代。

以下文章请恶补,也可以先看完我的分享。

http://labs.jam3.ca/2011/03/molehill-getting-started/http://www.bytearray.org/?p=2555

http://jamesli.cn/blog/http://yuleisunhong.wordpress.com/2011/03/30/%e6%88%91%e5%8f%abagal%ef%bc%8c%e6%9d%a5%e8%87%aaadobe-%e3%80%90part1%e3%80%91/(AGAL)看完大神们的文章,根据自己的理解,结合下面几个问题跟大家分享一下.个人学习总结,有个人揣测的地方不一定正确,还请纠正。1、molehill让显卡做什么事情?2、AGAL是什么?为什么都说AGAL是molehill核心?3、molehill API与AGAL编程4、Advanced “hello world”..?1、molehill让显卡做了些什么事情?
3D模型是由若干个顶点构成的三角面呈现(很多3D Demo中有当前绘制的tri数量,那个就是指同时绘制的三角形个数,家用机游戏机都号称百万级别,甚至千万级别)。

显卡通常做两件事情,即顶点(包括顶点颜色,和贴图UV)的计算和三角面的像素填充。

顶点计算的目的主要是因为模型通常需要移动,旋转,缩放。有时候顶点定义还包含颜色值和贴图坐标({x,y,z,r,g,b},{u,v}),这个颜色是干嘛用的呢?我不太敢确定是否已经完全理解顶点着色的概念。但是,万事皆有因。咱们从解决问题的根本出发,不难猜出这个颜色值就是用来指导三角面的像素填充的,这个过程就是着色。举例来说即时渲染的灯光效果就是靠顶点色来实现的。

完成顶点计算和三角面的像素填充,也就差不多完成了光栅化的主要过程。所谓光栅化,就是把定义的模型,贴图,灯光等数据最终变成屏幕上的像素点的过程。当然,这是指3D的光栅化过程。

2、AGAL是什么?为什么说AGAL是molehill核心。这里先YY一下固定编程管线和可编程图形管线,因为它跟AGAL的身世有关系。以下内容都是个人意淫的.如果各位大虾有真相请不吝分享。Flash 3D年代,Molehill学习日记?Flash 3D年代,Molehill学习日记
(上两张图来自9ria.com,图中第一张是固定管线工作流程,第二张是可编程管线工作流程,橙色部分的缺失就变成Shader要做的事情了)

当看到这两个概念时(可编程管线,固定编程管线),个人猜测原来的老显卡都只能实现一些固定的效果。随着3D技术的发达,玩家,设计师,策划各方面对效果的苛求,推进显卡技术也在不断发展。人们不希望显卡只能实现一些固定的功能和效果(主要是针对顶点和像素填充的算法)。显卡厂商也疲于针对一些顶点级,和像素级的特性出新产品,于是,可编程图形管线出现了。

说白了,这个过程就像是一次封装的重构,最开始显卡厂商把一些本该由用户来实现的功能封装在硬件里面了,所以用户在新需求产生的时候就悲剧了,因为硬件不可修改。所以显卡厂商重新对显卡功能进行封装,将可能由用户来控制(通常指顶点,和像素的变化)的逻辑部分从显卡里面分离出来。要实现这点,就需要有一种语言来沟通。于是有了Shader这个概念。

Shader就是一个交给显卡执行的程序。既然是程序就需要一种语言来编写,AGAL就是了。molehill的目的就是让ActionScript这种高级语言和AGAL这种低级语言进行交互,以实现ActionScript对显卡的调用。

3、molehill API与AGAL编程AGAL是一种汇编语言,一种低级语言。所以,首先别怕他,因为它本身真的很低级。下面摘取了一些AGAL的函数
add 0×01 add destination = source1 + source2, componentwise
sub 0×02 subtract destination = source1 – source2, componentwise
mul 0×03 multiply destination = source1 * source2, componentwise
div 0×04 divide destination = source1 / source2, componentwise
…..以下省略若干个函数。看看,AGAL干的事情,不外乎加,减,乘,除..此处省略若干数学计算操作.对,AGAL干的事情仅仅是计算.所以AGAL写的东西几乎纯粹是数学算法。简单的工具却直接考验你的智商。所以,如果你是图形算法工程师你会很喜欢它,他做的事情就是这么简单(不知道公司有没有这种专家..)。just 算..

先消除大家对AGAL的顾虑,接着入正题。好吧,开始写第一个AGAL程序,看看我们怎么用ActionScript3跟他交互。这个demo是Digging more into the Molehill APIs一文的源代码上修改的。模型改成立方体,支持鼠标旋转,贴图和顶点色混合。 文章中的demo只实现了一个三角形的渲染,包括顶点色和贴图,但是缺少贴图混合顶点色(动态光照)。

PS:并非一定要手写晦涩的AGAL语言,可以借助PixelBender3D(继承自PixelBender),可以用稍微高级的C语言来写你的Shader.PixelBender3D的学习日记后面我再整理后分享给大家。?package
{
?import com.adobe.utils.AGALMiniAssembler;
?
?import flash.display.Bitmap;
?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.Context3DTriangleFace;
?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.events.MouseEvent;
?import flash.geom.Matrix3D;
?import flash.geom.Rectangle;
?import flash.geom.Vector3D;
?import flash.utils.getTimer;
?[SWF(width="980", height="570", frameRate="60")]
?public class test extends Sprite
?{
??private var context3d:Context3D;
??private var vertexBuffer:VertexBuffer3D;
??private var indexBuffer:IndexBuffer3D;
??private var program:Program3D;
??private var model:Matrix3D=new Matrix3D();
??
?
??private var tx:int=0;
??private var ty:int=0;
??private var tz:int=0;
??private var step:int;
??//贴图资源
??[Embed(source="map.jpg")]
??private var bmp:Class;
??
??private var bb:Bitmap;
??public function test()
??{
???stage.scaleMode=StageScaleMode.NO_SCALE;
???stage.align=StageAlign.TOP_LEFT;
???//一切基于Context3D, 这个类可以理解为一个3D环境的配置类.它的建立过程是异步,所以需要侦听。
???//stage3Ds可以理解为3dLayer,可以有多个.
???stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onGotContext);
???//请求一个Context3D
???stage.stage3Ds[0].requestContext3D();
???//设置视口区域
???stage.stage3Ds[0].viewPort=new Rectangle(0, 0, 570, 570);
???bb=new bmp
??}
??protected function onGotContext(ev:Event):void
??{
???var stage3d:Stage3D=Stage3D(ev.currentTarget);
???//终于拿到Context3D
???context3d=stage3d.context3D;
???if(context3d==null)
????return;
??????????? //顾名思义,打开就好。有帮助
???context3d.enableErrorChecking=true;
???//这个也是顾名思义,一般来说比视口稍大即可
???context3d.configureBackBuffer(980, 570,2, true);
???//设置三角片剔除选项(用来提升性能,比如我们看不到的地方根本不需要作处理。
???//可以设置Context3DTriangleFace.BACK)
???context3d.setCulling(Context3DTriangleFace.NONE);
???//8个顶点,且每个顶点8个元素<x,y,z,r,g,b,u,v>
???//坐标,颜色,贴图都有了,因为我们要实现贴图和顶点色的混合
???vertexBuffer=context3d.createVertexBuffer(8, 8);
???//立方体6个面需要36个顶点索引构建12个三角形
???indexBuffer=context3d.createIndexBuffer(36);
???//顶点数据.注意这里面的值都是比例值
???//x,y,z是-1到1(视口正中间为0),r,g,b是0×00-0xff,u,v是0-贴图尺寸
???var vertexData:Vector.<Number>=Vector.<Number>(
????[
?????-.2,? .2,?? .3,? 0,? 0,1,0,0, //<- 1st vertex x,y,z,r,g,b,u,v
?????.2,? .2,??? .3,? 1,? 0,1,1,0,
?????.2,? -.2,?? .3,? 1,? 1,1,0,1,
?????-0.2, -.2,? .3,? 0,? 1,1,0,1,
?????
?????-.2,? .2,?? .5,? 0,? 0,1,0,0,
?????.2,? .2,??? .5,? 1,? 0,1,1,0,
?????.2,? -.2,?? .5,? 1,? 1,1,0,1,
?????-0.2, -.2,? .5,? 0,? 0,1,1,0
????]
???);
???//定义12个三角形,三点一面。0,1,2…是上面顶点的index值
???var indexData:Vector.<uint>=Vector.<uint>([0,1,2,
?????????????????????????????????????????? 0,2,3,
??????????????? 0,3,4,
??????????????? 3,4,7,
??????????????? 0,4,5,
??????????????? 0,1,5,
??????????????? 1,2,5,
??????????????? 5,2,6,
??????????????? 5,4,7,
??????????????? 5,7,6,
????????????????????????????????????????????? 3,6,7,
????????????????????????????????????????????? 3,2,6]);
???//向顶点buffer传递8个顶点
???vertexBuffer.uploadFromVector(vertexData, 0,8);
???//向索引buffer传递36个索引值
???indexBuffer.uploadFromVector(indexData, 0, 36);
???
???//AGALMiniAssembler这玩意是用来编译Shader的,Shader最终的形式就是二进制数据
???//这里定义两个编译器,一个为顶点Shader所用,一个为着色Shader所用
???var agalVertex:AGALMiniAssembler=new AGALMiniAssembler();
???var agalFragment:AGALMiniAssembler=new AGALMiniAssembler();
???//呐,这就是AGAL语言。for VertexShader
???var agalVertexSource:String=
????//计算顶点结果=原始顶点值(x,y,z)*Matrix3D变换矩阵、目的就是通过实现了旋转,缩放,位移等
????//变化的Matrix3D来影响顶点。
????//普通3D游戏的顶点Shader主要逻辑就写好了.
????//va0即x,y,z.vc0即Matrix3D
????”m44 op, va0, vc0\n” +
????//将r,g,b值传给FragmentShader,暂存vo这个寄存器
????”mov v0, va1\n”+
????//将u,v传给FragmentShader,暂存v1这个寄存器
????”mov v1,va2″;
?????? //顶点Shader就写好了,是不是很简单。v<n>是内部的寄存器,用来将顶点Shader的数据传递给FragmentShader
?????? //一般来说我们需要将顶点定义中的r,g,b和u,u传递给FragmentShader
?????? //也可以在顶点上定义一个引力值g.<x,y,z,r,g,b,u,v,g>
?????? //你可以增加一句mov v2,va3就可以传递给FragmentShader了
?????? //总共有8个内部寄存器可以用
???
??????????? //for FragmentShader
???var agalFragmentSource:String=
????//将U,V的值存入临时变量寄存器ft<n>
????”mov ft0, v0 \n”+
????//根据传入的U,V即ft0对材质fs1进行采样,并将结果存入ft1
????”tex ft1, ft0, fs1 <2d,repeat,nearest> \n”+
????//贴图寄存器和顶点色色做mul计算
????”mul ft2,v1,ft1\n”+// sample texture 1
????//fc0是外部传入的颜色变换矩阵用来改变ft2的值
????”add ft2,fc0,ft2\n”+
????//输出ft2
????”mov oc, ft2 \n”;
?????? //FragmentShader到此结束
???
???//传入AGAL源代码,编译
???agalVertex.assemble(Context3DProgramType.VERTEX, agalVertexSource);
???agalFragment.assemble(Context3DProgramType.FRAGMENT, agalFragmentSource);
???//生成Program3D类,此类用于接收AGAL编译程序
???program=context3d.createProgram();
???//呐,看他接收了Shader的字节码。
???program.upload(agalVertex.agalcode, agalFragment.agalcode);
???
???//侦听鼠标移动,滚轮和enterFrame
???addEventListener(Event.ENTER_FRAME, onRenderLoop);
???stage.addEventListener(MouseEvent.MOUSE_MOVE,onMove);
???stage.addEventListener(MouseEvent.MOUSE_WHEEL,onWheel);
??}
??private function onWheel(e:MouseEvent):void
??{
???tz+=e.delta;
??}
??
??private function onMove(e:MouseEvent):void
??{
???tx=e.stageX/570*720
???ty=e.stageY/570*720
??}
??protected function onRenderLoop(event:Event):void
??{
???context3d.clear();
???context3d.setProgram(program);
???
???//这里三次调用setVertexBufferAt 指定AGAL中的va0,va1,va2即顶点x,y,y贴图坐标u,v和r,g,b
???context3d.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
???context3d.setVertexBufferAt(1,vertexBuffer,3, Context3DVertexBufferFormat.FLOAT_2);
???context3d.setVertexBufferAt(2,vertexBuffer,5, Context3DVertexBufferFormat.FLOAT_3);
???
???//创建贴图类
???var txt:Texture=context3d.createTexture(256,256,Context3DTextureFormat.BGRA,false)
???txt.uploadFromBitmapData(bb.bitmapData);
???
???//传入贴图数据,对应AGAL中的fs1
???context3d.setTextureAt(1,txt);
???
??
???model.identity();
???
???//三轴旋转
???model.appendRotation(ty,Vector3D.X_AXIS,new Vector3D(0,0,.4));
???model.appendRotation(tx,Vector3D.Y_AXIS,new Vector3D(0,0,.4));
???model.appendRotation(tz,Vector3D.Z_AXIS);
???
??
???//传入Matrix3D变换矩阵。对应AGAL中的vc0
???context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, model,true);
???
???//这里传入的Vector 就是上文AGAL中 “add ft2,fc0,ft2\n” 的fc0 ,用来改变定点色
???context3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT,
??????????????????????????????????????????????????????????????????? 0,
?????????????????? Vector.<Number>( [ tz*.1,tz*.3, tz*.2,? 1] )
??????????????????????????? );
???
???//绘制三角形
???context3d.drawTriangles(indexBuffer, 0,12);
???//显示
???context3d.present();
??}
?}
}
?最后摘抄一段AGAL的寄存器解释,非常有助于理解AGAL
? 1。属性寄存器

这些寄存器参考顶点着色器的VertexBuffer输入。因此,他们只能在顶点着色器中可用。

要通过正确的索引分配一个VertexBuffer到一个特定的属性寄存器,使用方法

Context3D:setVertexBufferAt()

在着色器中,访问属性寄存器的语法:va<n>,其中<n>是属性寄存器的索引号。

有一共有8个属性寄存器用于顶点着色器。

2。常量寄存器

这些寄存器是用来从ActionScript传递参数??到着色的。这是通过Context3D::setProgramConstants()系列函数来实现。

在着色器中,这些寄存器的访问语法:

vc<n>,用于顶点着色器

fc<n>,用于像素着色器

其中<n>是常量寄存器的索引值。

有128个常量寄存器用于顶点着色器和28常量寄存器用于像素着色器。

3。临时寄存器

这些寄存器在着色器中,可以用于临时计算。

这些寄存器的访问语法:

vt<n> (vertex),用于顶点着色器

ft<n> (pixel),用于像素着色器

<n>是寄存器编号。

有8个用于顶点着色器,8个用于像素着色器。

4。输出寄存器

输出寄存器是在顶点和像素着色器存储其计算输出。此输出用于顶点着色器是顶点的剪辑空间位置。用于像素着色器是该像素的颜色。

访问这些寄存器运算的语法:

op,用于顶点着色器

oc,用于像素着色器

但显然只能一个输出寄存器用于顶点和像素着色器。

5。变寄存器

这些寄存器用来从顶点着色器传递数据到像素着色器。传递数据被正确地插入图形芯片,从而使像素着色器接收到正确的正在处理的像素的值。

以这种方式获取传递的典型数据是顶点颜色,或 纹理UV 坐标。

这些寄存器可以被访问的语法v <n>,其中<n>是寄存器编号。

有8个变寄存器可用。

6。纹理取样器

纹理采样寄存器是用来基于UV坐标从纹理中获取颜色值。

纹理是通过ActionScriptcall指定方法Context3D::setTextureAt()。

纹理样本的使用语法是:ft<n> <flags>,其中<n>是取样指数,<flags>是由一个或多个标记,用于指定如何进行采样。

<flags>是以逗号分隔的一组字符串,它定义:

纹理尺寸。可以是:二维,三维,多维数据集
纹理映射。可以是:nomip,mipnone,mipnearest,mipnone
纹理过滤。可以是:最近点采样,线性
纹理重复。可以是:重复,包装,夹取。
因此,举例来说,一个标准的2D纹理没有纹理映射,并进行线性过滤,可以进行采样到临时寄存器FT1,使用以下命令:
“tex ft1, v0, fs0 <2d,linear,nomip> “

变寄存器v0持有插值的纹理 UVs。

热点排行