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
149@dataclass
150class Vertex:
...
215 def perspective(self: Vertex,
216 fov: float,
217 aspectRatio: float,
218 nearZ: float,
219 farZ: float) -> Vertex:
220 # fov, field of view, is angle of y
221 # aspectRatio is xwidth / ywidth
222
223 top: float = -nearZ * math.tan(math.radians(fov) / 2.0)
224 right: float = top * aspectRatio
225
226 scaled_x: float = self.x / self.z * nearZ
227 scaled_y: float = self.y / self.z * nearZ
228 retangular_prism: Vertex = Vertex(scaled_x,
229 scaled_y,
230 self.z)
231
232 return retangular_prism.ortho(left=-right,
233 right=right,
234 bottom=-top,
235 top=top,
236 near=nearZ,
237 far=farZ)
238
239 def camera_space_to_ndc_space_fn(self: Vertex) -> Vertex:
240 return self.perspective(fov=45.0,
241 aspectRatio=1.0,
242 nearZ=-.1,
243 farZ=-1000.0)
368while not glfw.window_should_close(window):
...
400 # draw paddle 1
401 glColor3f(paddle1.r, paddle1.g, paddle1.b)
402
403 glBegin(GL_QUADS)
404 for paddle1_vertex_in_model_space in paddle1.vertices:
405 paddle1_vertex_in_world_space: Vertex = paddle1_vertex_in_model_space.rotate_z(paddle1.rotation) \
406 .translate(paddle1.position)
407 # paddle1_vertex_in_world_space: Vertex = paddle1_vertex_in_camera_space.rotate_x(camera.rot_x) \
408 # .rotate_y(camera.rot_y) \
409 # .translate(camera.position_worldspace)
410 paddle1_vertex_in_camera_space: Vertex = paddle1_vertex_in_world_space.translate(-camera.position_worldspace) \
411 .rotate_y(-camera.rot_y) \
412 .rotate_x(-camera.rot_x)
413 paddle1_vertex_in_ndc_space: Vertex = paddle1_vertex_in_camera_space.camera_space_to_ndc_space_fn()
414 glVertex3f(paddle1_vertex_in_ndc_space.x, paddle1_vertex_in_ndc_space.y, paddle1_vertex_in_ndc_space.z)
415 glEnd()