Example Gallery - 62firelight/manimRT-490 GitHub Wiki

This gallery is a collection of the scenes that I created to demonstrate ManimRT's capabilities during development. These are not meant to be the neatest images or animations (in terms of visual appeal and coding style), but will hopefully be useful as a starting point for different scenes. Feel free to use them as a template.

Camera and Ray

CameraTest_ManimCE_v0 18 0

from manim import * 
from manim_rt import *

class CameraAndRay(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=-45*DEGREES, frame_center=[0, 0, 0.25], zoom=5.5)
        camera = RTCamera([0, 0, 1], focal_length=1)
        red_ray = camera.draw_ray(8, 5, color=RED, length=1.75, thickness=0.005)
        self.add(camera, red_ray)

Ray-Sphere Intersection

RayIntersectionTest_ManimCE_v0 18 0

from manim import *
from manim_rt import *

class RaySphereIntersection(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=75 * DEGREES, theta=-95 * DEGREES)
        axes = ThreeDAxes()
        red_ray = Ray3D([-3, 0, 0], RIGHT, 5, color=RED)
        sphere = RTSphere(color=BLUE)
        hit_points = sphere.get_intersection(red_ray)
        for hit_point in hit_points:
            hit_point_obj = Dot3D(hit_point)
            self.add(hit_point_obj)
        self.add(axes, red_ray, sphere)

Ray-Sphere Intersections (Transformed Sphere)

TransformedRayIntersectionTest_ManimCE_v0 18 0

from manim import *
from manim_rt import *

class RaySphereIntersectionTransformed(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=75 * DEGREES, theta=-90 * DEGREES)
        
        axes = ThreeDAxes()
        x_label = MathTex("x").next_to(axes.get_x_axis().get_end(), buff=0.25).shift([-0.5, 0.5, 0])
        z_label = MathTex("z").next_to(axes.get_z_axis().get_end(), buff=0.25).shift([0, 3, 0])
        red_ray = Ray3D([-4, 0, 0], RIGHT, 8, color=RED)
        camera = RTCamera([-4, 1.75, 0], image_width=3, image_height=3).rotate(90*DEGREES, RIGHT).rotate(90*DEGREES, IN)
        sphere = RTSphere([0, 0, 0], x_scale=2, y_scale=2, z_scale=2)
        sphere.set_color(BLUE)
        
        hit_points = sphere.get_intersection(red_ray)
        
        intersections_text = VGroup()
        intersections_text = VGroup(Tex("\\textbf{Hit Points}"), MathTex("(-2, 0, 0)"), MathTex("(2, 0, 0)"))
        
        for hit_point in hit_points:
            hit_point_obj = Dot3D(hit_point)
            self.add(hit_point_obj)
            
        intersections_text.arrange(DOWN).to_corner(DR, buff=1.5).scale(1.45).shift([0, -0.35, 0])
        sphere_text = VGroup(Tex("\\textbf{Sphere at Origin}"), Tex("Radius is 2")).set_color(BLUE).arrange(DOWN).to_corner(UR, buff=1.5).scale(1.45).shift([0.25, 0.5, 0])
        
        hit_point_normal = hit_points[0]
        
        red_ray_equation = MathTex(red_ray.get_equation(), color=RED).scale(1.25)
        
        equations = VGroup(red_ray_equation).arrange(DOWN, center=False, aligned_edge=LEFT).to_edge(LEFT).shift([0, -1.9, 0])
        
        self.add(axes, camera, red_ray, sphere)
        self.add_fixed_in_frame_mobjects(equations)
        self.add_fixed_in_frame_mobjects(intersections_text)
        self.add_fixed_in_frame_mobjects(sphere_text)
        self.add_fixed_in_frame_mobjects(x_label)
        self.add_fixed_in_frame_mobjects(z_label)

Phong Illumination Model

RTPointLightSourceTest_ManimCE_v0 18 0

from manim import *
from manim_rt import *

