You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
64 lines
2.2 KiB
64 lines
2.2 KiB
import numpy as np |
|
|
|
def project_point(camera_position, look_at, point_3d): |
|
""" |
|
Project a 3D point into a normalized projection matrix. |
|
|
|
Args: |
|
camera_position: The 3D position of the camera (x, y, z). |
|
look_at: The 3D position the camera is looking at (x, y, z). |
|
point_3d: The 3D point to project (x, y, z). |
|
|
|
Returns: |
|
The 2D coordinates of the projected point in normalized space. |
|
""" |
|
# Step 1: Calculate the forward, right, and up vectors |
|
def normalize(v): |
|
return v / np.linalg.norm(v) |
|
|
|
forward = normalize(np.array(look_at) - np.array(camera_position)) |
|
right = normalize(np.cross(forward, [0, 1, 0])) |
|
up = np.cross(right, forward) |
|
|
|
# Step 2: Create the view matrix |
|
view_matrix = np.array([ |
|
[right[0], right[1], right[2], -np.dot(right, camera_position)], |
|
[up[0], up[1], up[2], -np.dot(up, camera_position)], |
|
[-forward[0], -forward[1], -forward[2], np.dot(forward, camera_position)], |
|
[0, 0, 0, 1] |
|
]) |
|
|
|
# Step 3: Create the projection matrix |
|
near = 1.0 # Near plane normalized to 1 |
|
width = 1.0 # Width of the near plane |
|
height = 1.0 # Height of the near plane |
|
aspect_ratio = width / height |
|
|
|
projection_matrix = np.array([ |
|
[1 / aspect_ratio, 0, 0, 0], |
|
[0, 1, 0, 0], |
|
[0, 0, -1, -2 * near], |
|
[0, 0, -1, 0] |
|
]) |
|
|
|
# Step 4: Transform the 3D point into clip space |
|
point_3d_homogeneous = np.array([point_3d[0], point_3d[1], point_3d[2], 1]) |
|
view_space_point = view_matrix @ point_3d_homogeneous |
|
clip_space_point = projection_matrix @ view_space_point |
|
|
|
# Step 5: Perform perspective divide to get normalized device coordinates (NDC) |
|
if clip_space_point[3] == 0: |
|
raise ValueError("Invalid projection: w component is zero.") |
|
ndc_x = clip_space_point[0] / clip_space_point[3] |
|
ndc_y = clip_space_point[1] / clip_space_point[3] |
|
|
|
return ndc_x, ndc_y |
|
|
|
|
|
# Example usage |
|
camera_position = (0, 0, 0) # Camera position |
|
look_at = (0, 0, -1) # Camera is looking along the negative Z-axis |
|
point_3d = (0.5, 0.5, -2) # A 3D point to project |
|
|
|
projected_point = project_point(camera_position, look_at, point_3d) |
|
print("Projected 2D Point in NDC:", projected_point) |