Skip to content
This repository was archived by the owner on Jan 22, 2023. It is now read-only.

Commit 53af35b

Browse files
committed
cleaner imports, a new module 'camera', refactored default_scenes
1 parent ba2b243 commit 53af35b

File tree

11 files changed

+200
-205
lines changed

11 files changed

+200
-205
lines changed

camera.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
2+
from utils import *
3+
4+
def camera_rotmat(direction, up=(0, 0, 1)):
5+
6+
# TODO camera roll
7+
8+
direction = np.array(direction)
9+
up = np.array(up)
10+
11+
right = np.cross(direction, up)
12+
up = np.cross(right, direction)
13+
14+
rotmat = np.vstack((right, -up, direction))
15+
16+
return normalize(rotmat).transpose()
17+
18+
def rotmat_tilt_camera(xa, ya):
19+
20+
rm2d = lambda a : np.array([[np.cos(a), -np.sin(a)], [np.sin(a), np.cos(a)]])
21+
22+
rot3dy = np.identity(3)
23+
rot3dy[1:, 1:] = rm2d(ya)
24+
25+
rot3dx = np.identity(3)
26+
27+
rot3dx[[[0], [2]], [0, 2]] = rm2d(xa)
28+
29+
return np.dot(rot3dy, rot3dx)
30+
31+
def camera_rays(wh, flat=False, wfov=60, direction=(0, 1, 0), up=(0, 0, 1)):
32+
33+
w, h = wh
34+
wfov = wfov/180.0*np.pi
35+
aspect = h / float(w)
36+
hfov = wfov * aspect
37+
38+
rotmat = camera_rotmat(direction, up)
39+
40+
#tilt = rotmat_tilt_camera(0.3,0.4)
41+
#rotmat = np.dot(rotmat, tilt)
42+
43+
if flat:
44+
ra = np.tan(wfov * 0.5)
45+
xr = np.linspace(-ra, ra, w)
46+
yr = np.linspace(-ra*aspect, ra*aspect, h)
47+
X, Y = np.meshgrid(xr, yr)
48+
Z = np.ones(X.shape)
49+
else:
50+
pixel_angle = float(wfov)/w
51+
xa = (np.arange(0, w)+0.5)*pixel_angle - wfov/2.0
52+
ya = (np.arange(0, h)+0.5)*pixel_angle - hfov/2.0
53+
Xa, Ya = np.meshgrid(xa, ya)
54+
55+
X = np.sin(Xa)*np.cos(Ya)
56+
Z = np.cos(Xa)*np.cos(Ya)
57+
Y = np.sin(Ya)
58+
59+
N = w*h
60+
vecs = np.dstack((X, Y, Z))
61+
vecs = np.reshape(vecs, (N, 3)).transpose()
62+
vecs = np.dot(rotmat, vecs).transpose()
63+
vecs = np.reshape(vecs, (h, w, 3))
64+
65+
if flat:
66+
vecs = normalize(vecs)
67+
68+
return vecs
69+
70+
def quasi_random_direction_sample(n, hemisphere=True):
71+
"""
72+
"Vogel's method / Fermat's spiral",
73+
adapted from http://blog.marmakoide.org/?p=1
74+
75+
It appears that, in addition to the discussion in the above blog,
76+
the first n of 2n of these points are evenly distributed in the real
77+
projective space RP^2 which is very nice.
78+
79+
Now ANY diffusion hemisphere is sampled with a regular even grid!
80+
"""
81+
82+
if hemisphere:
83+
n = n*2
84+
85+
golden_angle = np.pi * (3 - np.sqrt(5))
86+
theta = golden_angle * np.arange(n)
87+
z = np.linspace(1 - 1.0 / n, 1.0 / n - 1, n)
88+
radius = np.sqrt(1 - z * z)
89+
90+
points = np.zeros((n, 3))
91+
points[:, 0] = radius * np.cos(theta)
92+
points[:, 1] = radius * np.sin(theta)
93+
points[:, 2] = z
94+
95+
if hemisphere:
96+
n = n/2
97+
points = points[:n, :]
98+
99+
return points
100+
101+
def random_dof_sample():
102+
103+
insideDof = lambda x, y: (x**2 + y**2) < 1.0
104+
105+
while True:
106+
dofx = (np.random.rand()-0.5)*2.0
107+
dofy = (np.random.rand()-0.5)*2.0
108+
if insideDof( dofx, dofy ):
109+
return (dofx, dofy)

