Inverting rotation in 3D, to make an object always face the camera?

14,541

Solution 1

The easiest way to do this is "clearing" the rotational part of the transform matrix. Your typical homogenous transformation looks like this

| xx xy xz xw |
| yx yy yz yw |
| zx zy zz zw |
| wx wy wz ww | 

with wx = wy = wz = 0, ww = 1. If you take a closer look you'll see that in fact this matrix is composed of a 3x3 submatrix defining the rotation, a 3 subvector for the translation and a homogenous row 0 0 0 1

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

For a billboard/sprite you want to keep the translation, but get rid of the rotation, i.e. R = I. In case some scaleing was applied the identity needs to be scaled as well.

This gives the following recipie:

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

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

Loading this matrix allows you to draw camera aligned sprites.

Solution 2

Apologies in advance for this being a suggestion, not a solution:

If your intent is simulate 3D spherical rotations, your easiest route would be to forego the 2.5D API and just use simple scaling and sin/cos calculations for the positioning.

A fun place to start: http://www.reflektions.com/miniml/template_permalink.asp?id=329

Solution 3

I've solved it using Wikipedia, matrices and black magic. I choose to implement custom rotation instead of inverting the rotation of all objects. Heres the code if anyones interested:

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);
        }
    })
}
}
}

and the matrix3 class:

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;
    }
}
}

Solution 4

enter image description here

As you can see in the picture above, the camera on the right is set to face the quad (on the left), so that their normals are exactly opposite. When the camera is rotated, for the quad to still have an opposite normal, the quad would have to be rotated in the opposite direction the same angle as the camera. So, I was able to produce the right effect simply by rotating the quad by -camera.Yawaround the y-axis.

Share:
14,541
sydd
Author by

sydd

Updated on June 24, 2022

Comments

  • sydd
    sydd almost 2 years

    i have lots of sprites arranged in 3D space, and their parent container has rotations applied. How do i reverse the sprites 3D rotation, that they always face the camera (Actionscript 3)?

    heres a code to test it:

    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
            }
        })
    }
    }
    }
    

    I am guessing i have to do some magic with the rotation3D matrices of the sprites... I've tried to implement this script: http://ughzoid.wordpress.com/2011/02/03/papervision3d-sprite3d/ , but had so success
    Thanks for help.

  • sydd
    sydd about 13 years
    I think its easier to use the 3D api that came with Flashplayer 10 (.z property of displayobjects) - this allows easy positioning in 3D space. If i dont find a solution in 1-2 days, ill simulate the rotation with sin/cos calculations, or use zedbox
  • NickHubben
    NickHubben about 13 years
    Absolutely. The two methods have different 'looks' as the displayObjects are converted to bitmaps when using 'z' or any of the axis-specific rotations. Check out Matrix3D.invert() method.
  • Bill Kotsias
    Bill Kotsias over 11 years
    OK, the question is : how do you get the "global" matrix for any particular object, manipulate it as you describe, then set it back to this sprite??? AFAIK, you can't set a "global" transformation matrix to an object, only a "local" one! Could the "pointAt" Matrix3D method solve this problem?
  • datenwolf
    datenwolf over 11 years
    @BillKotsias: It's your responsibility to track the transformation matrices of the objects in your scene. You usually have some sort of transformation hierachy: view · t_1 · t_2 · t_3 …. This is your "global" transformation matrix. Now if you want to draw some object in this transformation chain, but make it face the camera, you take that compound matrix and change it the way I described. Effectively this just translates the object at the right spot in the view without turning it away from the "camera".
  • user18490
    user18490 over 9 years
    I don't understand this at all. It would only scale the object and move it some distance away from the camera, but it wouldn't "orient" the plane so it faces the cameras (it's oriented towards the camera as in the normal of the plane is parallel to the a line from the camera origin to the centre of the plane).
  • Summit
    Summit almost 2 years
    @datenwolf If the camera moves from its position( Position , pan , tilt) will this work ?
  • datenwolf
    datenwolf almost 2 years
    @Summit – yes it will. Because there is no camera! At the end it comes all down to rotating, scaling and translating stuff in screen space. All that matrix multiplication "magic" does is figuring out, how to bring things into screen space. If you "clear out" the rotating part, things will just be upright and in the plane of screen space.