class PhongIlluminationModel(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=90 * DEGREES, theta=-95 * DEGREES, frame_center=[0, 0, 0.65], zoom=5)
        
        phong = MathTex("I(\\boldsymbol{p})=I_ak_a + I_dk_d(",
                        "\\boldsymbol{\hat{n}}",
                        "\cdot", 
                        "\\boldsymbol{\hat{l}}", 
                        ") + I_sk_s(", 
                        "\\boldsymbol{\hat{r}}", 
                        "\cdot", 
                        "\\boldsymbol{\hat{v}}", 
                        ")^a")
        
        phong.set_color_by_tex("\\boldsymbol{\hat{n}}", BLUE)
        phong.set_color_by_tex("\\boldsymbol{\hat{l}}", YELLOW)
        phong.set_color_by_tex("\\boldsymbol{\hat{r}}", ORANGE)
        phong.set_color_by_tex("\\boldsymbol{\hat{v}}", GREEN)
        phong.scale(1.45)
        phong.move_to([0.125, 0, 1.25])
        
        # 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, image_width=3, image_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], length=4.5, thickness=0.01, 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=[2.35, 0, 4], radius=0.1, 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=LIGHT_PINK)
        
        # 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("\\boldsymbol{\\hat{n}}", color=BLUE).scale(2).next_to(unit_normal.get_end(), RIGHT, buff=-0.14)
        
        # Light vector
        light_vector = Ray3D(first_hit_point, ray.get_light_vector(0, light), color=YELLOW)
        light_vector_text = MathTex("\\boldsymbol{\\hat{l}}", color=YELLOW).scale(2).next_to(light_vector.get_end(), RIGHT, buff=-0.025)
        
        # Reflected light vector
        reflected_light_vector = Ray3D(first_hit_point, ray.get_reflected_light_vector(0, light), color=ORANGE)
        reflected_light_vector_text = MathTex("\\boldsymbol{\\hat{r}}", color=ORANGE).scale(2).next_to(reflected_light_vector.get_end(), RIGHT, buff=-0.075)
        
        # 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("\\boldsymbol{\\hat{v}}", color=GREEN).scale(2).next_to(viewer_vector.get_end(), IN)
        
        angle_between_normal_and_light = Arc3D(unit_normal, light_vector)
        angle_between_reflected_and_viewer = Arc3D(reflected_light_vector, viewer_vector)
        
        # Add all relevant objects and text to the image
        self.add(sphere)
        self.add(light)
        self.add(camera)
        self.add(ray)
        self.add(first_hit_point_dot)
        self.add(second_hit_point_dot) 
        self.add(unit_normal) 
        self.add(light_vector) 
        self.add(reflected_light_vector)
        self.add(viewer_vector)
        self.add(angle_between_normal_and_light)
        self.add(angle_between_reflected_and_viewer)
        
        self.add_fixed_orientation_mobjects(x_label)
        self.add_fixed_orientation_mobjects(z_label)
        self.add_fixed_orientation_mobjects(ray_text)
        self.add_fixed_orientation_mobjects(phong)
        self.add_fixed_orientation_mobjects(unit_normal_text)
        self.add_fixed_orientation_mobjects(light_vector_text)
        self.add_fixed_orientation_mobjects(reflected_light_vector_text)
        self.add_fixed_orientation_mobjects(viewer_vector_text)

Shadow Rays

ShadowRays_ManimCE_v0 18 0

from manim import *
from manim_rt import *

class ShadowRays(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=90 * DEGREES, theta=-90 * DEGREES, frame_center=[0, 0, 1.3], zoom=0.9)
        
        # 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, image_width=3, image_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], length=4.5, thickness=0.01, 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 1
        light1 = RTPointLightSource(center=[-3, 0, 3], radius=0.65, color=YELLOW)
        light1_text = Tex("A").next_to(light1)
        
        # Light Source 2
        light2 = RTPointLightSource(center=[3, 0, 4], radius=0.75, color=YELLOW)
        light2_text = Tex("B").next_to(light2)
        
        # 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, radius=0.25, color=RED)
        first_hit_point_text = Tex("Hit Point").next_to(first_hit_point, buff=0.65).shift([0, 0, 0.25])
        
        # Shadow ray
        shadow_ray1 = ray.get_shadow_ray(0, light1, color=LIGHT_BROWN)
        shadow_ray2 = ray.get_shadow_ray(0, light2, color=LIGHT_BROWN)
        
        # This sphere will intersect with the shadow ray above
        blocking_sphere = RTSphere([1.5, 0, 2], x_scale=0.5, y_scale=0.5, z_scale=0.5)
        blocking_sphere.set_color(GREEN)
        
        # Add all relevant objects and text to the image
        self.add(sphere, first_hit_point_dot, light1, light2, shadow_ray1, shadow_ray2, blocking_sphere)
        self.add_fixed_orientation_mobjects(first_hit_point_text, light1_text, light2_text)

Mirror Ray

ReflectionRay_ManimCE_v0 18 0

from manim import *
from manim_rt import *

