camera transformation matrix

Hi, I have object representing camera, It has it’s own transformation matrix. The question is how to use that matrix to position the camera in OpenGL.
I tried gl.glLoadMatrix(cameratransformationmatrix) but I haven’t got desired result

please help…

That is the correct way, at least that is how I do it. A couple of things that you might want to check:

  • OpenGL is column major, ie the indices of the matrix written out by column.
    [ 0 4 8 12] [ 1 5 9 13] [ 2 6 10 14] [ 3 7 11 15]
    If you did row major then change it to match the OpenGL ordering.

  • You are setting up the matrix incorrectly. An easy way to check this is to do some transforms using the gl functions, glRotate, glTranslate, and glScale and then ask the OpenGl context for the current matrix (glGetDoublev(GL.GL_MODELVIEW_MATRIX, matrix) and see if doing the same sequence of transforms match up with your transformation matrix.

I like to think of my scene as having a separate model and view matrix. To apply the view or camera pose, simply make your first transformation the inverse of the camera pose.

gl.glMultMatrixf(camera.getInverseMatrix());

If it doesn’t make sense to you why you multiply by the inverse camera object transformation, think about the following situation:

You start out at the origin of your world. You have a virtual camera that has position x, y, z and orientation h, p, r. We can compose this camera pose into matrix Mc. In order to move the camera to the origin, we simply multiply Mc by its inverse.

Mc * Mc^-1 = Identiy

Conversely, you can think of this as moving the world origin to the camera. If you are looking out the camera lens, you can’t tell what moved, so it’s all the same.

One more nice thing about thinking in these terms is that you can easily attach a camera to any object in your scene that has its own pose. All you have to do is start your frame rendering by multiplying by the inverse of the object pose to see the world from that object’s perspective.

Hope that helps.

  1. how do you calculate inverse matrix?
    2 In what form do you keep your matrix data ?
    3, to draw object after we aplied inverse transformation of the camera, we aply transformation matrix of object or again inverse transfomation matrix of the object?

[quote]1) how do you calculate inverse matrix?
[/quote]
If the rotation matrix M is M = R * T, then the inverse is Minv = Tinv * Rinv. The important thing to note here is that we multiply the inverted translation matrix by the inverse rotation matrix.

From a practical standpoint, the inverse of the homogeneous transformtion matrix is very simple. Just transpose the 3x3 rotation block and multiply the negated translations by the new rotation matrix.

If you use quaternions for rotation storage, inverting rotations is even easier. Just negate the w component or negate the x, y, z vector component.

[quote]2 In what form do you keep your matrix data ?
[/quote]
Quaternions. I store the rotations as a quaternion and the position as a point. I can easily compose a homogeneous matrix from this structure. Quaternions are nice because they don’t suffer from gimbal lock. You can also optimize rotations about the principal axes to significantly reduce the required arithmetic operations.

[quote]3, to draw object after we aplied inverse transformation of the camera, we aply transformation matrix of object or again inverse transfomation matrix of the object?
[/quote]
Not sure what you are asking. If you want to draw the object that the camera is tracking (a car, for example), just apply the object’s regular transformation when you are drawing the model portion of the modelview operation. The cumulative result will be viewMatrix*objectmatrix = Identity since your view matrix is the inverse of the object matrix in this case. In the car case, if your car origin is behind the wheel, this will result in a driver’s seat perspective.

I have my class representing objects, they have rotation matrix, translation matrix and transformation matrix data stored as instances of Transform3D.class I found on the net. I think it has sam logic as java3d’s Transform3D but it is quite simpler.

So you said that I need to calculate inverse transformation by multiplying inverse rotation with inverse translation. That’s OK, but I dont know hot to get inverse matrix of rotation and translation.

Do you use your own algoritam or you have some math library for that?

Roll your own.

Have you looked at javax.vecmath, wich is part of Java3D?

There is javax.vecmath.Matrix4f.invert() wich inverts a matrix.

[quote]Have you looked at javax.vecmath, wich is part of Java3D?

