[转] 玩转Flash AS 3D之贴图篇

玩转Flash AS 3D之贴图篇 (1)

作者:tiger_0309   类型:原创   来源:闪吧

  在之前我发过一篇名为玩转flash as 3d的贴子,那时候之所以用“玩转”的字眼,是因为我当时觉得我对flash 3d掌握的很精通了,可是在后来的学习中却发现自己只不过是懂点皮毛罢了,或者可以说连皮毛也还不够,在玩转flash as 3d中我主要介绍了从3d坐标到2d坐标的转换,并且结合flash的画图方法做的一些旋转的例子,关于这方面的例子网上也不少,只不过没有更系统的把它组织和整理出来,而我做的可能就是这方面的工作罢了。
   flash是个二维软件,所以它所做的都是假3d,这一点大家必须要认识到,但是从后来的发展,越来越多的人尝试着用它来做一些3d的东西来,假的也越来越来真起来,时下最有名的俩个flash 3d引擎,sandy和papervision3d就是很好的例子,尤其是后者更为突出,并且已经有了as3。
   如果说到3d,而不涉及到贴图和光源的话就不能说是3d,在次我将对flash 3d贴图做一个比较“基础”和“系统”的讲解,之所以上引号是到时候有人觉得我讲解的不够系统或者基础,我好有台阶可下,以下我讲的理论并不是我的原创,大部分来自网上。
  好了言归正转,在阅读此文章之前我强烈推荐先看看我的第一篇文章,因为我对一些坐标转化之类的不在介绍了。 http://space.flash8.net/bbs/viewthread.php?tid=323970&highlight=
   说到如何贴图,其实就是如何动态地skew一个影片剪辑,skew的汉语意思是歪斜.扭曲和变形,我无法用一个词语来代替它,所以就用了一个英文,先看下面的一个简单例子,做一个嵌套的影片剪辑,然后改变父影片剪辑的_yscale,在旋转子影片剪辑就可以。效果:(skew1.swf)

源文件:   skew1.rar

再看下一个例子,做一个嵌套的影片剪辑,父级叫parent,子级叫son,把子级旋转45度,再做几个按钮,在按钮中加入代码来控制父影片剪辑的_yscale和子影片剪辑的_yscale和_xscale,其中的图片大小我们先用100*100,就是正方形。效果:(skew2.swf)
下面的图就是有关skew的数学解释,不过说实话我虽然会应用它,但是对它的数学解释不是太懂,不过我觉得没有必要把它弄的多透彻,只要知道它是怎么来的就。可以了有兴趣的可以自己研究一下,研究出来别忘了告诉我一声。

图片附件: 001.gif



