Dispersion through a PrismΒΆ
This demo demonstrates the dispersion of a white light source through a prism. Note, it may take a very long time to generate adequate samples for this demonstration.
# External module imports
import pickle
from math import tan, pi
import matplotlib.pyplot as plt
# Raysect imports
from raysect.primitive import Intersect, Subtract, Box, Cylinder, Sphere
from raysect.optical import World, Node, Point3D, translate, rotate, d65_white, ConstantSF
from raysect.optical.observer import PinholeCamera, RGBPipeline2D, RGBAdaptiveSampler2D
from raysect.optical.material import Lambert
from raysect.optical.material.emitter import UniformSurfaceEmitter
from raysect.optical.library import schott, RoughIron
# Utility method to construct a glass prism from CSG operations
def equilateral_prism(width=1.0, height=1.0, parent=None, transform=None, material=None):
half_width = width / 2
mid_point = half_width * tan(60/180*pi) / 2
centre = Box(Point3D(-half_width * 1.001, 0, 0), Point3D(half_width * 1.001, height, width))
left = Box(
Point3D(0, -height * 0.001, -width * 0.001),
Point3D(width, height * 1.001, 2 * width),
transform=translate(half_width, 0, 0) * rotate(30, 0, 0)
)
right = Box(
Point3D(-width, -height * 0.001, -width * 0.001),
Point3D(0.0, height * 1.001, 2 * width),
transform=translate(-half_width, 0, 0) * rotate(-30, 0, 0)
)
csg_prism = Subtract(
Subtract(centre, left),
right,
parent=parent,
transform=transform * translate(0, 0, -mid_point),
material=material
)
return csg_prism
# Utility method to construct a box with a slit emitting white light
def light_box(parent, transform=None):
# Notice that this function is creating and returning a parent node which holds references
# to the underlying primitives.
node = Node(parent=parent, transform=transform)
outer = Box(Point3D(-0.01, 0, -0.05), Point3D(0.01, 0.15, 0.0))
slit = Box(Point3D(-0.0015, 0.03, -0.045), Point3D(0.0015, 0.12, 0.0001))
Subtract(outer, slit, parent=node, material=Lambert(reflectivity=ConstantSF(0.1)))
Box(
Point3D(-0.0015, 0.03, -0.045),
Point3D(0.0015, 0.12, -0.04),
parent=node,
material=UniformSurfaceEmitter(d65_white, 250)
)
return node
world = World()
# construct diffuse floor surface
floor = Box(Point3D(-1000, -0.1, -1000), Point3D(1000, 0, 1000), parent=world, material=Lambert())
# construct prism from utility method
prism = equilateral_prism(0.06, 0.15, parent=world, material=schott("SF11"), transform=translate(0, 0.0 + 1e-6, -0.01))
# Curved target screen for collecting rainbow light
stand = Intersect(
Box(Point3D(-10, -10, -10), Point3D(10, 10, 0)),
Subtract(
Cylinder(0.21, 0.15),
Cylinder(0.20, 0.16, transform=translate(0, 0, -0.005)),
transform=rotate(0, 90, 0)
),
transform=translate(0.0, 1e-6, 0.0),
parent=world,
material=schott("N-BK7") # RoughIron(0.25)
)
surface = Intersect(
Box(Point3D(-10, -10, -10), Point3D(10, 10, -0.015)),
Subtract(
Cylinder(0.1999, 0.12, transform=translate(0, 0, 0.015)),
Cylinder(0.1998, 0.13, transform=translate(0, 0, 0.010)),
transform=rotate(0, 90, 0)
),
parent=stand,
material=Lambert(ConstantSF(1.0))
)
# construct main collimated light source
prism_light = light_box(parent=world, transform=rotate(-35.5, 0, 0) * translate(0.10, 0, 0) * rotate(90, 0, 0))
# background light source
top_light = Sphere(0.25, parent=world, transform=translate(-1, 2, 1), material=UniformSurfaceEmitter(d65_white, scale=5))
# Give the prism a high importance to ensure adequate sampling
prism.material.importance = 9
rgb = RGBPipeline2D()
rgb.display_sensitivity = 2.0
sampler = RGBAdaptiveSampler2D(rgb, min_samples=500)
# create and setup the camera
camera = PinholeCamera((1920, 1080), fov=45, parent=world, pipelines=[rgb], frame_sampler=sampler)
camera.transform = translate(0, 0.075, -0.05) * rotate(180, -45, 0) * translate(0, 0, -0.75)
camera.ray_importance_sampling = True
camera.ray_important_path_weight = 0.75
camera.ray_max_depth = 500
camera.ray_extinction_prob = 0.01
camera.spectral_bins = 32
camera.spectral_rays = 32
camera.pixel_samples = 250
# start ray tracing
plt.ion()
for p in range(0, 1000):
print("Rendering pass {}".format(p+1))
camera.observe()
# save image
rgb.save("prisms_{}.png".format(p+1))
# save pipeline object
with open('prisms_{}.pickle'.format(p+1), 'wb') as f:
pickle.dump(rgb, f)
print()
# display final result
plt.ioff()
rgb.display()
plt.show()