Semester 1, Week 10 Development - 62firelight/manimRT-490 GitHub Wiki
- As a user, I want to...
- add an arc representing an angle between two vectors so I can demonstrate that lighting is dependent on angles between certain vectors
- create a Manim mobject representing a human eye so I can introduce ray-tracing by comparing it with how humans perceive light (need permission to complete)
Some progress has also been done on computing a refracted ray, but it's not in a state that I'm satisfied with yet.
I found this Reddit thread that ran into a similar issue. Apparently inserting an animation to make the camera move over time will cause intersecting objects to display correctly. The rate at which the camera moves can be set to 0 so the camera doesn't move, but any intersecting objects will have their intersections shown properly. Here is a revised animation with that fix in place:
IntersectionAnimationTest.mp4
In the thread I linked, one of the commenters said that they still have no idea why this fix works after trudging through the source code for hours, which is a bit concerning... but at least it works.
Below is a zoomed in version of the image from last week, but with angles between some of the vectors (n and l, r and v). I considered adding a theta symbol but it's already pretty cluttered as it is.
Code (click to reveal)
from manim import *
from manim_rt.Arc3D import Arc3D
from manim_rt.RTPointLightSource import RTPointLightSource
from manim_rt.RTSphere import RTSphere
from manim_rt.Ray3D import Ray3D
from manim_rt.RTCamera import RTCamera
class RTPointLightSourceTest(ThreeDScene):
def construct(self):
self.set_camera_orientation(phi=90 * DEGREES, theta=-90 * DEGREES, frame_center=[0, 0, 1], zoom=1.5])
# Axes and X + Z labels
axes = ThreeDAxes(x_length=8)
x_label = MathTex("x").next_to(axes.get_x_axis().get_end())
z_label = MathTex("z").next_to(axes.get_z_axis().get_end())
# Camera
camera = RTCamera([-3, 0, 3], focal_length=1, plane_width=3, plane_height=3, total_width=1, total_height=1)
camera.rotate(-45 * DEGREES, UP, camera.projection_point_coords)
# Ray
ray = Ray3D(start=[-3, 0, 3], direction=[1, 0, -1], distance=4.5, color=RED)
ray_text = MathTex("R=(-3, 0, 3) + \\lambda (1, 0, -1)").next_to(ray.get_start(), OUT, buff=0.375).shift(0.5 * RIGHT)
# Sphere
sphere = RTSphere([0, 0, -1])
sphere.set_color(BLUE)
# Light Source
light = RTPointLightSource(center=[3, 0, 4], color=YELLOW)
# Calculate hit points
hit_points = sphere.get_intersection(ray)
# First hit point
first_hit_point = hit_points[0]
first_hit_point_dot = Dot3D(first_hit_point, color=PURPLE)
# Second hit point
second_hit_point = hit_points[1]
second_hit_point_dot = Dot3D(second_hit_point, color=PURPLE)
# Unit normal
unit_normal = Ray3D(first_hit_point, ray.get_unit_normal(0), color=BLUE)
unit_normal_text = MathTex("\\hat{n}").next_to(unit_normal.get_end(), RIGHT + OUT, buff=0.1)
# Light vector
light_vector = Ray3D(first_hit_point, ray.get_light_vector(0, light), color=YELLOW)
light_vector_text = MathTex("\\hat{l}").next_to(light_vector.get_end(), 0.1 * RIGHT + OUT, buff=0.45)
# Reflected light vector
reflected_light_vector = Ray3D(first_hit_point, ray.get_reflected_light_vector(0, light), color=ORANGE)
reflected_light_vector_text = MathTex("\\hat{r}").next_to(reflected_light_vector.get_end(), RIGHT + OUT, buff=0.1)
# Viewer vector (points towards the viewer of scene)
viewer_vector = Ray3D(first_hit_point, ray.get_viewer_vector(0, camera), color=GREEN)
viewer_vector_text = MathTex("\\hat{v}").next_to(viewer_vector.get_end(), IN)
# Shadow ray
shadow_ray = ray.get_shadow_ray(0, light, color=LIGHT_BROWN)
shadow_ray_text = VGroup(Tex("\\textbf{Shadow Ray}"), Tex("(no intersections)")).arrange(DOWN, aligned_edge=LEFT).next_to(shadow_ray.get_center(), RIGHT, buff=0.001)
# This sphere will intersect with the shadow ray above
blocking_sphere = RTSphere([2, 0, 2])
blocking_sphere.set_color(LIGHT_GRAY)
# Reflected ray (assuming the sphere is reflective)
reflected_ray = ray.get_reflected_ray(0, camera, color=GRAY)
reflected_ray_text = MathTex("\\hat{m}").next_to(reflected_ray.get_end(), RIGHT, buff=0.1)
angle_between_normal_and_light = Arc3D(unit_normal, light_vector)
angle_between_reflected_and_viewer = Arc3D(reflected_light_vector, viewer_vector)
# Add all relevants objects and text to the image
self.add(axes, sphere, light, camera, ray, first_hit_point_dot, second_hit_point_dot, unit_normal, light_vector, reflected_light_vector, viewer_vector, shadow_ray, reflected_ray, angle_between_normal_and_light, angle_between_reflected_and_viewer)
self.add_fixed_orientation_mobjects(x_label, z_label, ray_text, unit_normal_text, light_vector_text, reflected_light_vector_text, viewer_vector_text, shadow_ray_text, reflected_ray_text)
I used an image mobject of an eyeball for this implementation, but I still have not gotten permission to use this.
Eye3DTest.mp4
Code (click to reveal)
from manim import *
from manim_rt.Ray3D import Ray3D
class Eye3DTest(ThreeDScene):
def construct(self):
self.set_camera_orientation(phi=45 * DEGREES, theta=45 * DEGREES)
axes = ThreeDAxes()
labels = axes.get_axis_labels()
eye = ImageMobject("Eye.png")
ray = Ray3D([5, 0, 0], LEFT, distance=4.1, color=YELLOW)
# self.add(axes, labels, eye, ray)
self.play(GrowFromCenter(eye))
self.play(Create(ray))
Refraction is still a big WIP, but here is where I am at so far. The main problem that I'm having is coming up with a good example for refraction that can easily demonstrate that it is working correctly.
Code (click to reveal)
from manim import *
from manim_rt.RTSphere import RTSphere
from manim_rt.Ray3D import Ray3D
class RefractedRayTest(ThreeDScene):
def construct(self):
self.set_camera_orientation(phi=45 * DEGREES, theta=-135 * DEGREES, zoom=1.5)
axes = ThreeDAxes()
labels = axes.get_axis_labels()
sphere = RTSphere(refractive_index=1.7)
sphere.set_color(WHITE)
sphere.set_opacity(0.25)
ray = Ray3D([-3, 3, 1], [1, -1, -0.25], 2.45, color=RED)
hit_points = sphere.get_intersection(ray)
refracted_ray = ray.get_refracted_ray(sphere, color=BLUE)
print(refracted_ray.get_equation())
unit_normal = Ray3D(hit_points[0], ray.get_unit_normal(0), color=GREEN)
self.add(axes, labels, sphere, ray, refracted_ray, unit_normal)
- Do I have permission to use the eyeball image in ManimRT?
- Is there any feedback that can be given on the Ray-Object Intersection script that I've made at this link? (this can also be found in the Animation Planning section)
- Should the videos be commentated using voice or text? The answer is probably obvious here -- I've looked at a dozen SoME3 videos and all of them are voiced.
- Any potential issues with following the COSC342/450 lecture material too closely?
From highest to lowest priority:
- Develop full animation video for illustrating ray-object intersections
- Check TODO comments in code and potentially make changes based on those
- Start on interim report/presentation
- Elicit more requirements for either lighting and/or reflections and refractions
- Feature - As a developer I want to add a new object (other than spheres) that rays can intersect so that I can teach ray-tracing for different objects in my scene
- Add docstrings to created classes and their methods
- Add option to initialize camera grid with x value and aspect ratio
- Generate spheres that intersect the ray at different points (2 intersection points + 1 intersection + no intersection)