Camera Space - Demo 10¶
Purpose¶
Graphical programs in which the viewer never “moves” are boring. Model a virtual “camera”, and let the user move the camera around in the scene. In this picture, notice that the purple paddle is no longer on the left side of the screen, but towards the right, horizontally.
How to Execute¶
On Linux or on MacOS, in a shell, type “python src/demo10/demo.py”. On Windows, in a command prompt, type “python src\demo10\demo.py”.
Move the Paddles using the Keyboard¶
Keyboard Input |
Action |
---|---|
w |
Move Left Paddle Up |
s |
Move Left Paddle Down |
k |
Move Right Paddle Down |
i |
Move Right Paddle Up |
d |
Increase Left Paddle’s Rotation |
a |
Decrease Left Paddle’s Rotation |
l |
Increase Right Paddle’s Rotation |
j |
Decrease Right Paddle’s Rotation |
UP |
Move the camera up, moving the objects down |
DOWN |
Move the camera down, moving the objects up |
LEFT |
Move the camera left, moving the objects right |
RIGHT |
Move the camera right, moving the objects left |
Description¶
“Camera space” means the coordinates of everything relative to a camera, not relative to world space.
In the picture above, NDC is the square in the center of the screen, and its coordinate system is defined by its two axes X and Y. The square to the to the left and up is the position of the camera; which has its own X axis and Y axis.
The coordinates of the paddle can be described in world space, or in camera space, and that we can create functions to convert coordinates between the two spaces.
Towards that, in the Cayley graph, camera space will be between world space and NDC, but which way should the direction of the edge be?
It could be
From world space to camera space.
From camera space to world space.
Since the camera’s position will be described relative to world space in world space coordinates, just like the paddles are, it makes more sense to use the latter, in which the directed edge goes from camera space to world space.
Think about it this way. Imagine you a driving. Are you staying in a constant position, and the wheels of your car rotate the entire earth and solar system around yourself? That’s one way to look at it, and the earth revolves around me too, but the mathematical descriptions of everything else that’s not you breaks down.
The math is easier in the aggregate if you describe your position as moving relative to earth.
But this introduces a new problem. Follow from the paddle’s modelspace to screen space. Up until this demo, we’ve always been following the the direction of each edges. In our new Cayley graph, when going from world space to camera space, we’re moving in the opposite direction of the edge.
Going against the direction of an edge in a Cayley Graph means that we don’t apply the function itself; instead we apply the inverse of the function. This concept comes from Group Theory in Abstract Algebra, the details of which won’t be discussed here, but we will use it to help us reason about the transformations.
Inverses¶
Inverse Of Translate¶
The inverse of
>>> v.translate(x,y)
is
>>> v.translate(-x,-y)
Inverse Of Rotate¶
The inverse of
>>> v.rotate(theta)
is
>>> v.rotate(-theta)
Inverse Of Scale¶
The inverse of
>>> v.scale(x,y)
is
>>> v.scale(1.0/x,1.0/y)
Inverse Of Sequence Of Functions¶
The inverse of a sequence of functions is the inverse of each function, applied in reverse order.
For the linear-algebra inclined reader,
The inverse of
>>> v.scale(x,y).translate(Vertex(x,y))
is
>>> v.translate(-Vertex(x,y).scale(1.0/x,1.0/y)
Think of the inverses this way. Stand up. Make sure that nothing is close around you that you could trip on or that you would walk into. Look forward. Keeping your eyes straight ahead, sidestep a few steps to the left. From your perspective, the objects in your room all moved to the right. Now, rotate your head to your right, keeping your eyes looking directly in front of your head. Which way did the room move? Towards the left.
Looking at the graph paper on the ground there, that’s world space. The camera gets placed in world space just like any other modelspace data. In order to render from the camera’s point of view, we need to move and orient the -1.0 to 1.0 box relative to the camera to match the -1.0 to 1.0 box in world space.
Run “python mvpVisualization/modelviewperspectiveprojection/modelviewperspectiveprojection.py”, and follow along with the Cayley graph. The camera will be placed like any other modelspace data, but then the inverse of the transformations will be applied to all of the vertices.
For the attentive reader¶
The purpose of watching that animation was to see how the camera was placed just like the modelspace data was placed, while not modifying the modelspace data, but then the inverse was applied, moving the modelspace data.
The attentive reader may notice: “Bill, you said the the inverse causes the operations to be run in opposite order, yet I saw them run in the same order. For camera placement, I saw translate, then rotate sideways, then rotate up. For the inverse, I saw inverse translate, then inverse rotate sideways, then inverse rotate up. What gives?”
Well, the answer, which we talk about later in more detail, is that we change the order in which we read the sequence of transformations.
Let’s say we read the following from left to right
So we imagine the a transformation, then the b transformation, then the c.
To take the inverse, we reverse the order of the transformations, and invert them.
But now we read the transformations from right to left.
So we imagine the a inverse transformation, then the b inverse transformation, then the c inverse.
The author understands that this may be confusing to the reader, and seems illogical. Camera placement is, in the author’s opinion, the most difficult concept for a novice to grasp, and the author is unaware of a better explanation. Bear with the author, by the end of this book, it should make more sense to the reader.
Code¶
177
178
179@dataclass
180class Camera:
181 position_worldspace: Vertex = field(default_factory=lambda: Vertex(x=0.0, y=0.0))
189def handle_inputs() -> None:
190 global camera
191
192 if glfw.get_key(window, glfw.KEY_UP) == glfw.PRESS:
193 camera.position_worldspace.y += 1.0
194 if glfw.get_key(window, glfw.KEY_DOWN) == glfw.PRESS:
195 camera.position_worldspace.y -= 1.0
196 if glfw.get_key(window, glfw.KEY_LEFT) == glfw.PRESS:
197 camera.position_worldspace.x -= 1.0
198 if glfw.get_key(window, glfw.KEY_RIGHT) == glfw.PRESS:
199 camera.position_worldspace.x += 1.0
...
The Event Loop¶
229while not glfw.window_should_close(window):
247 glColor3f(paddle1.r, paddle1.g, paddle1.b)
248
249 glBegin(GL_QUADS)
250 for paddle1_vertex_in_model_space in paddle1.vertices:
251 paddle1_vertex_in_world_space: Vertex = paddle1_vertex_in_model_space.rotate(paddle1.rotation) \
252 .translate(translate_amount=paddle1.position)
The camera’s position is at camera.x, camera.y. So apply the inverse of the camera’s transformations to paddle 1’s vertices, i.e. translate with the negative values.
255 paddle1_vertex_in_camera_space: Vertex = paddle1_vertex_in_world_space.translate(translate_amount=-camera.position_worldspace)
258 paddle1_vertex_in_ndc_space: Vertex = paddle1_vertex_in_camera_space.uniform_scale(scalar=1.0/10.0)
259 glVertex2f(paddle1_vertex_in_ndc_space.x, paddle1_vertex_in_ndc_space.y)
260 glEnd()
266 glColor3f(paddle2.r, paddle2.g, paddle2.b)
267
268 glBegin(GL_QUADS)
269 for paddle2_vertex_model_space in paddle2.vertices:
270 paddle2_vertex_world_space: Vertex = paddle2_vertex_model_space.rotate(paddle2.rotation) \
271 .translate(translate_amount=paddle2.position)
The camera’s position is at camera.x, camera.y. So apply the inverse of the camera’s transformations to paddle 2’s vertices, i.e. translate with the negative values.
274 paddle2_vertex_camera_space: Vertex = paddle2_vertex_world_space.translate(translate_amount=-camera.position_worldspace)
279 glVertex2f(paddle2_vertex_ndc_space.x, paddle2_vertex_ndc_space.y)
280 glEnd()