objects/octree.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from tracer import Tracer
2-
from utils import normalize
32
import numpy
43

54
class Octree(Tracer):

objects/polyhedra.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from objects import ConvexIntersection, HalfSpaceComponent, LayerComponent, FixedConvexIntersection
2-
import math
32
from utils import vec_norm
3+
import math
44

55
class Parallelepiped(ConvexIntersection):
66

objects/sphere.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from tracer import Tracer
22
from objects import ConvexIntersection
33
import numpy
4-
import math
54
from transformations import Affine
65

76
class Sphere(Tracer):
@@ -12,7 +11,7 @@ def __init__(self, pos, R):
1211

1312
def surface_area(self):
1413
self._check_no_distortion()
15-
return 4.0 * math.pi * self.R**2
14+
return 4.0 * numpy.pi * self.R**2
1615

1716
def random_surface_point_and_normal(self):
1817
self._check_no_distortion()

renderer.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
from accelerator import Accelerator
33
import numpy as np
4+
import camera
45
import utils
56
import itertools
67
import math
@@ -80,7 +81,7 @@ def _init_misc(self):
8081
self.vec_param_buf = np.zeros((self.max_broadcast_vecs, 4), dtype=np.float32)
8182

8283
# Randomization init
83-
self.qdirs = utils.quasi_random_direction_sample(self.scene.samples_per_pixel)
84+
self.qdirs = camera.quasi_random_direction_sample(self.scene.samples_per_pixel)
8485
self.qdirs = np.random.permutation(self.qdirs)
8586

8687
def _init_camera_and_image(self):
@@ -273,13 +274,13 @@ def tent_filter_transformation(x):
273274
thetax = (sx-0.5)*self.pixel_angle*(1.0+overlap)
274275
thetay = (sy-0.5)*self.pixel_angle*(1.0+overlap)
275276

276-
dofx, dofy = utils.random_dof_sample()
277+
dofx, dofy = camera.random_dof_sample()
277278

278279
dof_pos = (dofx * self.rotmat[:, 0] + dofy * self.rotmat[:, 1]) * scene.camera_dof_fstop
279280

280281
sharp_distance = scene.camera_sharp_distance
281282

282-
tilt = utils.rotmat_tilt_camera(thetax, thetay)
283+
tilt = camera.rotmat_tilt_camera(thetax, thetay)
283284
mat = np.dot(np.dot(self.rotmat, tilt), self.rotmat.transpose())
284285
mat4 = np.zeros((4, 4))
285286
mat4[0:3, 0:3] = mat

scene.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
2-
from objects import *
3-
import utils
41
import numpy as np
5-
import tracer
6-
from spectrum import Spectrum
2+
import camera
73

84
class Object:
95
"""
@@ -25,7 +21,7 @@ class Scene:
2521
"""
2622

2723
def get_camera_rotmat(self):
28-
return utils.camera_rotmat(self.camera_direction, self.camera_up)
24+
return camera.camera_rotmat(self.camera_direction, self.camera_up)
2925

3026
def get_objects(self, name):
3127
return [obj for obj in self.objects if obj.name == name]
@@ -46,13 +42,11 @@ def direct_camera_towards(self, target):
4642
self.camera_sharp_distance = np.linalg.norm(self.camera_direction)
4743