class MirrorRay(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=90 * DEGREES, theta=-90 * DEGREES, frame_center=[0.25, 0, 0.5], zoom=1)
        
        # Axes and X + Z labels
        axes = ThreeDAxes(x_length=8)
        
        # Camera
        camera = RTCamera([-3, 0, 3], focal_length=1, image_width=3, image_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], length=5.5, thickness=0.01, 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
        plane = RTPlane([1, 0, -1], x_scale=8, y_scale=2, opacity=0.5)
        plane.set_color(BLUE)
        
        # Calculate hit points 
        hit_points = plane.get_intersection(ray)
        
        # First hit point
        first_hit_point = hit_points[0]
        first_hit_point_dot = Dot3D(first_hit_point, radius=0.25, color=RED)
        first_hit_point_text = Tex("Hit Point").next_to(first_hit_point, direction=LEFT, buff=0.5).shift([0.25, 0, -0.55])
        
        # Unit normal
        normal = Ray3D(first_hit_point, ray.get_unit_normal(0), length=4, color=GREEN)
        normal_text = Tex("Normal").next_to(normal.get_center(), buff=0.2).shift([0.1, 0, 1])
        
        # Mirror ray
        mirror_ray = ray.get_reflected_ray(0, camera, length=6, color=LIGHT_GREY)
        
        # This sphere will intersect with the mirror ray
        blocking_sphere = RTSphere([4, 0, 2], x_scale=0.5, y_scale=0.5, z_scale=0.5)
        blocking_sphere.set_color(GREEN)
        
        # Add all relevant objects and text to the image
        self.add(camera, ray, plane, first_hit_point_dot, normal, mirror_ray, blocking_sphere)
        self.add_fixed_orientation_mobjects(first_hit_point_text, normal_text)

Refracted Rays

RefractedRayTest_ManimCE_v0 18 0

from manim import *
from manim_rt import *

class RefractedRays(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=89 * DEGREES, theta=-180 * DEGREES, zoom=2, frame_center=[1, 0, -0.5])
        
        # Axes for easier placement of objects
        axes = ThreeDAxes()
        labels = axes.get_axis_labels()
        
        # Planes (maybe the air plane should be a different colour)
        water_plane = RTPlane(refractive_index=1.33, color=BLUE)
        air_plane = RTPlane([0, -1, -1], refractive_index=1, color=BLUE)
        
        # Incident ray
        ray_start = [-1, 1, 0.5]
        ray = Ray3D(ray_start, ORIGIN - ray_start, 1, color=RED)
        
        # Calculate intersection so we can actually get the refracted ray
        hit_points = water_plane.get_intersection(ray)
        
        # Unit normal for n1
        unit_normal_n1 = Ray3D(hit_points[0], ray.get_unit_normal(0), color=GREEN)
        
        # Angle between unit normal for n1 and incident ray
        angle_n1 = Arc3D(ray, unit_normal_n1)
        angle_n1_text = MathTex("\\theta_1").next_to(angle_n1.get_center() + 0.1 * UP, OUT)
        
        # Unit normal for n2
        unit_normal_n2 = Ray3D(hit_points[0], [0, 0, -1], color=ORANGE)
        
        # Our first refracted ray travelling from air into water
        first_refracted_ray = ray.get_refracted_ray(water_plane, color=BLUE, length=1.5)
        
        # Angle between unit normal for n2 and the first refracted ray
        angle_n2 = Arc3D(unit_normal_n2, first_refracted_ray)
        angle_n2_text = MathTex("\\theta_2").next_to(angle_n2.get_center() + 0.2 * DOWN, IN)
        
        # Show refractive indices
        n1_text = MathTex("n = 1").next_to(unit_normal_n1.get_center(), DOWN, buff=0.35)
        n2_text = MathTex("n = 1.33").next_to(first_refracted_ray.get_center(), DOWN, buff=0.75)
        
        # Calculate intersection for refracted ray so we can show the second refracted ray
        refracted_ray_hit_points = air_plane.get_intersection(first_refracted_ray)
        
        # Our second refracted ray travelling from water to air
        # The direction of this ray should be the same as the previous incident ray
        second_refracted_ray = first_refracted_ray.get_refracted_ray(air_plane, refractive_index=1.33, length=2, color=RED)
        
        # Show last refractive index 
        n3_text = MathTex("n = 1").next_to(second_refracted_ray.get_center(), IN, buff=0.5)
        
        # Objects
        self.add(water_plane, air_plane, ray, first_refracted_ray, unit_normal_n1, unit_normal_n2, angle_n1, angle_n2, second_refracted_ray)
        
        # Text
        self.add_fixed_orientation_mobjects(angle_n1_text, angle_n2_text, n1_text, n2_text, n3_text)

