3D Perspective - Demo 17¶
Purpose¶
Implement a perspective projection so that objects further away are smaller than the would be if they were close by
How to Execute¶
On Linux or on MacOS, in a shell, type “python src/demo17/demo.py”. On Windows, in a command prompt, type “python src\demo17\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 |
q |
Rotate the square around its center |
e |
Rotate the square around paddle 1’s center |
Description¶
------------------------------- far z
\ | /
\ | /
\ (x,z) *----|(0,z) /
\ | | /
\ | | /
\ | | /
\ | | /
\ | | /
\ | | /
\---*-------- near z
| | /
|\ | /
| \ | /
| \ | /
| \|/
-----------------*----*-(0,0)-------------------
(x,0)
If we draw a straight line between (x,z) and (0,0), we will have a right triangle with vertices (0,0), (0,z), and (x,z).
There also will be a similar right triangle with vertices (0,0), (0,nearZ), and whatever point the line above intersects the line at z=nearZ. Let’s call that point (projX, nearZ)
because right angle and tan(theta) = tan(theta)
x / z = projX / nearZ
projX = x / z * nearZ
So use projX as the transformed x value, and keep the distance z.
----------- far z
| |
| |
(x / z * nearZ,z) * | non-linear -- the transformation of x depends on its z value
| |
| |
| |
| |
| |
| |
| |
| |
------------ near z
\ | /
\ | /
\ | /
\ | /
\|/
----------------------*-(0,0)-------------------
Top calculation based off of vertical field of view
/* top
/ |
/ |
/ |
/ |
/ |
/ |
/ |
/ |
/ |
/ |
/ |
/ |
origin/ |
/ fov/2 |
z----*---------------*
|\ |-nearZ
| \ |
| \ |
x \ |
\ |
\ |
\ |
\ |
\ |
\ |
\ |
\ |
\ |
\ |
\|
Right calculation based off of Top and aspect ration
top
-------------------------------------------------------
| |
| y |
| | |
| | |
| *----x | right =
| origin | top * aspectRatio
| |
| | aspect ratio should be the viewport's
| | width/height, not necessarily the
------------------------------------------------------- window's
152@dataclass
153class Vertex:
...
218 def perspective(self: Vertex,
219 field_of_view: float,
220 aspect_ratio: float,
221 near_z: float,
222 far_z: float) -> Vertex:
223 # field_of_view, field of view, is angle of y
224 # aspect_ratio is x_width / y_width
225
226 top: float = -near_z * math.tan(math.radians(field_of_view) / 2.0)
227 right: float = top * aspect_ratio
228
229 scaled_x: float = self.x / self.z * near_z
230 scaled_y: float = self.y / self.z * near_z
231 rectangular_prism: Vertex = Vertex(scaled_x,
232 scaled_y,
233 self.z)
234
235 return rectangular_prism.ortho(left=-right,
236 right=right,
237 bottom=-top,
238 top=top,
239 near=near_z,
240 far=far_z)
241
242 def camera_space_to_ndc_space_fn(self: Vertex) -> Vertex:
243 return self.perspective(field_of_view=45.0,
244 aspect_ratio=1.0,
245 near_z=-.1,
246 far_z=-1000.0)
375while not glfw.window_should_close(window):
...
417 # draw paddle 1
418 glColor3f(paddle1.r, paddle1.g, paddle1.b)
419
420 glBegin(GL_QUADS)
421 for paddle1_vertex_in_model_space in paddle1.vertices:
422 paddle1_vertex_in_world_space: Vertex = paddle1_vertex_in_model_space.rotate_z(paddle1.rotation) \
423 .translate(paddle1.position)
424 # paddle1_vertex_in_world_space: Vertex = paddle1_vertex_in_camera_space.rotate_x(camera.rot_x) \
425 # .rotate_y(camera.rot_y) \
426 # .translate(camera.position_worldspace)
427 paddle1_vertex_in_camera_space: Vertex = paddle1_vertex_in_world_space.translate(-camera.position_worldspace) \
428 .rotate_y(-camera.rot_y) \
429 .rotate_x(-camera.rot_x)
430 paddle1_vertex_in_ndc_space: Vertex = paddle1_vertex_in_camera_space.camera_space_to_ndc_space_fn()
431 glVertex3f(paddle1_vertex_in_ndc_space.x, paddle1_vertex_in_ndc_space.y, paddle1_vertex_in_ndc_space.z)
432 glEnd()