那么我就直接介绍这个函数了,如图:
 function skewObj (obj, mcW, mcH, pt0, ptH, ptW) {
    function distance (pt1, pt2) {
        var dy = pt2.y-pt1.y;
        var dx = pt2.x-pt1.x;
        var side = Math.sqrt(dy*dy+dx*dx);
        return side;
    }
    obj._x = pt0.x;//obj是父级影片剪辑
    obj._y = pt0.y;
    obj._yscale = 100;
    var angleP2 = Math.atan2(ptW.y-pt0.y, ptW.x-pt0.x);
    var angleP1 = Math.atan2(ptH.y-pt0.y, ptH.x-pt0.x);
    var dAngle = (angleP1-angleP2)/2;
    var arm = Math.sqrt(2)/2/Math.cos(dAngle);
        // original a 100x100 model , now use 1x1 model
    obj._rotation = (180/Math.PI)*(angleP1-dAngle);
    obj.mc._rotation = -45;//mc是子影片剪辑
    obj._yscale = 100*Math.tan(dAngle);
    obj.mc._xscale = distance(ptW, pt0)*100/arm/mcW;
    obj.mc._yscale = distance(ptH, pt0)*100/arm/mcH;
其中pt0, ptH, ptW就是上图的三点,为什么只要三点,因为学过数学的都知道,三点可以确定一个平面,先做一个小小的应用,如下面的例子(skew3.swf):

图片附件: 002.gif


源文件:   skew3.rar

有了以上的基础我们来做第一个3d贴图,代码如下:
Point = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
};
Transform3DPointsTo2DPoints = function (pts, angles) {
var tps = new Array();
var sx = Math.sin(angles.x);
var cx = Math.cos(angles.x);
var sy = Math.sin(angles.y);
var cy = Math.cos(angles.y);
var sz = Math.sin(angles.z);
var cz = Math.cos(angles.z);
var p, xy, xz, yx, yz, zx, zy;
var i = pts.length;
while (i--) {
  p = pts;
  xy = cx*p.y-sx*p.z;
  xz = sx*p.y+cx*p.z;
  yz = cy*xz-sy*p.x;
  yx = sy*xz+cy*p.x;
  zx = cz*yx-sz*xy;
  zy = sz*yx+cz*xy;
  x = 275+zx;
  y = 200-zy;
  z = yz;
  tps = new Point(x, y, z);
}
return tps;
};//坐标转化我就不说了,我在上一篇贴子里已经讲过了
var points3D = [
new Point(-70, -52.5, 70),
new Point(-70, 52.5, 70),
new Point(70, 52.5, 70), 
new Point(70, -52.5, 70), 
new Point(-70, -52.5, -70), 
new Point(-70, 52.5, -70), 
new Point(70, 52.5, -70), 
new Point(70, -52.5, -70)
];//其实根据三点确定一个平面,不需要这么多点,为了配合图片的大小,我自己算出的3d坐标,你也
可以自己算
var points2D = new Array();
var rotation = new Point(0, 0, 0);
function skewObj(obj, mcW, mcH, pt0, ptH, ptW) {
function distance(pt1, pt2) {
  var dy = pt2.y-pt1.y;
  var dx = pt2.x-pt1.x;
  var side = Math.sqrt(dy*dy+dx*dx);
  return side;
}
var z = (ptH.z+ptW.z)/2;
obj.swapDepths(Math.round(10000+z*100));
obj._x = pt0.x;
obj._y = pt0.y;
obj._yscale = 100;
var angleP2 = Math.atan2(ptW.y-pt0.y, ptW.x-pt0.x);
var angleP1 = Math.atan2(ptH.y-pt0.y, ptH.x-pt0.x);
var dAngle = (angleP1-angleP2)/2;
var arm = Math.sqrt(2)/2/Math.cos(dAngle);
// original a 100x100 model , now use 1x1 model
obj._rotation = (180/Math.PI)*(angleP1-dAngle);
obj.mc._rotation = -45;
obj._yscale = 100*Math.tan(dAngle);
obj.mc._xscale = distance(ptW, pt0)*100/arm/mcW;
obj.mc._yscale = distance(ptH, pt0)*100/arm/mcH;
}
for (var i = 1; i<=4; i++) {
_root.attachMovie("obj"+i, "obj"+i, i);
H1 = obj1.mc._height;
W1 = obj1.mc._width;
H2 = obj2.mc._height;
W2 = obj2.mc._width;
H3 = obj3.mc._height;
W3 = obj3.mc._width;
H4 = obj4.mc._height;
W4 = obj4.mc._width;
}
onEnterFrame = function () {
rotation.x -= _ymouse/5000;
rotation.y += _xmouse/5000;
points2D = Transform3DPointsTo2DPoints(points3D, rotation);
skewObj(obj1, W1, H1, points2D[1], points2D[0], points2D[2]);
skewObj(obj2, W2, H2, points2D[2], points2D[3], points2D[6]);
skewObj(obj3, W3, H3, points2D[6], points2D[7], points2D[5]);
skewObj(obj4, W4, H4, points2D[5], points2D[4], points2D[1]);
};
(3d贴图1.swf):

