2019年6月6日木曜日

開発環境

The Ray Tracer Challenge: A Test-Driven Guide to Your First 3D Renderer (Jamis Buck(著)、Pragmatic Bookshelf)、Chapter 14(Groups)のFinding the Normal on a Child Object、Test #8(Convert a Normal Vector from Object Space to World Space)を取り組んでみる。

コード

shapes_test.py

#!/usr/bin/env python3
import math
from unittest import TestCase, main
from shapes import Shape
from transformations import translation
from matrices import IDENTITY_MATRIX, Matrix
from materials import Material
from rays import Ray
from tuples import Point, Vector
from transformations import scaling, rotation_y
from groups import Group
from spheres import Sphere


class ShapeTest(TestCase):
    def setUp(self):
        self.shape = Shape(material=Material())

    def tearDown(self):
        pass

    def test_default_transformation(self):
        self.assertEqual(self.shape.transform, IDENTITY_MATRIX)

    def test_material(self):
        self.assertEqual(self.shape.material.ambient, 0.1)
        self.assertEqual(self.shape.material, Material())

    def test_transform(self):
        self.assertEqual(Shape().transform,
                         IDENTITY_MATRIX)
        s = Shape()
        t = translation(2, 3, 4)
        s.transform = t
        self.assertEqual(s.transform, t)

    def test_parent_attribute(self):
        s = Shape()
        self.assertIsNone(s.parent)

    def test_converting_point_from_world_to_object_space(self):
        group1 = Group(transform=rotation_y(math.pi / 2))
        group2 = Group(transform=scaling(2, 2, 2))
        group1.add_child(group2)
        sphere = Sphere(translation(5, 0, 0))
        group2.add_child(sphere)
        point = sphere.world_to_obj(Point(-2, 0, -10))
        self.assertEqual(point, Point(0, 0, -1))

    def test_converting_normal_from_obj_to_world_space(self):
        group1 = Group(transform=rotation_y(math.pi / 2))
        group2 = Group(transform=scaling(1, 2, 3))
        group1.add_child(group2)
        sphere = Sphere(translation(5, 0, 0))
        group2.add_child(sphere)
        normal = sphere.normal_to_world(
            Vector(-1 / math.sqrt(3), 1 / math.sqrt(3), 1 / math.sqrt(3)))
        self.assertEqual(normal, Vector(0.28571, 0.42857, 0.85714))


if __name__ == '__main__':
    main()

shapes.py

from matrices import Matrix, IDENTITY_MATRIX
from materials import Material


class Shape:
    def __init__(self, transform=None, material=None, parent=None):
        if transform is None:
            self.transform = IDENTITY_MATRIX
        else:
            self.transform = transform
        if material is None:
            self.material = Material()
        else:
            self.material = material
        self.parent = None

    def __repr__(self):
        return f'{self.__class__.__name__}({self.transform},{self.material})'

    def intersect(self, ray):
        raise NotImplementedError()

    def normal_at(self, point):
        raise NotImplementedError()

    def world_to_obj(self, point):
        if self.parent is not None:
            point = self.parent.world_to_obj(point)
        return self.transform.inverse() * point

    def normal_to_world(self, normal):
        normal = self.transform.inverse().transpose() * normal
        normal.w = 0
        normal = normal.normalize()
        if self.parent is not None:
            normal = self.parent.normal_to_world(normal)
        return normal

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

C:\Users\...>py shapes_test.py
......
----------------------------------------------------------------------
Ran 6 tests in 0.004s

OK

C:\Users\...>

0 コメント:

コメントを投稿

関連コンテンツ