Flash 3D时代,Molehill学习日记

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

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

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

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的身世有关系。
以下内容都是个人意淫的.如果各位大虾有真相请不吝分享。
 
(上两张图来自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。

你可能感兴趣的:(游戏,编程,算法,Flash,actionscript)