Skip to content
Snippets Groups Projects
video.py 7.82 KiB
from manim import *
from matplotlib.lines import Line2D

SCREEN_WIDTH = 14.2
SCREEN_HEIGHT = 8

class Slide:
    def __init__(self, objects, durations):
        self.objects = objects
        self.durations = durations

class Slides(Scene):
    def construct(self):
        def get_spaced_text(text, im, spacing=0.5, font_size=90):
            spacer = Text("d",font_size=90).scale(spacing).align_to(im,DOWN)
            return Text(text,color="white",font_size=font_size).scale(0.5).next_to(spacer, UP)

        def get_sphere_slide(im_path, text, durations):
            im = ImageMobject(im_path).scale_to_fit_height(SCREEN_HEIGHT)
            return Slide([
                im,
                get_spaced_text(text, im)
            ], durations)

        cheapo_group = self.get_image_group(["monte_carlo_one.png", "monte_carlo_vando.png", "monte_carlo_many.png"])
        cheapo_group_text = "one sample                           random samples                            many samples"
        cheapo_group_description = "too simple                           just right                            too expensive"
        
        guru_text = '''
        “It is generally not so much about the fact that rasterisation couldn't do the things. 
         It probably could, but it would most likely need several rendering passes 
         and all kinds of dirty tricks to accomplish things that depend on nonlocal effects.” 
                                                                        - (stackexchange guru joojaa)
        '''
        
        slides = [
            ### INTRODUCTION ###
            # title slide here [5]
            Slide([self.get_image_group(["raster_vs_raytrace.webp"])], [8]),

            ### RASTERIZATION ###
            # rasterization animation here [34]
            
            ### PROBLEMS ####
            get_sphere_slide("sad_sphere.png", "\"I'm not smooth\" :(", [3, 7]),
            Slide([self.get_image_group(["shadow.png", "glass.png"])], [25]),
            Slide([Text(guru_text,color="white",font_size=20)], [18]),

            ### RAYTRACING ###
            # raytracing animation goes here [15]
            Slide([self.get_image_height("different_materials.png")], [7]),

            ### PHYSICS ###
            Slide([self.get_image_group(["vindow_light_transfer.jpg"])], [12]),
            Slide([self.get_image_group(["vindow_light_same.jpg"])], [9]),
            Slide([self.get_image_group(["vindow_function_angle.jpg"])], [15]),
            Slide([self.get_image_group(["rendering_eq.png"])], [24]),

            ### MONTE CARLO INTEGRATION ###
            Slide([self.get_image_height("monte_carlo_img.png")], [10]),
            Slide([self.get_image_group(["monte_carlo_eq1.png", "monte_carlo_eq2"])], [22]),

            ### MONTE CARLO RAYTRACING ###
            Slide([self.get_image_height("monte_carlo_integral.png")], [10]),
            Slide([self.get_image_height("monte_carlo_question_mark.png")], [21]),
            Slide([self.get_image_height("monte_carlo_vando.png")], [8]),
            Slide([cheapo_group, get_spaced_text(cheapo_group_text, cheapo_group, spacing=1, font_size=60),get_spaced_text(cheapo_group_description, cheapo_group, spacing=0, font_size=60)], [3,3,17]),
            Slide([self.get_image_height("monte_carlo_bouncing.png")], [10]),
            Slide([self.get_image_height("monte_carlo_bouncing_infinite.png")], [10]),
            Slide([self.get_image_height("different_materials.png")], [7]),
            Slide([self.get_image_group(["mirror.png"])], [13]),
            get_sphere_slide("happy_sphere.png", "\"I'm smooth!\" :)", [3, 15]),
            Slide([self.get_image_group(["motion_blur.jpg"])], [10]),
          
            ### PATH TRACING ###
            Slide([self.get_image_height("monte_carlo_path_tracing.png")], [23]),
            # raytracing animation goes here [15]

            ### CONCLUSION ###
            # title slide here [5]
            Slide([self.get_image_group(["raster_vs_raytrace.webp"])], [7]),
        ]
        self.slideshow(slides)

    def slideshow(self, slides, fade_in_fade_out_time=1):
        for slide in slides:
            for i in range(0, len(slide.objects)):
                self.play(FadeIn(slide.objects[i]), run_time=fade_in_fade_out_time)
                self.wait(slide.durations[i])

            self.play(FadeOut(*slide.objects), run_time=fade_in_fade_out_time)

    def get_image_group(self, images):
        image_width = SCREEN_WIDTH / (len(images))
        all_images = []
        for image in images:
            im = ImageMobject(image)
            im.scale_to_fit_width(image_width)
            all_images.append(im)
    
        group = Group(*all_images)
        group.arrange()
        return group

    def get_image_height(self, path):
        return ImageMobject(path).scale_to_fit_height(SCREEN_HEIGHT)