There is javax.vecmath.Matrix4f.invert() wich inverts a matrix.
[/quote]
thanx, thats what I found yesterday on google, I hope that will solve my problems…

That can get real messy, right??

I will add my code. Feel free to modify and use :slight_smile:

It uses the Jama matrix package.

Camera is represented by 4 vectors: x,y,z,p.

x,y,z give orientation of camera inside world. All unit length and perpendicular. determinant(x,y,z) == 1.

p : position inside the world.
z : always points away from your eyes; (you look that way)
y : points out of your head.
x : well, that’s the only one left!

orientzy() is used to set z, and y. Just point z at the thing you must look at. Set p separately.

Code follows.

This has been tested and works.

Remember, a “black” screen could mean you got your coloring parameters wrong. That has bit me. Don’t let it bite you!



class Camera {
    public _3dpointd x,y,z,p; // orientation & position
    float shutter_angle;
    
    public Camera() {
      x=new _3dpointd(1,0,0);
      y=new _3dpointd(0,1,0);
      z=new _3dpointd(0,0,1);
      p=new _3dpointd(0,0,0);
      shutter_angle = 45;
    }

    Camera(Camera c) {
      x = new _3dpointd(c.x);
      y = new _3dpointd(c.y);
      z = new _3dpointd(c.z);
      p = new _3dpointd(c.p);
      shutter_angle = c.shutter_angle;
    }

    void print() {
      System.out.print("camera position: ");
      System.out.print(shutter_angle + " ");
      x.print();
      System.out.print("  ");
      y.print();
      System.out.print("  ");
      z.print();
      System.out.print("  ");
      p.print();
      System.out.println("");
    }
    
    void start_viewing(GL gl, GLU glu, Viewport v) {
      if(false) {
          gl.glViewport( 0, 0, v.w, v.h);
          gl.glMatrixMode( GL.GL_PROJECTION );  
          gl.glLoadIdentity(); 
          glu.gluPerspective(45.0f, (float)v.w / (float)v.h, 10f, 10000.0f);
          gl.glTranslatef(0,0,-400);
          gl.glMatrixMode(GL.GL_MODELVIEW);
          return;
      }
      
      gl.glViewport( 0, 0, v.w, v.h );
      gl.glMatrixMode(GL.GL_PROJECTION);
      gl.glLoadIdentity(); 
      glu.gluPerspective(shutter_angle, (float)v.w / (float)v.h, 0.1f, 1000.0f);
      GLUtil.print_errors("pers", gl, glu);
      gl.glRotatef(180, 0, 1, 0);
      double [] orientation = {x.x,x.y,x.z,0,
                         y.x,y.y,y.z,0,
                         z.x,z.y,z.z,0,
                         0,0,0,1};
      Matrix o = new Matrix(orientation, 4);
      Matrix oi = o.inverse();
      double [] dd = new double[16];
      int k = 0;
      for(int j=0; j < 4; ++j) //notice j is first: we want the columns
          for(int i=0; i < 4; ++i) {
            dd[k++] = oi.getArray()[i][j];
            //System.out.println(k+" "+i+" "+j+" "+dd[k-1]);
          }
      if(k != 16)
          throw new RuntimeException("bad camera");
      gl.glMultMatrixd(dd);
      //System.out.println(p.x+" "+p.y+" "+p.z);
      gl.glTranslatef(-(float)p.x,-(float)p.y,-(float)p.z);
      gl.glMatrixMode( GL.GL_MODELVIEW );  
      GLUtil.print_errors("start_viewing",gl,glu);
    }

    float distance(float x, float y, float z) {
      double 
          x2 = (x-p.x), 
          y2 = (y-p.y), 
          z2 = (z-p.z);
      x2 *= x2;
      y2 *= y2;
      z2 *= z2;
      return (float)Math.sqrt(x2 + y2 + z2);
    }