4844
def get_camera_rays(self):
49-
return utils.camera_rays(self.image_size, self.camera_flat_ccd, \
45+
return camera.camera_rays(self.image_size, self.camera_flat_ccd, \
5046
self.camera_fov, self.camera_direction, self.camera_up)
5147

5248
def get_number_of_camera_rays(self):
5349
return self.get_camera_rays().size / 3
5450

5551
def add_object(self, tracer, material, name=None):
5652
self.objects.append(Object(tracer, material, name))
57-
58-
from scenes.default_scenes import DefaultBoxScene, DefaultSpectrumBoxScene

scenes/default_scenes.py

Lines changed: 66 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from objects import HalfSpace, Sphere
66
import shader
77

8-
def default_materials():
8+
def default_rgb_materials():
99
# --- Materials
1010
return {\
1111
'default': # "Air" / initial / default material
@@ -48,50 +48,81 @@ def default_materials():
4848
'diffuse': 0.02
4949
}
5050
}
51-
5251

53-
# Default scene
54-
class DefaultBoxScene(Scene):
55-
"""
56-
Default raytracer scene. Override attributes as required
57-
"""
52+
def default_spectrum_materials(spectrum):
5853

59-
# helpers...
54+
materials = default_rgb_materials()
55+
56+
# Have to replace RGB colors by proper spectra...
57+
overrides = {
58+
'green':
59+
{ 'diffuse': spectrum.gaussian(540, 30)*0.65 + 0.3 },
60+
'red':
61+
{ 'diffuse': spectrum.gaussian(670, 30)*0.6 + 0.2 },
62+
'light': # warm yellow-orange-light
63+
{ 'diffuse': 1.0, 'emission': spectrum.black_body(3200)*7.0 },
64+
'sky':
65+
{ 'diffuse': 1.0, 'emission': spectrum.black_body(10000) },
66+
}
6067

61-
@staticmethod
62-
def make_world_box( material, dims, center=(0, 0, 0) ):
63-
return [\
64-
Object(HalfSpace( ( 1, 0, 0), dims[0]-center[0] ), material, 'wall'), \
65-
Object(HalfSpace( (-1, 0, 0), dims[0]+center[0] ), material, 'wall'), \
66-
Object(HalfSpace( ( 0, 1, 0), dims[1]-center[1] ), material, 'wall'), \
67-
Object(HalfSpace( ( 0,-1, 0), dims[1]+center[1] ), material, 'wall'), \
68-
Object(HalfSpace( ( 0, 0, 1), dims[2]-center[2] ), material, 'floor'), \
69-
Object(HalfSpace( ( 0, 0,-1), dims[2]+center[2] ), material, 'ceiling')]
68+
for k, mat in overrides.items():
69+
materials[k] = mat
70+
71+
materials['wax']['volume_absorption'] = \
72+
(1.0 - spectrum.gaussian(670, 100)) * 4.0
73+
74+
# Also make the glass dispersive
75+
materials['glass']['ior'] = spectrum.linear_dispersion_ior(1.5, 60.0)
7076

77+
return materials
78+
79+
def default_settings(scene):
80+
81+
# --- Image settings
82+
scene.image_size = (800, 600)
83+
scene.brightness = 0.3
84+
scene.gamma = 1.8
7185

72-
def initialize_materials(self):
73-
self.materials = default_materials()
86+
# --- Raytracer settings
87+
scene.tent_filter = True
88+
scene.quasirandom = False
89+
scene.samples_per_pixel = 10000
90+
scene.min_bounces = 2
91+
scene.max_bounces = 4
92+
scene.min_russian_prob = 0.15
93+
94+
def make_world_box(material, dims, center=(0, 0, 0) ):
95+
return [\
96+
Object(HalfSpace( ( 1, 0, 0), dims[0]-center[0] ), material, 'wall'), \
97+
Object(HalfSpace( (-1, 0, 0), dims[0]+center[0] ), material, 'wall'), \
98+
Object(HalfSpace( ( 0, 1, 0), dims[1]-center[1] ), material, 'wall'), \
99+
Object(HalfSpace( ( 0,-1, 0), dims[1]+center[1] ), material, 'wall'), \
100+
Object(HalfSpace( ( 0, 0, 1), dims[2]-center[2] ), material, 'floor'), \
101+
Object(HalfSpace( ( 0, 0,-1), dims[2]+center[2] ), material, 'ceiling')]
102+
103+
class DefaultScene(Scene):
74104

