使用 Flash 3D API

这里的小教程向您解释如何在haxe里使用最新的Flash Playr 11 3D APIs

安装

请依次按以下操作进行:

  • 下载Flash Player 11(或更高版本) ,点这里下载 Adobe
  • 安装 Haxe 2.08 Release
  • 下载并解压molehill.zip Haxe 例子(含基本的3d类库:顶点,uv等)
  • 当Haxe安装后,运行下面命令行安装第三方库 format :
    haxelib install format

例子 0 - 立方体

我们先做一个带有颜色的立方体例子。

你可以通过简单的命令行执行 haxe test.hxml命令构建一个输出文件 test.swf,并在你的浏览器测试这个swf,(运行前请注意确定你已经安装了Flash Player 11。)

molehill_1.png

下面我们一起分析文件 Test.hx :

new 函数 (Haxe 类构造函数), 我们装配一个新的3D场景(3D stage)用于3D渲染 . 代码如下 :

var s : flash.display.Stage3D;
//...
stage = flash.Lib.current.stage;
s = stage.stage3Ds[0];
s.addEventListener( flash.events.Event.CONTEXT3D_CREATE, onReady );
s.requestContext3D();

当3D环境(3D context)准备妥当,我们要配置它,并分配几项内容:一个新的着色器(Shader),一个新的相机( Camera),还有我们的一个3D立方体(3D Cube polygon).

var c : flash.display3D.Context3D;
var shader : Shader;
var pol : Polygon;
var camera : Camera;
// ...
function onReady( _ ) {
    c = s.context3D;
    c.enableErrorChecking = true;
    c.configureBackBuffer( stage.stageWidth, stage.stageHeight, 0, true );

    shader = new Shader(c);
    camera = new Camera();

    pol = new Cube();
    pol.alloc(c);
}

update函数内,我们将开始装配一些3D环境(context 3D)需要的内容。

c.clear(0, 0, 0, 1);
c.setDepthTest( true, flash.display3D.Context3DCompareMode.LESS_EQUAL );
c.setCulling(flash.display3D.Context3DTriangleFace.BACK);

接着处理相机移动(camera movement):通过方向键和+/-键进行控制

if( keys[K.UP] )
    camera.moveAxis(0,-0.1);
if( keys[K.DOWN] )
    camera.moveAxis(0,0.1);
if( keys[K.LEFT] )
    camera.moveAxis(-0.1,0);
if( keys[K.RIGHT] )
    camera.moveAxis(0.1, 0);
if( keys[109] )
    camera.zoom /= 1.05;
if( keys[107] )
    camera.zoom *= 1.05;
camera.update();

使用相机投影矩阵,把3d映射到2d屏幕。使用立方体转换矩阵使立方体绕Z轴进行旋转:

var project = camera.m.toMatrix();
var mpos = new flash.geom.Matrix3D();
mpos.appendRotation(t * 10, flash.geom.Vector3D.Z_AXIS);

剩下的是简单的装配我们的着色器(shader),绘制我们的立方体,并在3d环境(3D context)内显示:

shader.init(
    { mpos : mpos, mproj : project },
    {}
);
shader.bind(pol.vbuf);
c.drawTriangles(pol.ibuf);
c.present();

我们将看到一个旋转的3D立方体。请注意颜色是基于立方体顶点来描绘,色彩从(0,0,0)=黑色到(1,1,1)=白色。

着色器(Shaders)

第一部分我们没涉及立方体如何改变旋转和色彩变化,这个工作时由着色器完成了。与AS3创建着色器不同的是,AS3使用晦涩难懂的汇编语言来创建,而haxe是简单直接使用hxsl - Haxe 着色器语言 这个语言来创建。如你所见,在"Test.hx"相关代码如下:

class Shader extends format.hxsl.Shader {
    static var SRC = {
        var input : {
            pos : Float3,
        };
        var color : Float3;
        function vertex( mpos : M44, mproj : M44 ) {
            out = pos.xyzw * mpos * mproj;
            color = pos;
        }
        function fragment() {
            out = color.xyzw;
        }
    }
}

@:shader section contains Haxe Metadata which is compiled at the same time you're compiling your project by using Haxe .

要了解完整的着色器,请看 hxsl - Haxe 着色器语言 文档.

例子 1 - 灯光 Lighting

在这个例子里,我们将使用灯光照亮我们的3D立方体,它使用一个简单算法(Gouraud algorithm).为此我们给3D立方体添加 normals,在创建立方体完成后,添加如下代码:

pol.addNormals();

This will calculate a per-vertex normal. Since the cube points are shared by triangles faces, you'll get very smooth normals.
这样就可以计算每个顶点法线。由于立方体是由三角形面点共享,你会得到非常光滑的法线。

现在我们已经有了法线,让我们创建一个旋转的灯光,做为参数把它传递给我们的着色器:

var light = new flash.geom.Vector3D(
    Math.cos(t * 10) * 1,
    Math.sin(t * 5) * 2,
    Math.sin(t) * Math.cos(t) * 2
);
light.normalize();
shader.init(
    { mpos : mpos, mproj : project, light : light },
    {}
);

接着,我们只要修改我们的着色器代码。为了计算出光功率,我们在转化法线和光方向之间做积运算。我们要确保光强度不能为负,为此每个顶点的颜色范围设定为0-1:

var tnorm = (norm * mpos).normalize();
var lpow = light.dot(tnorm).max(0);
color = pos * lpow;

molehill_2.png

如果你想有更多的正交法线,你可以简单地确保每个顶点有唯一法线,在下面代码计算法线之前添加:

pol.unindex();

例子 2 - 材质贴图 Texturing

在这个例子里,我们将添加一个材质贴图。为此,我们先给3D立方体添加纹理坐标:

pol.addTCoords();

请注意,纹理坐标将要求立方体点是已建立索引的。

然后,我们将从创建一个嵌入材质贴图文件并分配给我们的3D环境

@:bitmap("hxlogo.png")
class HaxeLogo extends flash.display.BitmapData {
}
// ...
var logo = new HaxeLogo();
texture = c.createTexture(logo.width, logo.height, flash.display3D.Context3DTextureFormat.BGRA, false);
texture.uploadFromBitmapData(logo);

我们把材质贴图作为参数传递给我们的着色器:

shader.init(
    { mpos : mpos, mproj : project, light : light },
    { tex : texture }
);

现在剩下的就是简单地修改我们的着色器,着色器通过纹理坐标读取保存在立方体内的贴图颜色值:

var input : {
    pos : Float3,
    norm : Float3,
    uv : Float2,
};
var tuv : Float2;
var lpow : Float;
function vertex( mpos : M44, mproj : M44, light : Float3 ) {
    out = pos.xyzw * mpos * mproj;
    var tnorm = (norm * mpos).normalize();
    lpow = light.dot(tnorm).max(0);
    tuv = uv;
}
function fragment( tex : Texture ) {
    out = tex.get(tuv) * (lpow * 0.8 + 0.2);
}

molehill_3.png

请注意,我们使用了一些0-1光功率因数操作。在这种情况下,我们还增加了20%的环境光,以确保背对灯光时候贴图颜色亮度不会完全变黑。

version #12118, modified 2011-12-31 17:00:26 by haha123_0