class Title(Scene):
    def construct(self):
        t2 = Text("d",color="black",font_size=90).scale(0.25)
        t1 = Text("Carlo",color='#8080ff',font_size=90).scale(1).next_to(t2,UP)
        t0 = Text("Monte",color="pink",font_size=90).scale(1).next_to(t1,UP)
        t3 = Text("Ray",color="purple",font_size=90).scale(1).next_to(t2,DOWN)
        t4 = Text("Tracing",color="green",font_size=90).scale(1).next_to(t3,DOWN)
        self.wait(1)
        self.play(FadeIn(t0),lag_ratio=0.1,run_time=0.5)
        self.play(FadeIn(t1),lag_ratio=0.1,run_time=0.5)
        self.play(FadeIn(t3),lag_ratio=0.1,run_time=0.5)
        self.play(FadeIn(t4),lag_ratio=0.1,run_time=0.5)
        self.wait(1.5)
        self.play(FadeOut(t0,t1,t3,t4),lag_ratio=0.1,run_time=0.5)
        
class RasterizationTriangleScene(ThreeDScene):
    def construct(self):
        self.set_camera_orientation(phi=PI / 4, theta=PI / 6, gamma= PI / 2)
        cube = Cube(side_length=1)
        cube.shift([0, 0, 3])
        rect = Rectangle(width=7, height=5)
        rect.shift([-1, 0, -4])
        self.play(Create(rect), run_time=1)
        self.play(Create(cube), run_time=1)
        camera_plane = Text("camera plane",color="white",font_size=30)
        camera_plane.shift([1.5, 3, -4])
        self.play(Create(camera_plane), run_time=1)
        self.wait(7)

        normal = Line3D(start=[0.25, 0.25, 0], end=[0.25, 0.25, -1])
        normal.shift([-0.5, -0.5, 2.5])

        triangle = Polygon([0, 0, 0], [0, 1, 0], [1, 0, 0], stroke_width = 1, stroke_color=RED)
        triangle.shift([-0.5, -0.5, 2.5])
        self.play(Create(triangle), run_time=1)
        self.wait(2)
        self.play(Create(normal), run_time=1)
        self.wait(10)
        
        plane_triangle = Polygon([0, 0, 0], [0, 0.7, 0], [0.7, 0, 0], stroke_width = 1, stroke_color=RED)
        plane_triangle.shift([-0.35, -0.35, -4])

        remain_triangle = Polygon([0, 0, 0], [0, 1, 0], [1, 0, 0], stroke_width = 1, stroke_color=RED)
        remain_triangle.shift([-0.5, -0.5, 2.5])

        self.add(remain_triangle)
        self.play(Transform(triangle, plane_triangle))
        self.wait(3)
        self.move_camera(phi=PI, theta=0, gamma= PI / 2)
        self.wait(5)

class RaytracingScene(Scene):
    def construct(self):
        square = Square(side_length=3)
        square.shift([1, 1, 0])

        circle = Circle(radius=1.5)
        circle.shift([-3, -2, 0])

        triangle = Triangle()
        triangle.shift([1.5, -3, 0])

        lightbulb = ImageMobject("lightbulb.png")
        lightbulb.shift([-0.75, -3.5, 0])
        self.add(lightbulb)
        self.play(Create(square), run_time=1)
        self.play(Create(circle), run_time=1)
        self.play(Create(triangle), run_time=1)
        self.wait(3)

        light_rays = [
            ([-2, 5, 0], [-2, -0.85, 0]),
            ([-2, -0.85, 0], [1.4, -2.2, 0]),
            ([1.4, -2.2, 0], [0, -0.5, 0]),
            ([0, -0.5, 0], [-0.7, -3.3, 0])
        ]

        for ray in light_rays:
            self.play(Create(Line3D(start=ray[0], end=ray[1], color=YELLOW)), run_time=1)

        self.wait(5)