75-
def __init__(self):
76-
"""Initialize default scene"""
105+
def __init__(self, shader_class = shader.RgbShader):
106+
default_settings(self)
107+
self.shader = shader_class
77108

78-
# --- Image settings
79-
self.image_size = (800, 600)
80-
self.brightness = 0.3
81-
self.gamma = 1.8
109+
if shader_class == shader.SpectrumShader:
110+
self.spectrum = Spectrum()
111+
self.materials = default_spectrum_materials(self.spectrum)
112+
else:
113+
self.materials = default_rgb_materials()
82114

83-
# --- Raytracer settings
84-
self.tent_filter = True
85-
self.quasirandom = False
86-
self.samples_per_pixel = 10000
87-
self.min_bounces = 2
88-
self.max_bounces = 4
89-
self.min_russian_prob = 0.15
90-
91-
self.initialize_materials()
115+
self.set_up_objects_and_camera()
116+
117+
class BoxScene(DefaultScene):
118+
"""
119+
Default raytracer scene. Override attributes as required
120+
"""
121+
122+
def set_up_objects_and_camera(self):
92123

93124
# --- Objects
94-
self.objects = DefaultBoxScene.make_world_box( 'white', (3, 5, 2), (0, 0, 2) )
125+
self.objects = make_world_box( 'white', (3, 5, 2), (0, 0, 2) )
95126
self.objects[-1].material = "sky" # world box ceiling
96127
self.objects[-2].material = "green" # world box floor
97128
self.root_object = None
@@ -108,38 +139,3 @@ def __init__(self):
108139
camera_target = (0, 2, 0.5)
109140
self.camera_dof_fstop = 0.0
110141
self.direct_camera_towards(camera_target)
111-
112-
self.shader = shader.RgbShader
113-
114-
class DefaultSpectrumBoxScene(DefaultBoxScene):
115-
116-
def __init__(self):
117-
self.spectrum = Spectrum()
118-
DefaultBoxScene.__init__(self)
119-
self.shader = shader.SpectrumShader
120-
121-
def initialize_materials(self):
122-
123-
s = self.spectrum
124-
self.materials = default_materials()
125-
126-
# Have to replace RGB colors by proper spectra...
127-
overrides = {
128-
'green':
129-
{ 'diffuse': s.gaussian(540, 30)*0.65 + 0.3 },
130-
'red':
131-
{ 'diffuse': s.gaussian(670, 30)*0.6 + 0.2 },
132-
'light': # warm yellow-orange-light
133-
{ 'diffuse': 1.0, 'emission': s.black_body(3200)*7.0 },
134-
'sky':
135-
{ 'diffuse': 1.0, 'emission': s.black_body(10000) },
136-
}
137-
138-
for k, mat in overrides.items():
139-
self.materials[k] = mat
140-
141-
self.materials['wax']['volume_absorption'] = \
142-
(1.0 - s.gaussian(670, 100)) * 4.0
143-
144-
# Also make the glass dispersive
145-
self.materials['glass']['ior'] = s.linear_dispersion_ior(1.5, 60.0)

scenes/scene-dev.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from scene import *
2-
from utils import normalize, vec_norm
1+
from scenes.default_scenes import BoxScene
2+
from scene import Object
3+
from objects import *
4+
#from shader import SpectrumShader
35

4-
scene = DefaultSpectrumBoxScene()
5-
#scene = DefaultBoxScene()
6+
#scene = BoxScene(SpectrumShader)
7+
scene = BoxScene()
68

79
for obj in scene.get_objects('wall'): obj.material = 'red'
810
scene.get_object('floor').material = 'white'

0 commit comments

Comments
 (0)