2019年5月26日日曜日

開発環境

The Ray Tracer Challenge: A Test-Driven Guide to Your First 3D Renderer (Jamis Buck(著)、Pragmatic Bookshelf)、Chapter 12(Cubes)のPuutting It Together(175)を取り組んでみる。

コード

cubes_test.py

#!//usr/bin/env python3
from unittest import TestCase, main
from cubes import Cube
from tuples import Point, Vector
from rays import Ray


class CubeTest(TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_ray_intersects_cube(self):
        cube = Cube()
        origins = [(5, 0.5, 0),
                   (-5, 0.5, 0),
                   (0.5, 5, 0),
                   (0.5, -5, 0),
                   (0.5, 0, 5),
                   (0.5, 0, -5),
                   (0, 0.5, 0)]
        directions = [(-1, 0, 0),
                      (1, 0, 0),
                      (0, -1, 0),
                      (0, 1, 0),
                      (0, 0, -1),
                      (0, 0, 1),
                      (0, 0, 1)]
        ts1 = [4, 4, 4, 4, 4, 4, -1]
        ts2 = [6, 6, 6, 6, 6, 6, 1]
        for origin, direction, t1, t2 in zip(origins, directions, ts1, ts2):
            origin = Point(*origin)
            direction = Vector(*direction)
            ray = Ray(origin, direction)
            intersections = cube.intersect(ray)
            self.assertEqual(len(intersections), 2)
            for intersection, t in zip(intersections, [t1, t2]):
                self.assertEqual(intersection.t, t)

    def test_ray_misses_cube(self):
        cube = Cube()
        origins = [(-2, 0, 0),
                   (0, -2, 0),
                   (0, 0, -2),
                   (2, 0, 2),
                   (0, 2, 2),
                   (2, 2, 0)]
        directions = [(0.2673, 0.5345, 0.8018),
                      (0.8018, 0.2673, 0.5345),
                      (0.5345, 0.8018, 0.2673),
                      (0, 0, -1),
                      (0, -1, 0),
                      (-1, 0, 0)]
        for origin, direction in zip(origins, directions):
            origin = Point(*origin)
            direction = Vector(*direction)
            ray = Ray(origin, direction)
            intersections = cube.intersect(ray)
            self.assertEqual(len(intersections), 0)

    def test_normal_on_surface_of_cube(self):
        cube = Cube()
        points = [(1, 0.5, -0.8),
                  (-1, -0.2, 0.9),
                  (-0.4, 1, -0.1),
                  (0.3, -1, -0.7),
                  (-0.6, 0.3, 1),
                  (0.4, 0.4, -1),
                  (1, 1, 1),
                  (-1, -1, -1)]
        normals = [(1, 0, 0),
                   (-1, 0, 0),
                   (0, 1, 0),
                   (0, -1, 0),
                   (0, 0, 1),
                   (0, 0, -1),
                   (1, 0, 0),
                   (-1, 0, 0)]
        for point, normal in zip(points, normals):
            point = Point(*point)
            self.assertEqual(cube.normal_at(point),
                             Vector(*normal))


if __name__ == '__main__':
    main()

cubes.py

import math
from shapes import Shape
from tuples import EPSILON, Vector
from intersections import Intersection, Intersections


class Cube(Shape):
    def intersect(self, ray):
        xtmin, xtmax = check_axis(ray.origin.x, ray.direction.x)
        ytmin, ytmax = check_axis(ray.origin.y, ray.direction.y)
        ztmin, ztmax = check_axis(ray.origin.z, ray.direction.z)

        tmin = max(xtmin, ytmin, ztmin)
        tmax = min(xtmax, ytmax, ztmax)
        if tmin > tmax:
            return Intersections()
        return Intersections(*[Intersection(t, self) for t in [tmin, tmax]])

    def normal_at(self, point):
        max_c = max([abs(c) for c in [point.x, point.y, point.z]])
        if max_c == abs(point.x):
            return Vector(point.x, 0, 0)
        if max_c == abs(point.y):
            return Vector(0, point.y, 0)
        return Vector(0, 0, point.z)


def check_axis(origin, direction):
    tmin_numerator = -1 - origin
    tmax_numerator = 1 - origin

    if abs(direction) >= EPSILON:
        tmin = tmin_numerator / direction
        tmax = tmax_numerator / direction
    else:
        tmin = tmin_numerator * math.inf
        tmax = tmax_numerator * math.inf
    if tmin > tmax:
        return tmax, tmin
    return tmin, tmax

sample1.py

#!/usr/bin/env python3
import math
import time
from tuples import Point, Vector, Color
from planes import Plane
from spheres import Sphere
from cubes import Cube
from materials import Material
from camera import Camera
from lights import Light
from world import World
from transformations import translation, scaling, view_transform
from transformations import rotation_x, rotation_y
print('ファイル名, rendering time(秒)')

width = 250
height = 125

wall1 = Plane(material=Material(color=Color(0, 0, 1)),
              transform=translation(0, 0, 7) *
              rotation_y(-math.pi / 4) *
              rotation_x(math.pi / 2))
wall2 = Plane(material=Material(color=Color(1, 0, 0)),
              transform=translation(0, 0, 7) *
              rotation_y(math.pi / 4) *
              rotation_x(math.pi / 2))

floor = Plane(material=Material(Color(0, 1, 0)),
              transform=translation(0, -1, 0))

burlywood = Color(0.87059, 0.72159, 0.52941)
yellow = Color(1, 1, 0)
c = Color(0.5, 0.5, 0)
board = Cube(material=Material(color=burlywood),
             transform=translation(0, 1.1, -1.75) *
             scaling(2, 0.1, 1))
leg1 = Cube(material=Material(color=burlywood),
            transform=translation(1.9, 0.5, -2.65) *
            scaling(0.1, 0.5, 0.1))
leg2 = Cube(material=Material(color=burlywood),
            transform=translation(-1.9, 0.5, -2.65) *
            scaling(0.1, 0.5, 0.1))
leg3 = Cube(material=Material(color=burlywood),
            transform=translation(1.9, 0.5, -0.85) *
            scaling(0.1, 0.5, 0.1))
leg4 = Cube(material=Material(color=burlywood),
            transform=translation(-1.9, 0.5, -0.85) *
            scaling(0.1, 0.5, 0.1))

cube1 = Cube(material=Material(color=yellow),
             transform=translation(0.5, 1.7, -1.0) *
             rotation_y(math.pi / 6) *
             scaling(0.5, 0.5, 0.5))
cube2 = Cube(material=Material(color=yellow),
             transform=translation(-1, 0.2, -2.5) *
             scaling(0.2, 0.2, 0.2))
cube3 = Cube(material=Material(color=yellow),
             transform=translation(1, 0.2, -2.5) *
             scaling(0.2, 0.2, 0.2))
sphere = Sphere(material=Material(color=yellow),
                transform=translation(-0.5, 1.7, -1.5) *
                scaling(0.5, 0.5, 0.5))

camera = Camera(width, height, math.pi / 2,
                transform=view_transform(Point(0, 1.5, -5), Point(0, 1, 0),
                                         Vector(0, 1, 0)))
world = World([floor, wall1, wall2, board, leg1, leg2, leg3, leg4,
               cube1, cube2, cube3, sphere],
              Light(Point(-10, 10, -10), Color(1, 1, 1)))

start = time.time()
canvas = camera.render(world)
s = time.time() - start
with open(f'sample1.ppm', 'w') as f:
    canvas.to_ppm(f)
print(f'sample1.ppm,{s}')

入出力結果(Bash、cmd(コマンドプロンプト)、Terminal、Jupyter(IPython))

C:\Users\...>py cube_test.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK

C:\Users\...>py sample1.py
ファイル名, rendering time(秒)
sample1.ppm,515.1900041103363

C:\Users\...>

0 コメント:

コメントを投稿