    void rotatex(float angle) {
      rotate(y,z,(double)angle);
    }
    void rotate_absolute_z(float degrees) {
      double angle = Math.toRadians(degrees);
      _3dpointd [] p = {x,y,z};
      double [] d = {Math.cos(angle), Math.sin(angle), 0,
                   - Math.sin(angle), Math.cos(angle), 0,
                   0, 0, 1};
      Matrix rot = new Matrix(d, 3);
      for(int i=0;i<3;++i) {
          double [] parray = {p[i].x, p[i].y, p[i].z};
          Matrix pm = new Matrix(parray, 3);
          Matrix prot = rot.times(pm);
          double [] protvalues = prot.getColumnPackedCopy();
          p[i].set(new _3dpointd(protvalues[0], protvalues[1], protvalues[2]));
      }
    }
    
    void rotatey(float angle) {
      rotate(z,x,(double)angle);
    }
    void rotatez(float angle) {
      rotate(x,y,(double)angle);
    }
    void rotate(_3dpointd s, _3dpointd t, double angle) {
      double a = Math.toRadians(angle);
      _3dpointd ss = s.mul(Math.cos(a)).add(t.mul(Math.sin(a)));
      _3dpointd tt = t.mul(Math.cos(a)).add(s.mul(-Math.sin(a)));
      s.set(ss);
      t.set(tt);
    }
    void orientzy(_3dpointf zimage, _3dpointf yimage) {
      _3dpointf zz = zimage.normalized();
      _3dpointf yy = yimage.normalized();
      _3dpointf xx = yy.unoriented_cross(zz).normalized();
      z= new _3dpointd(zz.x, zz.y, zz.z);
      y= new _3dpointd(yy.x, yy.y, yy.z);
      x= new _3dpointd(xx.x, xx.y, xx.z);
    }

    void move2(_3dpointf _p) {
      p = new _3dpointd(_p);
    }
    void move_horizontal(float d) {
      _3dpointd move = x.unoriented_cross(new _3dpointd(0,0,1)).normalized();
      if(Math.abs(move.z) > 0.1) {
          System.out.println("bad z for horizontal move");
          System.exit(1);
      }
      p = p.add(move.mul(d));
    }
    void movex(float d) {
      p = p.add(x.mul(d));
    }
    void movey(float d) {
      p = p.add(y.mul(d));
    }
    void movez(float d) {
      p = p.add(z.mul(d));
    }
    void move_absolute_z(float d) {
      p = p.add(new _3dpointd(0,0,1).mul(d));
    }
    void set_shutter(float angle) {
      shutter_angle = angle;
    }
    void change_shutter(float angle) {
      shutter_angle += angle;
      if(shutter_angle > 45)
          shutter_angle = 45;
      if(shutter_angle < 1)
          shutter_angle = 1;
    }
};

class Viewport {
    int x,y,w,h, size;
    Viewport(int xx, int yy, int ww, int hh) {
      x=xx;
      y=yy;
      w=ww;
      h=hh;
      size = Math.max(w, h);
    }
}


package test;

import hr.ib.math3D.*;
import net.java.games.jogl.*;

public class Body
{
  private Matrix4d orientation, translation ,transformation;
  public Matrix4d getTransformation()
  {
    return transformation;
  }

  public Matrix4d getOrientation()
  {
    return orientation;
  }

  public Matrix4d getTranslation()
  {
    return translation;
  }

  public Body(Point3D position, Matrix4d orientation)
  {
    this.translation=new Matrix4d(1.0, 0.0, 0.0, position.x, 0.0, 1.0, 0.0, position.y, 0.0, 0.0, 1.0, position.z, 0.0, 0.0, 0.0, 1.0);
    this.orientation=new Matrix4d(orientation);
    
    transformation=new Matrix4d(orientation);
    transformation.mul(translation);
  }
  
  
  public void rotateX(double degrees)
  {
    Matrix4d rot=new Matrix4d();
    rot.rotX(Math.toRadians(degrees));
    orientation.mul(rot);
    
    transformation=new Matrix4d(orientation);
    transformation.mul(translation);
  }
  