Total Internal Reflection

TotalInternalRefractionTest_ManimCE_v0 18 0

from manim import *
from manim_rt import *

class TotalInternalRefraction(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=85 * DEGREES, theta=-90 * DEGREES, zoom=1.75)
        
        # Axes for easier placement of objects
        axes = ThreeDAxes()
        labels = axes.get_axis_labels()
        
        # Planes
        bottom_air_plane = RTPlane(x_scale=8, refractive_index=1, color=BLUE)
        top_air_plane = RTPlane(translation=[0, 0, 0.5], x_scale=8, refractive_index=1, color=BLUE)
        
        # Incident ray
        ray_start = [-2, 0, 0.5]
        ray = Ray3D(ray_start, np.subtract([-1, 0, 0], ray_start), 1, color=RED)
        
        # Objects
        self.add(bottom_air_plane, top_air_plane, ray)
        
        # Text
        n1_text = MathTex("n_1 = 1.33").next_to(ray.get_center(), LEFT)
        
        n2_top_text = MathTex("n_2 = 1").next_to(ORIGIN, OUT, 1)
        n2_bottom_text = MathTex("n_2 = 1").next_to(ORIGIN, IN, 0.5)
        
        self.add_fixed_orientation_mobjects(n1_text, n2_top_text, n2_bottom_text)
        
        for i in range(6):
            if i % 2 == 0:
                intersecting_plane = bottom_air_plane
                ray_color = GREEN
            else:
                intersecting_plane = top_air_plane
                ray_color = RED
            
            # Calculate intersection so we can actually get the refracted ray
            intersecting_plane.get_intersection(ray)
            
            # First (of many) refracted rays that get reflected instead due to Total Internal Reflection
            refracted_ray = ray.get_refracted_ray(bottom_air_plane, color=ray_color, length=1.125, refractive_index=1.33)
            
            self.add(refracted_ray)
            
            ray = refracted_ray

Animations

Basic Intersection

https://github.com/user-attachments/assets/8b971f01-36d9-4e0c-a136-2d750d31f42e

from manim import *
from manim_rt import *

class BasicIntersection(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=65*DEGREES, theta=-135*DEGREES, zoom=1.5) 
        sphere = RTSphere(color=BLUE)
        ray = Ray3D([-5, 0, 0], length=10, color=RED)
        self.begin_ambient_camera_rotation(0)
        self.play(GrowFromCenter(sphere))
        self.play(Create(ray, run_time=2))
        self.wait(2)

Ray Tracing Algorithm

https://github.com/user-attachments/assets/d75863b4-b22d-4663-96b7-5bf504f541c0

from manim import *
from manim_rt import *

class BasicRayTracingAlgorithm(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=65*DEGREES, theta=0*DEGREES, zoom=1.5, frame_center=[0, 0, 1.5])

        axes = ThreeDAxes()
        labels = axes.get_axis_labels()
        
        red_sphere_location = [-1, 2, 2]
        
        # Camera
        ray_start = [2, -2.5, 3]
        camera = RTCamera(ray_start, image_width=3, image_height=3).rotate(90 * DEGREES, RIGHT).rotate(35 * DEGREES, OUT)
        partial_ray = camera.draw_ray(2, 2, 2)
        ray = camera.draw_ray(2, 2, 7.5)
        
        pixel = camera.colour_pixel(2, 2, RED).rotate(90 * DEGREES, RIGHT).rotate(35 * DEGREES, OUT)
        
        # Scene
        plane = RTPlane(x_scale=3, y_scale=8, color=BLUE)
        transparent_sphere = RTSphere(translation=[0, 0, 1], opacity=0.5)
        red_sphere = RTSphere(translation=red_sphere_location, color=RED)
        light_source = RTPointLightSource([0, -0.5, 3]).scale(0.25)
        
        sphere = RTSphere.generate_sphere(ray, 2.5, color=RED)
        
        # Animations
        self.begin_ambient_camera_rotation(0)
        
        self.play(GrowFromCenter(camera))
        self.play(GrowFromCenter(plane))
        self.play(GrowFromCenter(red_sphere))
        self.play(GrowFromCenter(transparent_sphere))
        self.play(GrowFromCenter(light_source))
        self.play(Create(partial_ray))
        self.play(FadeOut(partial_ray))
        self.play(Create(ray))
        self.play(FadeIn(pixel))