Shadow

In this module, I will explain the basic concept behind our shadow calculation.

../_images/final.png

In this module we are dealing with the following code snippet of the Engine Loop:

main method
1...
2
3    shadow_points = Shadow.get_shadow(self.mesh_list, light_direction)
4    shadow_points_camera = self.camera_model.world_transform(shadow_points, self.C_T_V)
5    self.camera_model.draw_poly(shadow_points_camera)
6
7    ...

The shadow calculation is actually quite simple.

  • We take the world points of the triangle and apply the light vector to them.

  • Then, we calculate the intersection of this line with the ground plane.


class Shadow

This class provides static methods to calculate the shadow projections of 3D triangles onto a plane.

Methods:

get_shadow(triangles, light_vec)

This method calculates the shadow projection of triangles onto a plane based on a given light vector.

Parameters: - triangles (list): A list of triangle objects, where each triangle contains a list of 3D world points. - light_vec (np.ndarray): A 3D vector representing the direction of the light source.

Returns: - list of np.ndarray: A list of points representing the shadow projection on the plane.

@staticmethod
def get_shadow(triangles, light_vec):

    shadow_points = []
    plane_normal = np.array([0, 0, 1])

    for triangle in triangles:
        for point in triangle.world_points:
            shadow_points.append(Shadow.find_intersection(plane_normal, point[:3].flatten(), light_vec))

    unique_array = list(map(np.array, set(tuple(arr) for arr in shadow_points)))
    shadow_points = []

    for point in unique_array:
        shadow_points.append(np.vstack([point.reshape(-1, 1), [[1]]]))

    return shadow_points

find_intersection(plane_normal, line_point, line_dir, plane_d=2)

This method calculates the intersection point between a line and a plane.

Note

This function is used by the get_shadow method.

Parameters:

  • plane_normal (np.ndarray): The normal vector of the plane.

  • line_point (np.ndarray): A point on the line.

  • line_dir (np.ndarray): The direction vector of the line.

  • plane_d (float, optional): The plane offset from the origin (default is 2).

Returns:

  • np.ndarray or None: The intersection point, or None if the line is parallel to the plane.

@staticmethod
def find_intersection(plane_normal, line_point, line_dir, plane_d=2):

    a, b, c = plane_normal
    x0, y0, z0 = line_point
    vx, vy, vz = line_dir

    denominator = a * vx + b * vy + c * vz

    if denominator == 0:
        return None

    t = -(a * x0 + b * y0 + c * z0 + plane_d) / denominator

    intersection_point = np.array([x0 + t * vx, y0 + t * vy, z0 + t * vz])

    return intersection_point

Warning

The shadow is only cast on the bottom plane. While the height can be adjusted, it will not affect other objects.