这是单面贴图,我们可以再在上面加上一个以前用过的函数就可以实现双面贴图,就是在上面skewObj(obj, mcW, mcH, pt0, ptH, ptW)函数中加入以下代码就行了。
if (isVisibleBetween(pt0, ptH, ptW)){
  if (obj.mc._currentframe != 2) obj.mc.gotoAndStop(2); // frame 1 for front
  
}else{
  if (obj.mc._currentframe != 1) obj.mc.gotoAndStop(1); // frame 2 for back
  
  
}
isVisibleBetween(pt0, ptH, ptW)这个函数我以前讲过,是根据平面任意三点的坐标可以计算出该平面是否可见,这个函数如下:
isVisibleBetween = function(a,b,c){
if (((b.y-a.y)/(b.x-a.x)-(c.y-a.y)/(c.x-a.x)<0)^(a.x<=b.x == a.x>c.x)){
  return true;
}else{
  return false;
}
};
最后你还要在每个影片剪辑加上一帧,换上你想要的背面图片就可以了,如下:(3d贴图2.swf)

 
所有的立体图形贴图都是由一小块的正方形或者三角形组成,比如圆柱体,我们可以把它看成许多块矩形组成的,我下面的例子就是一个简单的圆柱体,不过我只是用了十个矩形组成的,要让它更逼真可以把它分割成更多的矩形,这里我还要提醒一点,分的越多,你的cpu负担就越重,有人说这是flash做3d的缺点,认为其它专门的3d软件就不会有这中现象,其实我认为不然,3d软件在渲染的时候占用cpu更大,只不过导出图象序列后就不占用cpu,可是那是静态图象,而flash只所以占用cpu大,是因为它把这渲染和输出合二为一了。
代码:
Point = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
};
Transform3DPointsTo2DPoints = function (pts, angles) {
var tps = new Array();
var sx = Math.sin(angles.x);
var cx = Math.cos(angles.x);
var sy = Math.sin(angles.y);
var cy = Math.cos(angles.y);
var sz = Math.sin(angles.z);
var cz = Math.cos(angles.z);
var p, xy, xz, yx, yz, zx, zy;
var i = pts.length;
while (i--) {
  p = pts;
  xy = cx*p.y-sx*p.z;
  xz = sx*p.y+cx*p.z;
  yz = cy*xz-sy*p.x;
  yx = sy*xz+cy*p.x;
  zx = cz*yx-sz*xy;
  zy = sz*yx+cz*xy;
  x=275+zx;
  y=200-zy;
  z=yz;
  tps = new Point(x, y, z);
}
return tps;
};
var points3D = [
new Point(100*Math.cos(2*Math.PI/10*0), 100, 100*Math.sin(2*Math.PI/10*0)), 
new Point(100*Math.cos(2*Math.PI/10*1), 100, 100*Math.sin(2*Math.PI/10*1)),
new Point(100*Math.cos(2*Math.PI/10*2), 100, 100*Math.sin(2*Math.PI/10*2)),
new Point(100*Math.cos(2*Math.PI/10*3), 100, 100*Math.sin(2*Math.PI/10*3)),
new Point(100*Math.cos(2*Math.PI/10*4), 100, 100*Math.sin(2*Math.PI/10*4)), 
new Point(100*Math.cos(2*Math.PI/10*5), 100, 100*Math.sin(2*Math.PI/10*5)),
new Point(100*Math.cos(2*Math.PI/10*6), 100, 100*Math.sin(2*Math.PI/10*6)),
new Point(100*Math.cos(2*Math.PI/10*7), 100, 100*Math.sin(2*Math.PI/10*7)),
new Point(100*Math.cos(2*Math.PI/10*8), 100, 100*Math.sin(2*Math.PI/10*8)),
new Point(100*Math.cos(2*Math.PI/10*9), 100, 100*Math.sin(2*Math.PI/10*9)),
new Point(100*Math.cos(2*Math.PI/10*0), -100, 100*Math.sin(2*Math.PI/10*0)), 
new Point(100*Math.cos(2*Math.PI/10*1), -100, 100*Math.sin(2*Math.PI/10*1)),
new Point(100*Math.cos(2*Math.PI/10*2), -100, 100*Math.sin(2*Math.PI/10*2)),
new Point(100*Math.cos(2*Math.PI/10*3), -100, 100*Math.sin(2*Math.PI/10*3)),
new Point(100*Math.cos(2*Math.PI/10*4), -100, 100*Math.sin(2*Math.PI/10*4)), 
new Point(100*Math.cos(2*Math.PI/10*5), -100, 100*Math.sin(2*Math.PI/10*5)),
new Point(100*Math.cos(2*Math.PI/10*6), -100, 100*Math.sin(2*Math.PI/10*6)),
new Point(100*Math.cos(2*Math.PI/10*7), -100, 100*Math.sin(2*Math.PI/10*7)),
new Point(100*Math.cos(2*Math.PI/10*8), -100, 100*Math.sin(2*Math.PI/10*8)),
new Point(100*Math.cos(2*Math.PI/10*9), -100, 100*Math.sin(2*Math.PI/10*9))
];

