0
votes

I would like to implement a simple example in OpenGL just to test the first perspective view (without using lookAt facility), but it is driving me crazy!

I made a little prototype using Processing (that for this stuff is quite similar to OpenGL), but I got strange behavior when camera start to move!

It's not clear to me which it is the order of transformation (I have already tried all combination ;-))!

I was thinking that it should be simple and a possible solution could be:

  • Translate to the position of the camera
  • Rotate by angle of the camera
  • Translate each object in its position

In my simple example I positioned a box and a grid in the (0, 0, -400), but it doesn't work as expected. When I move camera along the X or Z axis the rotation around Y axis seems to rotate around a wrong center! I'd like to simulate a rotation of the camera around its own Y axis, just like a classic FPS game.

Here my sample code where user can move the camera and rotate (just around Y axis), you can test with Processing or using OpenProcessing. Only the first few lines are relevant to the problem... So it's a very little test!

float cameraXPos = 0;
float cameraYPos = 0;
float cameraZPos = 0;
float cameraYAngle = 0;

float moveIncrement = 5;
float angleIncrement = 5;

// Keys:
//     W
//     ^
//     |
// A <- -> D 
//     |
//     V
//     S 
//
// F and R for Z+/Z-
// O and P for rotation around Y axis

void setup()
{
  size(640, 480, OPENGL);

  resetCameraPos();
}

// Reset camera
void resetCameraPos()
{
  cameraXPos = width / 2;
  cameraYPos = height / 2;
  cameraZPos = (height /2 ) / tan(PI/6);
  cameraYAngle = 0;
}


void draw() 
{
  // Clear screen
  background(0);
  // View transform
  translate(cameraXPos, cameraYPos, cameraZPos);
  rotateY(radians(cameraYAngle));
  // World transform
  translate(0, 0, -400);
  // Draw a red box and a grid in the center
  stroke(255, 0, 0);
  noFill();
  box(100);
  drawGrid();

  // Check if user is pressing some key and update the camera position
  updateCameraPos();
}

/////////////////////////////////////////////////////////////////////
// The following part is not so relevant to the problem (I hope! ;-)) 
/////////////////////////////////////////////////////////////////////
void drawGrid()
{
  // Draw a white grid (not so important thing here!)
  stroke(255, 255, 255);
  float cellSize = 40;
  int gridSize = 10;
  float cY = 100;

  for(int z = 0; z < gridSize; z++)
  {
    float cZ = (gridSize / 2 - z) * cellSize;
    for(int x = 0; x < gridSize; x++)
    {
      float cX = (x - gridSize / 2) * cellSize;
      beginShape();
      vertex(cX, cY, cZ);
      vertex(cX + cellSize, cY, cZ);
      vertex(cX + cellSize, cY, cZ - cellSize);
      vertex(cX, cY, cZ - cellSize);
      vertex(cX, cY, cZ);
      endShape();
    }
  }
}

// Just update camera position and angle rotation
// according to the pressed key on the keyboard
void updateCameraPos()
{
  if (keyPressed)
  {
    switch(this.key)
    {
    case 'w':  // Y++
      cameraYPos += moveIncrement;
      break;
    case 's':  // Y--
      cameraYPos -= moveIncrement;
      break;
    case 'a':  // X--
      cameraXPos += moveIncrement;
      break;
    case 'd':  // X++
      cameraXPos -= moveIncrement;
      break;
    case 'r':  // Z++
      cameraZPos += moveIncrement;
      break;
    case 'f':  // Z--
      cameraZPos -= moveIncrement;
      break;
    case ' ':  // RESET
      resetCameraPos();
      break;
    case 'o':  // Angle++
      cameraYAngle += angleIncrement;
      break;
    case 'p':  // Angle--
      cameraYAngle -= angleIncrement;
      break;
    }
  }
}
2
Can you describe what the problem is more explicitly? Also, what happens if you do use lookAt? Does it work then?Xymostech
@Xymostech it seems that when I move the camera (so I change the cameraXPos or cameraZPos) on on the X or Z axis the rotation around Y axis is not made around the axis of the camera but on another center. I don't know how to use lookAt, and I was thinking that for this simple test it wouldn't be necessary... Isn't it?Kill KRT

2 Answers

0
votes

Your comment above seems to indicate that you want your cameraYAngle to rotate the camera about its current position. If so, you want to do your camera rotation first; try this:

// View transform
rotateY(radians(cameraYAngle));
translate(cameraXPos, cameraYPos, cameraZPos);
// World transform
translate(0, 0, -400);

Note that the above does nothing to keep you looking at the origin -- that is, it doesn't do anything like lookAt. Also recall that your camera starts out looking down the Z axis...


Here's where I guess you went wrong. Even though your camera and object poses are comparable, you need to generate your "View transform" and "World transform" differently: your camera pose must be inverted to generate your "View transform".

If you're chaining raw transforms (as in your example code), this means that your camera pose transforms need to be in reverse order relative to your object transforms, as well as in the reverse direction (that is, you must negate both translation vectors and rotation angles for the camera transforms).

To be more explicit, suppose both your camera and your object have comparable pose parameters: a position vector and 3 rotations about X, Y, and Z in that order. Then, your transformation sequence might be something like:

// View transform
rotateZ(radians(-cameraZAngle));
rotateY(radians(-cameraYAngle));
rotateX(radians(-cameraXAngle));
translate(-cameraXPos, -cameraYPos, -cameraZPos);
// Model transform
translate(objectXPos, objectYPos, objectZPos);
rotateX(radians(objectXAngle));
rotateY(radians(objectYAngle));
rotateZ(radians(objectZAngle));

As a conceptual check, suppose that your camera is exactly following your object, so that all position and location variables are exactly the same for camera and object. You'd expect the same view as if they were both sitting at the origin with no rotations.

Then note that, if cameraXPos==objectXPos, cameraYPos==objectYPos and so forth, the transforms in the above transform sequence all cancel out in pairs, starting at the center -- so that the end result is the same view as if they were both sitting at the origin with no rotations.

0
votes

I've tried to port the same example on C and OpenGL, and... It works!

To be precise it works after I inverted the sequence of rotation and translation (as suggested by @comingstorm and as I formerly already tried).

So I come back to Processing and try to figure out why this problem happens. I had already noticed that the default camera position is in:

(width/2, height/2, (height/2) / (PI/6))

So in my code I was moving the camera off by the same distance (in opposite direction) in order to center the camera to my objects, but it didn't work. I also try to leave the camera where it was and then move manually (using the keyboard keys) to reach my objects, but it didn't work either.

So I noticed that in the draw() method there isn't any initialization of the transformation matrix. In all examples I've seen none does this initialization, and I'm pretty sure to have read somewhere that is automatically initialized. So I was thinking/sure that it wasn't necessary. Anyway I've tried to put as first statement of draw() method:

resetMatrix(); // Same as glLoadIdentity in OpenGL

...And now everything is working!

(For the record I noticed the same problem also in openFramework library.)

To be honest I don't understand why they (these libraries) don't put camera in the origin and above all I was expecting that if the transformation matrix is not clear at each execution of draw method the camera should be translated automatically (summing the old matrix with the new one), so it would move fast in some direction (or spinning around Y axis).

It's not clear to me the pipeline implemented in these libraries (and I can't find a good document where it is shown), but by now it is important that this problem has been fixed.