Flash 三维引擎初探
本文将介绍的是一个简单的实时3D引擎,并以此向大家阐明一种用Action Script在Flash二维的世界里表现三维对象的方法。先看看一个线描的简单实例,这是一个三棱锥绕自己的一条棱旋转的动画。文章末尾提供源文件下载学习: 基本原理:我们所生活的世界是一个由x,y,z轴(也就是长宽高)组成的三维世界,要在一个二维平面上表现出三维的信息,我们首先必须要去掉三个坐标轴中的一个,z轴(表示物体深度的轴)。此外,我们还必须要选择一个视点,也就是代表观察者眼睛的一个点,和一个用于投影三维物体所有点的视平面,也就是我们所看见的那个面。然后,从三维物体上每个点到视点之间连线和视平面的交点就是该点在视平面上所应在位置。看了上面的一长串话是否有点头晕?没关系,请看看下面的那张图。
从图中我们可以看出当视点和物体AB位置不变,如果视平面S离物体AB越近,那么物体AB在视平面S上的投影A'B'(即我们所看到的对象)就越大。
实例:知道大致原理后,我们来看个三棱锥的例子。为了简单起见,我们假设视点,也就是眼睛的位置,是处于z轴之上的;另外,视平面S平行于x,y轴。
预备
新建一个Flash文件,并新建两个层,分别命名为actions和modal。新建一个图形元件(Graphic Symbol)并将之命名为line,在此元件中画一条沿45度角斜向右下方的直线并将之长宽皆设为100。
在actions层中插入9帧空白关键帧,并依次命名为modal info,init,start,loop,drawline,draw,rotx,roty和rotz。在modal层上插入4帧,然后将line元件从Library中拖到舞台上并将此实例命名为baseline。
一、 模型定义
使用某种编码方式将我们所要进行操作的三维对象表示出来的就是本步骤要做的。在这里,我们只建立简单的框架三维模型,所以只定义模型顶点以及点与点之间的线就够了。
1.1 定义点
在三维坐标系中的每个点都对应维一的一组x,y,z坐标量。因此,我们可以建立一个变量,比如叫points,并使用下面的编码方式来存放各点的坐标值:
“”
其中代表第n个点的坐标,每个坐标值以三位数字来表示,即,12编码为012;1编码为001;点(33,1,0)编码为(033,001,000)。
然后,为了让每组坐标看起来清晰点,将每个点的坐标之间以一个字符R(其实随便什么都可以)来分隔。此外,还要定义一个变量,比如说叫totalpoints,来确定总的点数。
1.2 定义线
很早以前老师就告诉我们两点确定一条直线这个道理。根据这个老道理,我们只要确定两个端点的坐标值便能确定一条线段。所以,我们可以生成一个变量,比如叫lines,并使用跟上面定义点相类似的编码方式来存放各条线的定义:
“”
其中代表第n条线段的两个端点坐标,每个端点以两位数字来表示。例如,从顶点1到顶点5的线段就被编码为0105。这里你可能会发现,这个简易3D引擎只能最多定义99个点。如果你需要添加点数的话其实也很简单,只要增加用以表示每个端点的数字的位数即可。
同样的,你得建立一个用来确定总线段数的变量,比如叫totallines。
1.3 其它的变量
定义完了点和线,我们还得告诉引擎眼睛的位置(比如说eyez)以及视平面所处的深度(zorder)。视点(eyez)离物体越远,物体的透视效果就会越差。
1.4 构建三棱锥
用于构建模型的变量差不多都介绍完了,下面就在modal info帧中输入以下这段定义一个三棱锥模型的代码:
totalpoints=4;//四个顶点
points="010,010,000R090,000,000R090,090,090R000,090,000";//四个顶点的坐标
totallines=6;//六条边
lines="0102R0103R0104R0203R0204R0304";//六条边的定义
eyez=-500;//视点的位置
zorder=100;//视平面深度
二、 构建引擎
这一步是整个程序的核心,完成了引擎差不多就等于完成了全部。
2.1 初始化
在开始真正的引擎前我们先来做一些初始化的工作——将所定义模型的信息转存到一个个单独的变量中。在init帧中输入以下代码:
setProperty ("/baseline", _x, 1000);//隐藏原来的那条线
//下面的循环转存所有点的信息
for (i=1; i<=totalpoints; i++) {
set ("x" add i, substring(points, Number((i-1)*12+1), 3));
set ("y" add i, substring(points, Number((i-1)*12+5), 3));
set ("z" add i, substring(points, Number((i-1)*12+9), 3));
}
//下面的循环转存所有线的信息
for (i=1; i<=totallines;i++) {
set ("pt1" add i, substring(lines, Number((i-1)*5+1), 2));
set ("pt2" add i, substring(lines, Number((i-1)*5+3), 2));
}
//下面两个变量分别表示显示中心点的x和y
centerx=190;
centery=215;
2.2 创建基本循环
完成了初始化,下面我们来建立程序中最基本的循环。选中loop帧,并在其中输入:
gotoAndPlay ("start");
2.3 绘制模型
绘制模型的过程可分为两个部分,第一部分将三维物体的各点坐标值转换为投影到视平面S上的相应二维坐标;第二部分根据转换完毕的二维坐标在视平面S上绘制出图像。在这个例子里我们在帧draw里面进行三维坐标的二维化,然后使用帧drawline来进行绘制工作。所以,在帧draw里输入以下的代码:
//下面的循环根据公式进行三维坐标的二维化
for (i=1; i<=totalpoints; i++) {
u = (zorder-eyez)/(eval("z" add i)-eyez);//二维化公式
if (i<=9) {
set ("2Dx0" add i, u*eval("x" add i)+centerx);
set ("2Dy0" add i, u*eval("y" add i)+centery);
} else {
set ("2Dx" add i, u*eval("x" add i)+centerx);
set ("2Dy" add i, u*eval("y" add i)+centery);
}
}
//下面的循环负责复制线段并将之显示出来
for(i=1;i<=totallines;i++){
duplicateMovieClip("/baseline","line" add i,i);
call("drawline");
}
在帧drawline里输入以下的代码:
pt1 = eval("pt1" add i);
pt2 = eval("pt2" add i);
setProperty ("line" add i, _x, eval("2Dx" add pt1));
setProperty ("line" add i, _y, eval("2Dy" add pt1));
setProperty ("line" add i, _xscale, eval("2Dx" add pt2) - eval("2Dx" add pt1));
setProperty ("line" add i, _yscale, eval("2Dy" add pt2) - eval("2Dy" add pt1));
至此绘制模型的工作大致就做完了,在start帧中输入:
call("draw");
按Ctrl+Enter后就可以看到你所定义的模型的模样了。
2.4 旋转
旋转其实也很简单,只要将坐标根据不同旋转角度重新映射一次就可以了,下面是一组用于旋转计算的公式:
沿x轴旋转:
沿y轴旋转:
沿z轴旋转:
由于篇幅有限,这里就只介绍一种旋转方式——沿x轴旋