  public void rotateY(double degrees)
  {
    Matrix4d rot=new Matrix4d();
    rot.rotY(Math.toRadians(degrees));
    orientation.mul(rot);
    
    transformation=new Matrix4d(orientation);
    transformation.mul(translation);
  }
  
  public void rotateZ(double degrees)
  {
    Matrix4d rot=new Matrix4d();
    rot.rotZ(Math.toRadians(degrees));
    orientation.mul(rot);
    
    transformation=new Matrix4d(orientation);
    transformation.mul(translation);
  }
  
  public  void moveLocalZ(double meters)
  {
    translation.m03 += orientation.m20 * meters;
    translation.m13 += orientation.m21 * meters;
    translation.m23 += orientation.m22 * meters;
    
    transformation=new Matrix4d(orientation);
    transformation.mul(translation);
  }

  public  void moveLocalY(double meters)
  {
    
    translation.m03 += orientation.m10 * meters;
    translation.m13 += orientation.m11 * meters;
    translation.m23 += orientation.m12 * meters;
   
    transformation=new Matrix4d(orientation);
    transformation.mul(translation);
  }

  public  void moveLocalX(double meters)
  {
    translation.m03 += orientation.m00 * meters;
    translation.m13 += orientation.m01 * meters;
    translation.m23 += orientation.m02 * meters;
   
    transformation=new Matrix4d(orientation);
    transformation.mul(translation);
  }
  
  
  
  public Point3D getPosition()
  {
    return new Point3D(translation.m03,translation.m13,translation.m23);  }
  
  public Matrix4d getInverseTransformation()
  {
    Matrix4d trans=new Matrix4d(translation);
    trans.invert();
    Matrix4d  or=new Matrix4d(orientation);
    or.invert();
    trans.mul(or);
    return trans;
  }


  
  public void drawMe(GL gl)
  {
    gl.glColor3d(1,1,1);
    gl.glBegin(gl.GL_QUADS);
    //Front Face
    gl.glNormal3d(0,0,1);
    gl.glVertex3d( -1, -1, 1); //Bottom Left Of The Texture and Quad
    gl.glVertex3d(1, -1, 1); //Bottom Right Of The Texture and Quad
    gl.glVertex3d(1, 1, 1); //Top Right Of The Texture and Quad
    gl.glVertex3d( -1, 1, 1); //Top Left Of The Texture and Quad
    //Back Face
    gl.glNormal3d(0,0,-1);
    gl.glVertex3d( -1, -1, -1); //Bottom Right Of The Texture and Quad
    gl.glVertex3d( -1, 1, -1); //Top Right Of The Texture and Quad
    gl.glVertex3d(1, 1, -1); //Top Left Of The Texture and Quad
    gl.glVertex3d(1, -1, -1); //Bottom Left Of The Texture and Quad
    //Top Face
    gl.glNormal3d(0,1,0);
    gl.glVertex3d( -1, 1, -1); //Top Left Of The Texture and Quad
    gl.glVertex3d( -1, 1, 1); //Bottom Left Of The Texture and Quad
    gl.glVertex3d(1, 1, 1); //Bottom Right Of The Texture and Quad
    gl.glVertex3d(1, 1, -1); //Top Right Of The Texture and Quad
    //Bottom Face
    gl.glNormal3d(0,-1,0);
    gl.glVertex3d( -1, -1, -1); //Top Right Of The Texture and Quad
    gl.glVertex3d(1, -1, -1); //Top Left Of The Texture and Quad
    gl.glVertex3d(1, -1, 1); //Bottom Left Of The Texture and Quad
    gl.glVertex3d( -1, -1, 1); //Bottom Right Of The Texture and Quad
    //Right face
    gl.glNormal3d(1,0,0);
    gl.glVertex3d(1, -1, -1); //Bottom Right Of The Texture and Quad
    gl.glVertex3d(1, 1, -1); //Top Right Of The Texture and Quad
    gl.glVertex3d(1, 1, 1); //Top Left Of The Texture and Quad
    gl.glVertex3d(1, -1, 1); //Bottom Left Of The Texture and Quad
    //Left Face
    gl.glNormal3d(-1,0,0);
    gl.glVertex3d( -1, -1, -1); //Bottom Left Of The Texture and Quad
    gl.glVertex3d( -1, -1, 1); //Bottom Right Of The Texture and Quad
    gl.glVertex3d( -1, 1, 1); //Top Right Of The Texture and Quad
    gl.glVertex3d( -1, 1, -1); //Top Left Of The Texture and Quad
    gl.glEnd();  
  }
}



