開発環境
- 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 10(Patterns)のPut It Together(138)、Perturbed patternsを取り組んでみる。
コード
Python 3
patterns.py
import math from matrices import IDENTITY_MATRIX from tuples import is_equal, Point, Color import random class Pattern: def __init__(self, transform=IDENTITY_MATRIX): self.transform = transform def at(self, point): return Color(point.x, point.y, point.z) def at_shape(self, shape, world_point): obj_point = shape.transform.inverse() * world_point point = self.transform.inverse() * obj_point return self.at(point) class Solid(Pattern): def __init__(self, color): super().__init__() self.color = color def at(self, point): return self.color class Stripe(Pattern): def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX): super().__init__(transform) self.a = pattern_a self.b = pattern_b def at(self, point): point = self.transform.inverse() * point if math.floor(point.x) % 2 == 0: return self.a.at(point) return self.b.at(point) class Gradient(Pattern): def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX): super().__init__(transform) self.a = pattern_a self.b = pattern_b def at(self, point): point = self.transform.inverse() * point distance = self.b.at(point) - self.a.at(point) fraction = point.x - math.floor(point.x) return self.a.at(point) + distance * fraction class Ring(Pattern): def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX): super().__init__(transform) self.a = pattern_a self.b = pattern_b def at(self, point): point = self.transform.inverse() * point if is_equal(math.floor(math.sqrt(point.x ** 2 + point.z ** 2)) % 2, 0): return self.a.at(point) return self.b.at(point) class Checkers(Pattern): def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX): super().__init__(transform) self.a = pattern_a self.b = pattern_b def at(self, point): point = self.transform.inverse() * point if is_equal(sum([math.floor(p) for p in [point.x, point.y, point.z]]) % 2, 0): return self.a.at(point) return self.b.at(point) class RadialGradient(Pattern): def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX): super().__init__(transform) self.a = pattern_a self.b = pattern_b def at(self, point): color_distance = self.b - self.a distance = math.sqrt(point.x ** 2 + point.z ** 2) fraction = distance - math.floor(distance) return self.a + color_distance * fraction class Blend(Pattern): def __init__(self, pattern_a, pattern_b, transform=IDENTITY_MATRIX): super().__init__(transform) self.a = pattern_a self.b = pattern_b def at(self, point): point = self.transform.inverse() * point color_a = self.a.at(point) color_b = self.b.at(point) return (color_a + color_b) / 2 class Perturb(Pattern): def __init__(self, pattern, transform=IDENTITY_MATRIX, range_of_values=None): super().__init__(transform) self.pattern = pattern if range_of_values is None: self.range_of_values = Point(*[random.random() for _ in range(3)]) else: self.range_of_values = range_of_values def at(self, point): point += self.range_of_values * 0.2 * random.random() point = self.transform.inverse() * point return self.pattern.at(point)
sample4.py
#!/usr/bin/env python3 import math import time from tuples import Point, Vector, Color from planes import Plane from materials import Material from patterns import Solid, Stripe, Blend, Perturb from camera import Camera from lights import Light from world import World from transformations import view_transform, rotation_y print('ファイル名, rendering time(秒)') width = 250 height = 125 stripe1 = Stripe(Solid(Color(1, 0, 0)), Solid(Color(1, 1, 1)), transform=rotation_y(math.pi / 4)) stripe2 = Stripe(Solid(Color(1, 0, 0)), Solid(Color(1, 1, 1)), transform=rotation_y(-math.pi / 4)) blend = Blend(stripe1, stripe2) perturb = Perturb(blend, range_of_values=Point(1, 1, 1)) patterns = [blend, perturb] colors = [Color(0, 1, 0), Color(1, 0, 0)] material = Material(specular=0) plane = Plane(material=material) 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([plane], Light(Point(-10, 10, -10), Color(1, 1, 1))) for i, (pattern, color) in enumerate(zip(patterns, colors), 6): material.pattern = pattern blend.a.a.color = color blend.b.a.color = color start = time.time() canvas = camera.render(world) s = time.time() - start with open(f'sample{i}.ppm', 'w') as f: canvas.to_ppm(f) print(f'sample{i}.ppm,{s}')
入出力結果(Bash、cmd(コマンドプロンプト)、Terminal、Jupyter(IPython))
C:\Users\...>py materials_test.py ........ ---------------------------------------------------------------------- Ran 8 tests in 0.003s OK C:\Users\...>py patterns_test.py ................ ---------------------------------------------------------------------- Ran 16 tests in 0.007s OK C:\Users\...>py sample4.py ファイル名, rendering time(秒) sample6.ppm,132.31091618537903 sample7.ppm,143.2693841457367 C:\Users\...>
0 コメント:
コメントを投稿