1
$\begingroup$

I'm trying to figure out how to move a camera in orthographic 3D. The scene is orthographic in the sense that an object looks the same regardless of position. The only thing that may affect the size or shape of an object is zooming the scene or changing the viewing angle.

The scene is rendered in WebGL: https://codepen.io/Candleout/pen/poxMLPB For some reason, the scene is sometimes empty (white) when you load the CodePen. If this happens, you can fix it by simply running the code again.

As you can see, I'm already at the point where I can move the object in all three dimensions (X, Y and Z) without altering its shape. What I need now is a good and practical way to zoom and change the viewing angle.

The camera is positioned with the help of a lookAt function. But something tells me you wouldn't want to use the lookAt function as the primary tool for moving the camera?

My guess is that you work with matrix calculations? Maybe you set the camera in a neutral position (something like cameraTarget = [0, 0, 0]; cameraPosition = [0, 0, distance]; up = [0, 0, -1];) and then add transformations to that position?

How would you typically zoom? By scaling the scene, or moving the camera? With my current code, moving the camera doesn't affect the size of an object.

axes

$\endgroup$
1
  • $\begingroup$ Moving the camera cannot produce zoooming. $\endgroup$
    – user1703
    Jun 29, 2023 at 14:20

1 Answer 1

2
$\begingroup$

It seems that some basic computer graphics knowledge is missing.

Normally you have two matrices for the camera (viewMatrix and projectionMatrix), both are 4x4 matrices. The viewMatrix stores the camera position and orientation relative to world coordinates. The projection matrix stores the projection.... in your case it is an orthographic projection. The projection matrix does NOT contain any information about the camera position or orientation.

Then you have a modelMatrix for each geometry. This matrix stores the position, scale, and orientation of a 3D object, relative to world coordinates.

So when rendering, you want to transform each vertex of a geometry in the following way:

First, you need to transform the vertex, which is stored in model coordinates, to world coordinates: $vec4 vertexWorldCoordinates = modelMatrix * vec4(vertex,1)$

**Now you have each vertex of an object in world coordinates.

The next step is to convert them to camera coordinates. Camera coordinates have nothing to do with projection! They are the coordinates related to the camera position and orientation. $vertexCameraSpace = viewMatrix * vertexWorldCoordinates$

Now you have translated your vertices into camera coordinates. The next step is to translate them into screen coordinates by applying the projection matrix. $vertexProjectedCoordinates = projectionMatrix * vertexCameraSpace$.

The final step is to bring the vertex projection coordinates to normalized device coordinates. In the case of a perspective projection (everything far away becomes smaller) you have to do the following: $vertexNormalizedDeviceCoordinates = vertexProjectedCoordinates / vertexProjectedCoordinates.w$. Here $vertexProjectedCoordinates.w$ is the 4th value of the vector.

In the case of orthographic projection, there is no need to divide by the value $vertexProjectedCoordinates.w$, since this value is always 1.

Now let's talk about how to apply these matrices without storing each transformation in a separate vector: Matrices can be applied directly one after the other. So you can write: $vertexProjectedCoordinates = projectionMatrix * viewMatrix * modelMatrix * vec4(vertex, 1)$.

You can combine matrices, which results in a matrix. So let's combine the projection matrix and the view matrix: $viewProjectionMatrix = projectionMatrix * viewMatrix$. This leads to better performance when computing this matrix on the CPU instead of the GPU. This is because: each vertex would have to calculate the multiplication of both matrices, and each vertex gets the same value from it. So, calculate the combined matrices and load them on the GPU.

On the GPU you then only need: $vertexProjectedCoordinates = viewProjectionMatrix * modelMatrix * vec4(vertex, 1)$. You can also combine the modelMatrix and the viewProjectionMatrix in the same way...

Okay, now let's take a look at your questions:

I'm trying to figure out how to move a camera in orthographic 3D.

Since the projection matrix is separate from the view matrix, camera movement works the same for orthographic cameras as it does for perspective cameras. You have to change the viewMatrix, then the position / orientation of the camera changes.

As you can see, I'm already at the point where I can move the object in all three dimensions (X, Y and Z) without changing its shape.

Here I would think about moving not the object, but the camera.... But I don't know what the final result will do... So maybe moving the object is the right choise.

What I need now is a good and practical way to zoom and change the angle of view.

zoom: what does zoom mean? As I understand it, you want to scale the camera projection to see more/less of the scenario. To do this, you need to change the projection matrix.

Changing the angle of view: I hope you don't mean the field of view, because orthographic projections don't have a field of view. Do you mean the camera orientation (rotating the camera)? Then you just need to multiply your viewMatrix by a rotation matrix. Note that $viewMatrix * rotationMatrix$ is not the same as $rotationMatrix * viewMatrix$, because matrix multiplications are not commutative!

The camera is positioned using a lookAt function. But something tells me that you don't want to use the lookAt function as the primary tool for moving the camera?

The lookAt function is a nice function to initialize the camera position / orientation. You can perform transformations (translations, rotations) by multiplying matrices by the viewMatrix. Note that after some time (if you rotate a lot) the floating point precision introduces small errors into the viewMatrix. Then you need to orthonormalize the top 3x3 part of your viewMatrix. You can do this by simply creating a new viewMatrix using the function lookAt.

How do you normally zoom? By scaling the scene or by moving the camera? With my current code, moving the camera does not affect the size of an object.

Moving the camera will not work! As I said before, you need to change the projectionMatrix. If you use the library "GLM" you can use the function "ortho".

BTW: please take a look at the following tutorial. This tutorial discribes the different matrices, what they represent and how to change them: OpenGL tutorial matrices

$\endgroup$
3
  • $\begingroup$ Thank you @Thomas ! 1. From your answer, I gather that camera movement (panning, rotation, etc) is handled by adding transformations to the view matrix. 2. "Here I would think about moving not the object, but the camera.... But I don't know what the final result will do" The camera will actually be in a fixed position for the most part. Either straight above or at an angle. So it's mostly objects moving around. 3. "As I understand it, you want to scale the camera projection to see more/less of the scenario." Correct. I will follow your advice and work this into the projection matrix. $\endgroup$
    – Candleout
    Jun 23, 2023 at 15:21
  • $\begingroup$ @Candleout are you using GLM for linear algebra? There the 'ortho' function can be used to scale the projectionMatrix. Good luck with your game/application :) $\endgroup$
    – Thomas
    Jun 23, 2023 at 18:13
  • $\begingroup$ Thank you @Thomas ! I use my own functions, inspired by the ones at webgl2fundamentals.org. The ortographic function can be found at the bottom of the codepen. $\endgroup$
    – Candleout
    Jun 24, 2023 at 10:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.