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.
Code
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.
Goal
- 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.
Explanation
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.
Questions
- How would you typically move the camera? My guess would be that you work with matrix calculations rather than adjusting
cameraPosition
andcameraTarget
directly. - 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? - 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?
- 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)
).
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.
- 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.
- 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
- 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])
- 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);
- Create view matrix by inverting the camera matrix
let viewMatrix = m4.inverse(cameraMatrix);
- Create the view projection matrix by multiplying the projection matrix and the projection matrix
let viewProjectionMatrix = m4.multiply(projectionMatrix, viewMatrix);
- Add in the model transformation to create the final matrix that is sent to the vertex shader
const finalMatrix = m4.multiply(viewProjectionMatrix, modelMatrix);