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。 |