I'm trying to figure out how to setup a 3D camera in WebGL. This is my first swing at 3D so a lot of the matrix stuff is new to me.


The code can be reviewed in this CodePen: 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. Just make any type of update to the code and it will work.


  • Full control of camera movement
  • A rendered model should be able to visibly move in all three dimensions
  • As with orthographic 3D, moving an object to a different position should not affect its shape. Every instance of the same object should look identical regardless of position.


Setting up a 3D camera

In the scene, Z (blue) is the vertical axis (up = [0, 0, -1]), while X (red) and Y (green) represents the horizontal plane. The reason for this is that my scene is actually a map with a real world coordinate system. In this coordinate system, Z represents elevation, X represents East-West and and Y represents North-South. Z increases with elevation, X increases to the East and Y increases to the North.

Also, the scene is viewed from an angle. The viewing point is located somewhere to the South-East of origo. Because of this, the Y axis is pointing to the upper right instead of straight up. The X axis is pointing to the lower right instead of straight to the right.

You may notice that the camera position and up direction is inverted. I believe this is necessary for the axes to point in the right directions according to my particular requirements. Please let me know if this is not the case.


  1. How would you typically move the camera? My guess would be that you work with matrix calculations rather than adjusting cameraPosition and cameraTarget directly.
  2. My first idea was to position the camera straight above the target (cameraTarget = [0, 0, 0]; cameraPosition = [0, 0, distance]; up = [0, 0, -1];) and then add transformations to reposition the camera in relation to that position. However, if I set the camera position to [0, 0, distance] the model disappears. Why is this?
  3. How would you zoom a scene like this? By scaling the scene, or moving the camera? Well, since the scene is orthographic (in the sense that an object maintains its shape regardless of position) moving the camera won't do anything. I guess this means I need to scale the scene?
  4. Let's say you want to change the viewing angle to make the top edge of the hexagon appear completely flat. This can be done by adding a 15 degree Z rotation to the view matrix (viewMatrix = m4.zRotate(viewMatrix, 15 * Math.PI/180)). Add rotation

Is this a valid approach (is the math correct)? I think the hexagon looks a bit blurry at the top, as if not completely straight. Maybe this has some other explanation.

  1. Is there some serious downside to assigning the upward direction to the Z axis? Like, issues relating to mathematics or compability with external resources? I believe the conventional way would be to use the Y axis.
  2. Is it a bad idea to invert the Z axis (up = [0, 0, -1])? Could it cause issues with depth calculations? I haven't noticed any issues so far.

Please let me know if there is some other issue I should be aware of.

Code breakdown

  1. Create an orthographic projection matrix, to convert between screen space and clip space
    let projectionMatrix = m4.orthographic(0, canvas.width, canvas.height, 0, -2000, 2000);

The orthographic function (the rest of the m4 functions can be found in the CodePen):

orthographic: function(left, right, bottom, top, near, far) {
    return [
      2 / (right - left), 0, 0, 0,
      0, 2 / (top - bottom), 0, 0,
      0, 0, 2 / (far - near), 0,
      (left + right) / (left - right),
      (bottom + top) / (bottom - top),
      (far + near) / (far - near),
  1. Create a transformation matrix for the model
    let modelMatrix = m4.identityMatrix();
    modelMatrix = m4.translate(modelMatrix, drawInfo.position[0], drawInfo.position[1], drawInfo.position[2])
  1. Create a camera matrix from a lookAt function
    let cameraTarget = [0, 0, 0]; // origo
    let cameraPosition = [-1, 1, -1];
    let up = [0, 0, -1];
    let cameraMatrix = m4.lookAt(cameraPosition, cameraTarget, up);
  1. Create view matrix by inverting the camera matrix
    let viewMatrix = m4.inverse(cameraMatrix);
  1. Create the view projection matrix by multiplying the projection matrix and the projection matrix
    let viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);
  1. Add in the model transformation to create the final matrix that is sent to the vertex shader
    const finalMatrix = m4.multiply(viewProjectionMatrix, modelMatrix);
  • 1
    $\begingroup$ This question is very broad its better to ask multiple different questions. Folks are more willing to answer smaller questions and you get more thorough/complete answers. For example each of the 5 questions could be a single question by themselves. $\endgroup$
    – pmw1234
    Jun 11 at 12:59
  • $\begingroup$ Thanks for the feedback @pmw1234. I'll probably follow your advice and divide it into multiple questions (not directly, but within the next 24 hours or so). $\endgroup$
    – Candleout
    Jun 11 at 17:21
  • $\begingroup$ As pmw1234 already mentioned, we usually prefer a single question per topic here. I understand why it is tempting to "get all your questions answered at once", but it is more likely that people stop reading when they see a lot of text and code. Some might only be able to answer one of your questions and shy away from writing an answer at all because they can't answer the other ones. Additionally, the purpose of this site is to conserve knowledge for others with similar questions. For them, it is hard to find the answers they seek if the relevant question is hidden deep inside a topic. $\endgroup$
    – wychmaster
    Jun 19 at 7:26
  • $\begingroup$ You bring up good points @wychmaster. Been busy this last week but will get back to this ASAP. $\endgroup$
    – Candleout
    Jun 20 at 11:22


Your Answer

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

Browse other questions tagged or ask your own question.