class Test
{
  Body camera, cube;
 
 Test()
 {
    Matrix4d startingOrientation=new  Matrix4d(1.0,0.0,0.0,0.0,   0.0,1.0,0.0,0.0,   0.0,0.0,-1.0,0.0,    0.0,0.0,0.0,1.0);
    
    camera=new Body(new Point3D(0,0,0),startingOrientation);
    
    cube=new Body(new Point3D(0,0,-10),startingOrientation);
}


public void display(GLDrawable drawable)
    {
      gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
      
      gl.glLoadMatrixd(camera.getInverseTransformation().getCM());
      gl.glMultMatrixd(cube.getTranslation().getCM());
      gl.glMultMatrixd(cube.getOrientation().getCM());
    
      cube.drawMe(gl);
    }

}


public void keyPressed(KeyEvent ke)
    {
      if(camera!=null)
      {
        if (ke.getKeyCode() == KeyEvent.VK_RIGHT)
        {
          camera.rotateY(-1);
        }
        else if (ke.getKeyCode() == KeyEvent.VK_LEFT)
        {
          camera.rotateY(1);
        }
        else if (ke.getKeyCode() == KeyEvent.VK_UP)
        {
          camera.rotateX(1);
        }
        else if (ke.getKeyCode() == KeyEvent.VK_DOWN)
        {
          camera.rotateX(-1);
        }
        else if (ke.getKeyCode() == KeyEvent.VK_END)
        {
          camera.moveLocalZ( -1);
        }
        else if (ke.getKeyCode() == KeyEvent.VK_HOME)
        {
          camera.moveLocalZ(1);
        }
      }
      
    }


I am using javax.vecmath.Matrix4d for storing rotation and translation matrix.
Some parts of code aren’t shown here because it is not important for the problem.
The problem is that moving camera doesn’t work as it should.

It looks like you are keeping your orientation and translations separate from each other. You do not need to do this. A 4x4 transformation matrix contains a 3x3 rotation component, and a 3x1 translation part. The matrix is shown below. The Rs are the rotation matrix, and the X,Y,Z is the translation.
[ R R R X ] [ R R R Y ] [ R R R Z ] [ 0 0 0 1 ]
I think the problem you are running into is that when you rotate, you just rotate the rotation matrix, and ignore the translation. Consider the following scenario:

  1. Translate 10 down the z axis (out of the screen).
  2. Rotate 180 degrees around the Y axis

After doing that our camera should be at (0,0,10) and -z should be pointing out of the screen. Your code translates correctly, but when you rotate, you rotate the orientation and then multiply it by the old translations. Which means you rotated then translated, which is not what we wanted to do, and our camera ends up at (0,0,-10).

What you should do is keep a single unified transformation, and every time you want to rotate or translate, create a new transformation matrix and multiply the unified one with the new transformation.

Here is some totally untested code =)


public void translate(Vector3d translation)
{
    Matrix4d transform = new Matrix4d();
    transform.set(translation);

    this.transformation.mul(transform);
}

// rotates around a vector
public void rotate(double x, double y, double z, double radians)
{
    Matrix4d transform = new Matrix4d();
    transform.set(new AxisAngle4d(x,y,z,radians));

    this.transformation.mul(transform);
}

public void setGLCamera(GL gl)
{
    Matrix4d inverse = new Matrix4d(transformation);
    inverse.invert();

    gl.glLoadMatrix4d(convertToGLMatrix(inverse));
}