在3D中翻转旋转,使物体始终面向相机?

6

我有很多在3D空间中排列的精灵,它们的父容器已经被旋转。

如何逆转精灵的3D旋转,使它们始终面向相机(Actionscript 3)?

以下是测试代码:

package{
import flash.display.Sprite;
import flash.events.Event;
public class test extends Sprite{

var canvas:Sprite = new Sprite();
var sprites:Array = []

public function test(){
    addChild(canvas)
    for (var i:int=0;i<20;i++){
        var sp:Sprite = new Sprite();
        canvas.addChild(sp);
        sp.graphics.beginFill(0xFF0000);
        sp.graphics.drawCircle(0,0,4);
        sp.x = Math.random()*400-200;
        sp.y = Math.random()*400-200;
        sp.z = Math.random()*400-200;
        sprites.push(sp);
    }
    addEventListener(Event.ENTER_FRAME,function():void{
        canvas.rotationX++;
        canvas.rotationY = canvas.rotationY+Math.random()*2;
        canvas.rotationZ++;
        for (var i:int=0;i<20;i++){
            //this is not working...
            sprites[i].rotationX = -canvas.rotationX
            sprites[i].rotationY = -canvas.rotationY
            sprites[i].rotationZ = -canvas.rotationZ
        }
    })
}
}
}

我猜想我需要对精灵的rotation3D矩阵进行一些魔法操作... 我尝试实现了这个脚本:http://ughzoid.wordpress.com/2011/02/03/papervision3d-sprite3d/,但没有成功。 谢谢帮助。

4个回答

27
最简单的方法是“清除”变换矩阵的旋转部分。您典型的齐次变换如下所示:
| xx xy xz xw |
| yx yy yz yw |
| zx zy zz zw |
| wx wy wz ww | 

当wx = wy = wz = 0,ww = 1时。如果你仔细观察,你会发现这个矩阵实际上由一个定义旋转的3x3子矩阵,一个用于平移的3子向量和一个齐次行0 0 0 1组成。

| R       T |
| (0,0,0) 1 |

对于广告牌/精灵,你想保留翻译,但去掉旋转,即 R = I。若应用了某些缩放,则恒等式也需要缩放。

这给出以下配方:

d = sqrt(xx² + yx² + zx²)

| d 0 0 T.x |
| 0 d 0 T.y |
| 0 0 d T.z |
| 0 0 0   1 |

加载此矩阵可让您绘制与相机对齐的精灵。


好的,问题是:您如何获取任何特定对象的“全局”矩阵,按照您所描述的方式进行操作,然后将其设置回此精灵???据我所知,您无法为对象设置“全局”转换矩阵,只能设置“本地”矩阵!“pointAt” Matrix3D方法是否可以解决这个问题? - Bill Kotsias
@BillKotsias:跟踪场景中对象的转换矩阵是您的责任。通常您会有某种转换层次结构:view · t_1 · t_2 · t_3 …。这是您的“全局”转换矩阵。现在,如果您想在此转换链中绘制某个对象,但使其面向相机,则需要采取该复合矩阵并按我所描述的方式进行更改。实际上,这只是将对象在视图中正确位置上平移而不使其远离“相机”。 - datenwolf
1
我完全不理解这个。它只会缩放对象并将其移动到相机远离的一些距离,但它不会“定位”平面使其正对相机(它是朝向相机的,就像平面的法线与从相机原点到平面中心的直线平行一样)。 - user18490
@datenwolf 如果相机从其位置(位置、平移、倾斜)移动,这个会起作用吗? - Summit
1
@Summit - 是的,它会。因为没有相机!最终,所有的事情都归结于在屏幕空间中旋转、缩放和平移物体。所有矩阵乘法的“魔力”所做的就是找出如何将物体带入屏幕空间。如果你“清除”旋转部分,物体就只会直立并处于屏幕空间的平面上。 - datenwolf

2

我使用维基百科、矩阵和黑魔法解决了这个问题。我选择实现自定义旋转而不是反转所有对象的旋转。 如果有人感兴趣,这里是代码:

package{
import flash.display.Sprite;
import flash.events.Event;

public class test extends Sprite{

private var canvas:Sprite = new Sprite();
private var sprites:Array = []
private var rotx:Number=0,roty:Number=0,rotz:Number=0;
private var mm:Matrix3 = new Matrix3();
public function test(){
    addChild(canvas);
    canvas.x = canvas.y = 230
    for (var i:int=0;i<30;i++){
        var sp:Sprite = new Sprite();
        canvas.addChild(sp);
        sp.graphics.beginFill(0xFF0000);
        sp.graphics.drawCircle(0,0,2);
        sp.x = Math.random()*200-100;
        sp.y = Math.random()*200-100;
        sp.z = Math.random()*200-100;
        sprites.push(sp);
        rotx=0.06; //from top to bottom
        //roty=0.1; //from right to left
        rotz=0.1; //clockwise
        mm.make3DTransformMatrix(rotx,roty,rotz);
    }
    addEventListener(Event.ENTER_FRAME,function():void{
        for (var i:int=0;i<sprites.length;i++){
            var s:Sprite = sprites[i];
            mm.rotateByAngles(s);
        }
    })
}
}
}

以及matrix3类:

public class Matrix3{

    private var da:Vector.<Number>; // rows

    public function make3DTransformMatrix(rotx:Number,roty:Number,rotz:Number):void{
        var cosx:Number = Math.cos(rotx);
        var cosy:Number = Math.cos(roty);
        var cosz:Number = Math.cos(rotz);
        var sinx:Number = Math.sin(rotx);
        var siny:Number = Math.sin(roty);
        var sinz:Number = Math.sin(rotz);
        da =    new <Number>[
            cosy*cosz, -cosx*sinz+sinx*siny*cosz,  sinx*sinz+cosx*siny*cosz,
            cosy*sinz, cosx*cosz+sinx*siny*sinz , -sinx*cosz+cosx*siny*sinz,
            -siny    ,         sinx*cosy        ,        cosx*cosy         ];
    }

    public function rotateByAngles(d:DisplayObject):void{
        var dx:Number,dy:Number,dz:Number;
        dx = da[0]*d.x+da[1]*d.y+da[2]*d.z;
        dy = da[3]*d.x+da[4]*d.y+da[5]*d.z;
        dz = da[6]*d.x+da[7]*d.y+da[8]*d.z;
        d.x = dx;
        d.y = dy;
        d.z = dz;
    }
}
}

1

我认为使用Flashplayer 10附带的3D API(displayobjects的.z属性)更容易-这允许在三维空间中轻松定位。如果我在1-2天内找不到解决方案,我将使用sin/cos计算模拟旋转,或者使用zedbox。 - sydd
当使用“z”或任何轴特定旋转时,这两种方法的外观不同,因为displayObjects在转换为位图时会发生变化。请查看Matrix3D.invert()方法。 - NickHubben

1

enter image description here

正如您在上图中所看到的,右侧的相机设置为面向四边形(左侧),使它们的法线完全相反。当相机旋转时,为了使四边形仍然具有相反的法线,需要将四边形绕相同角度的相反方向旋转。因此,我只需围绕y轴旋转四边形-camera.Yaw即可产生正确的效果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接