var points2D = new Array();
var rotation = new Point(0, 0, 0);
function skewObj(obj, mcW, mcH, pt0, ptH, ptW) {
function distance(pt1, pt2) {
  var dy = pt2.y-pt1.y;
  var dx = pt2.x-pt1.x;
  var side = Math.sqrt(dy*dy+dx*dx);
  return side;
}
var z=(ptH.z+ptW.z+pt0.z)/3;
obj.swapDepths(Math.round(10000+z*100));
obj._x = pt0.x;
obj._y = pt0.y;
obj._yscale = 100;
var angleP2 = Math.atan2(ptW.y-pt0.y, ptW.x-pt0.x);
var angleP1 = Math.atan2(ptH.y-pt0.y, ptH.x-pt0.x);
var dAngle = (angleP1-angleP2)/2;
var arm = Math.sqrt(2)/2/Math.cos(dAngle);
// original a 100x100 model , now use 1x1 model
obj._rotation = (180/Math.PI)*(angleP1-dAngle);
obj.mc._rotation = -45;
obj._yscale = 100*Math.tan(dAngle);
obj.mc._xscale = distance(ptW, pt0)*100/arm/mcW;
obj.mc._yscale = distance(ptH, pt0)*100/arm/mcH;
}
for(var i=0;i<10;i++){

_root.attachMovie("obj"+(i+1),"obj"+(i+1),i+1);

  H1 = obj1.mc._height;
W1 = obj1.mc._width;
H2 = obj2.mc._height;
W2 = obj2.mc._width;
H3 = obj3.mc._height;
W3 = obj3.mc._width;
H4 = obj4.mc._height;
W4 = obj4.mc._width;
H5 = obj5.mc._height;
W5 = obj5.mc._width;
H6 = obj6.mc._height;
W6 = obj6.mc._width;
H7 = obj7.mc._height;
W7 = obj7.mc._width;
H8 = obj8.mc._height;
W8 = obj8.mc._width;
H9 = obj9.mc._height;
W9 = obj9.mc._width;
H10 = obj10.mc._height;
W10 = obj10.mc._width;

}
onEnterFrame = function () {
rotation.x -= _ymouse/5000;
rotation.y += _xmouse/5000;
points2D = Transform3DPointsTo2DPoints(points3D, rotation);
skewObj(obj1, W1, H1, points2D[0], points2D[10], points2D[1]);
skewObj(obj2, W2, H2, points2D[1], points2D[11], points2D[2]);
skewObj(obj3, W3, H3, points2D[2], points2D[12], points2D[3]);
skewObj(obj4, W4, H4, points2D[3], points2D[13], points2D[4]);
skewObj(obj5, W5, H5, points2D[4], points2D[14], points2D[5]);
skewObj(obj6, W6, H6, points2D[5], points2D[15], points2D[6]);
skewObj(obj7, W7, H7, points2D[6], points2D[16], points2D[7]);
skewObj(obj8, W8, H8, points2D[7], points2D[17], points2D[8]);
skewObj(obj9, W9, H9, points2D[8], points2D[18], points2D[9]);
skewObj(obj10, W10, H10, points2D[9], points2D[19], points2D[0]);

};
(3d贴图3.swf):


光有矩形贴图还不够,有时候还需要三角形,三角形的原理也来自矩形,如下图,我们把矩形也看成正方形,通过给出的三角形pH的坐标可以算出ptH的坐标,
function getPH(pt0,pH,ptW){
    var pt=new Object();
    pt.x=pH.x-(ptW.x-pt0.x)/2;
    pt.y=pH.y-(ptW.y-pt0.y)/2
  pt.z=pH.z-(ptW.z-pt0.z)/2;
    return pt;
}
//这个函数不用解释也能看懂吧
那么上面的函数就变成这样了:
skewObj(clip, 100, 100, rp0, getPH(rp0,rpH,rpW), rpW);
其中100我是按照正方形来计算的,如果你不是用的正方形,那么应该是这样了
skewObj(clip, mcW, mcH, rp0, getPH(rp0,rpH,rpW), rpW);
mcW和mcH分别是矩形的长和宽
这是四面体的三维坐标:
pointsArray = [
new Point(-50, 29, 29),
new Point(0, 29, -58),
new Point(50, 29, 29),
new Point(0, -58, 0)];
具体的原代码我就不给了,大家只需把坐标和上面的函数换一下就好了,如果实在做不出,把我的swf反编译也行,所有的文件都没有加密。(3d贴图4.swf):


图片附件: 003. gif


然后我们再看看上面的哪个圆柱体,给它加个盖和底,说到这里我有要提一下闪客帝国有名的哪个3d易拉罐,当时看的时候怎么也看不懂,并且原文件加密了,现在再想想其实也不难,它只不过把它分割成一个个的三角形而已。(3d贴图5.swf):

写到这里理论上我们可以给任意的立体图形贴图了,只要你有耐心,你的配置足够高(主要是cpu).
回顾上面的例子,不知道你注意到没有,所有的贴图还有个致命的缺陷,就是做出来的三维图形不是真正意义上的透视图形,为什么呢?那是因为在flash8之前还不能操作位图,所有的坐标都没有进行透视处理,你可以试一下坐标透视处理后会出现下面状况,把上面坐标转化函数改成这样:
     scaleFactor = focalLength/(focalLength+yz);
        x = zx*scaleFactor;
        y = zy*scaleFactor;
        z = yz;
不过庆幸的是在flash8中我们可以做到真正的透视3d了,最后的一个例子我就是用flash8做的,你可以和上面的比较一下,看有什么不同,在这个原文件我应用了ditortimage类,老外写的类,很不错。(3d贴图6.swf):

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值