開発環境
- macOS Mojave - Apple (OS)
- Emacs (Text Editor)
- Windows 10 Pro (OS)
- Visual Studio Code (Text Editor)
- Python 3.7 (プログラミング言語)
- GIMP (ビットマップ画像編集・加工ソフトウェア、PPM形式(portable pixmap)の画像用)
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 コメント:
コメントを投稿