Add reflections

numpy
JD Cantrell 3 years ago
parent d7c2912645
commit 02ddb6af07

@ -10,6 +10,7 @@ class material:
diffuse=0.9,
specular=0.9,
shininess=200,
reflective=0.0,
pattern=None,
):
self.color = color(1, 1, 1) if c is None else c
@ -17,6 +18,7 @@ class material:
self.diffuse = diffuse
self.specular = specular
self.shininess = shininess
self.reflective = reflective
self.pattern = solid_pattern(self.color) if pattern is None else pattern
def __eq__(self, other):

@ -21,6 +21,7 @@ class computations:
self.point = position(ray, self.t)
self.eyev = -ray.direction
self.normalv = self.object.normal(self.point)
self.reflectv = ray.direction.reflect(self.normalv)
# TODO - understand why we neeed to multiply epsilon to get a useful
# overpoint
self.over_point = self.point + self.normalv * (sys.float_info.epsilon * 15000)

@ -22,25 +22,38 @@ class world:
return intersections(xs)
def shade(self, comps):
def shade(self, comps, bounces=10):
c = color(0, 0, 0)
for light in self.lights:
c += lighting(
comps.object,
light,
comps.over_point,
comps.eyev,
comps.normalv,
self.is_shadowed(comps.over_point, light),
c += (
lighting(
comps.object,
light,
comps.over_point,
comps.eyev,
comps.normalv,
self.is_shadowed(comps.over_point, light),
)
+ self.reflected_color(comps, bounces)
)
return c
def color(self, ray):
def color(self, ray, bounces=10):
h = hit(self.intersections(ray))
if h is None:
return color(0, 0, 0)
else:
return self.shade(computations(h, ray))
return self.shade(computations(h, ray), bounces)
def reflected_color(self, comps, bounces=10):
if comps.object.material.reflective == 0:
return color(0, 0, 0)
reflected_ray = ray(comps.over_point, comps.reflectv)
c = self.color(reflected_ray, bounces - 1)
return c * comps.object.material.reflective
pass
def is_shadowed(self, p, light):
v = light.position - p

@ -0,0 +1,89 @@
from math import pi
from raytracer.shapes import sphere
from raytracer.matrix import identity_matrix, view_transform
from raytracer.vector import color, point, vector
from raytracer.materials import material
from raytracer.patterns import stripe_pattern, gradient, ring, checkerboard
from raytracer.world import world
from raytracer.camera import camera
from raytracer.lights import point_light
from raytracer.colors import rainbow
pattern = stripe_pattern(rainbow.blue, rainbow.green)
pattern.transform = identity_matrix.scaling(0.1, 0.1, 0.1)
floor = sphere()
floor.transform = identity_matrix.scaling(10, 0.01, 10)
floor.material = material()
floor.material.specular = 0
floor.material.pattern = pattern
floor.material.reflective = 0.3
left_wall = sphere()
left_wall.transform = (
identity_matrix.translation(0, 0, 5)
.rotation_y(-pi / 4)
.rotation_x(pi / 2)
.scaling(10, 0.01, 10)
)
left_wall.material = floor.material
right_wall = sphere()
right_wall.transform = (
identity_matrix.translation(0, 0, 5)
.rotation_y(pi / 4)
.rotation_x(pi / 2)
.scaling(10, 0.01, 10)
)
right_wall.material = material()
right_wall.material.specular = 0
right_wall.material.pattern = checkerboard(rainbow.blue, rainbow.green)
right_wall.material.pattern.transform = (
identity_matrix.translation(0, 0, 5)
.rotation_y(pi / 4)
.rotation_x(pi / 2)
.scaling(0.05, 0.05, 0.05)
)
middle = sphere()
middle.transform = identity_matrix.translation(-0.5, 1, 0.5)
middle.material = material()
middle.material.pattern = pattern
middle.material.diffuse = 0.7
middle.material.specular = 0.3
right = sphere()
right.transform = identity_matrix.translation(1.5, 0.5, -0.5).scaling(0.5, 0.5, 0.5)
right.material = material()
right.material.reflective = 0.5
right.material.pattern = gradient(rainbow.orange, rainbow.yellow)
right.material.pattern.transform = identity_matrix.translation(-1, 0, 0).scaling(
2, 1, 1
)
right.material.diffuse = 0.7
right.material.specular = 0.3
left = sphere()
left.transform = identity_matrix.translation(-1.5, 0.33, -0.75).scaling(
0.33, 0.33, 0.33
)
left.material = material()
left.material.pattern = ring(rainbow.grass, rainbow.red)
left.material.pattern.transform = identity_matrix.translation(0.5, 0, 0).scaling(
0.2, 0.2, 0.2
)
left.material.diffuse = 0.7
left.material.specular = 0.3
w = world()
w.objects = [floor, left_wall, right_wall, middle, right, left]
w.lights = [point_light(point(-10, 10, -15), color(1, 1, 1))]
c = camera(800, 400, pi / 3)
c.transform = view_transform(point(0.33, 1.5, -5), point(0, 1, 0), vector(0, 1, 0))
image = c.render(w)
image.ppm("pictures/reflections")

@ -13,6 +13,7 @@ def test_material():
assert m.diffuse == 0.9
assert m.specular == 0.9
assert m.shininess == 200
assert m.reflective == 0.0
def test_material_with_pattern():

@ -1,4 +1,4 @@
from raytracer.shapes import test_shape, plane
from raytracer.shapes import test_shape, plane, computations
import math
from pytest import approx
from raytracer.vector import ray, point, vector
@ -98,3 +98,14 @@ def test_intersect_a_ray_below_the_plane():
assert len(xs) == 1
assert xs[0].t == 1
assert xs[0].object == p
def test_reflect_on_a_plane():
p = plane()
r = ray(point(0, 1, -1), vector(0, -math.sqrt(2) / 2, math.sqrt(2) / 2))
intersections = p.intersections(r)
comps = computations(intersections[0], r)
assert (
approx(comps.reflectv.asTuple())
== vector(0, 0.707106781, 0.707106781).asTuple()
)

@ -1,8 +1,9 @@
import math
from pytest import approx
from raytracer.lights import point_light
from raytracer.world import world
from raytracer.shapes import computations, intersection, sphere
from raytracer.shapes import computations, intersection, sphere, plane
from raytracer.vector import point, color, vector, ray
from raytracer.matrix import scaling, translation
@ -106,3 +107,44 @@ def test_shade_when_given_intersection_in_shadow():
comps = computations(i, r)
c = w.shade(comps)
assert c == color(0.1, 0.1, 0.1)
def test_reflected_color_for_a_nonreflective_material():
w = world()
r = ray(point(0, 0, 0), vector(0, 0, 1))
w.objects[1].material.ambient = 1
comps = computations(intersection(1, w.objects[1]), r)
c = w.reflected_color(comps)
assert c == color(0, 0, 0)
def test_reflected_color_for_a_reflective_material():
w = world()
p = plane()
p.material.reflective = 0.5
p.transform = translation(0, -1, 0)
w.objects.append(p)
r = ray(point(0, 0, -3), vector(0, -math.sqrt(2) / 2, math.sqrt(2) / 2))
comps = computations(intersection(math.sqrt(2), w.objects[2]), r)
c = w.reflected_color(comps)
assert approx(c.rgb) == color(0.1903305, 0.2379132, 0.1427479).rgb
def test_shade_for_a_reflective_material():
w = world()
p = plane()
p.material.reflective = 0.5
p.transform = translation(0, -1, 0)
w.objects.append(p)
r = ray(point(0, 0, -3), vector(0, -math.sqrt(2) / 2, math.sqrt(2) / 2))
comps = computations(intersection(math.sqrt(2), w.objects[2]), r)
c = w.shade(comps)
assert approx(c.rgb) == color(0.876756, 0.9243386, 0.8291733).rgb

@ -80,7 +80,7 @@ w = world()
w.objects = [floor, left_wall, right_wall, middle, right, left]
w.lights = [point_light(point(-10, 10, -15), color(1, 1, 1))]
c = camera(800, 400, pi / 3)
c = camera(400, 300, pi / 3)
c.transform = view_transform(point(0.33, 1.5, -5), point(0, 1, 0), vector(0, 1, 0))
image = c.render(w)

Loading…
Cancel
Save