-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSceneObjects.py
More file actions
174 lines (140 loc) · 6.27 KB
/
Copy pathSceneObjects.py
File metadata and controls
174 lines (140 loc) · 6.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
from vector import Vec3
from copy import deepcopy
from math import sqrt, trunc, pi
class Triangle:
"""Tuple of 3 points (Vec3's)"""
# TODO Add bounding box stuff
def __init__(self, pt_a, pt_b, pt_c, input_normal=None, input_color=Vec3(255, 255, 255),
reflectiveness=0, shininess=8.0, parent=None):
self.A = pt_a
self.B = pt_b
self.C = pt_c
self.reflectiveness = reflectiveness
self.shininess = shininess
self.parent = parent
if input_normal is not None:
self.normal = input_normal
else:
self.calc_normal()
if input_color is not None:
self.diffuse = input_color
def get_color(self, point_hit=None):
return self.diffuse
def calc_normal(self):
vec_ab = self.B - self.A
vec_ac = self.C - self.A
# Reversed from normal implementations due to reversed coordinate system
cross_vec = vec_ab.cross(vec_ac)
self.normal = cross_vec.normalize()
# print(vec_ab, vec_ac, cross_vec, self.normal)
def get_normal(self, pos=None):
return self.normal
def __str__(self):
pts = f"Tri Pts = {self.A},{self.B},{self.C}\t"
norm = f"Tri Normal = {self.normal}\t"
info = f"Color = {self.diffuse}, Reflectiveness = {self.reflectiveness}"
return pts + norm + info
def __repr__(self):
pts = f"Tri Pts = {self.A},{self.B},{self.C}\t"
norm = f"Tri Normal = {self.normal}\t"
info = f"Color = {self.diffuse}, Reflectiveness = {self.reflectiveness}"
return pts + norm + info
def intersect(self, ray_to_test):
"""Based on Ray-Triangle Intersection algorithm in Shirley and Marschner, pg 77-81"""
direction = ray_to_test.direction
# Pre-check for normal pointed towards ray's origin
if self.normal.dot(direction) > 0:
return False
# Saving to variables to prevent coding too much
a_ = self.A.x - self.B.x
b_ = self.A.y - self.B.y
c_ = self.A.z - self.B.z
d_ = self.A.x - self.C.x
e_ = self.A.y - self.C.y
f_ = self.A.z - self.C.z
g_, h_, i_ = ray_to_test.direction.x, ray_to_test.direction.y, ray_to_test.direction.z
j_, k_, l_ = self.A.x - ray_to_test.origin.x, \
self.A.y - ray_to_test.origin.y, \
self.A.z - ray_to_test.origin.z
# Saving More variables
ei_min_hf = e_ * i_ - h_ * f_
gf_min_di = g_ * f_ - d_ * i_
dh_min_eg = d_ * h_ - e_ * g_
ak_min_jb = a_ * k_ - j_ * b_
jc_min_al = j_ * c_ - a_ * l_
bl_min_kc = b_ * l_ - k_ * c_
m_denominator = a_ * ei_min_hf + b_ * gf_min_di + c_ * dh_min_eg
if m_denominator == 0.0:
return False
# First, compute t
t_of_hit = -(f_ * ak_min_jb + e_ * jc_min_al + d_ * bl_min_kc) / m_denominator
if t_of_hit < ray_to_test.initial_offset or t_of_hit > ray_to_test.nearest_hit_distance:
return False
# Next, try gamma
gamma = (i_ * ak_min_jb + h_ * jc_min_al + g_ * bl_min_kc) / m_denominator
if gamma < 0 or gamma > 1:
return False
# Then, try beta
beta = (j_ * ei_min_hf + k_ * gf_min_di + l_ * dh_min_eg) / m_denominator
if beta < 0 or beta > 1:
return False
# Finally, calculate alpha (1 - gamma - beta)
alpha = 1 - gamma - beta
if alpha < 0 or alpha > 1:
return False
ray_to_test.nearest_hit_distance = t_of_hit
return True
class Sphere:
def __init__(self, pos, radius, color=Vec3(255, 255, 255), reflectiveness=0, shininess=8.0, parent=None):
self.pos = pos
self.radius = radius
self.diffuse = color
self.reflectiveness = reflectiveness
self.shininess = shininess
self.parent = parent
def get_color(self, point_hit):
return self.diffuse
def get_normal(self, pos):
return (pos - self.pos)/self.radius
def __repr__(self):
return f"Sphere: Pos = {self.pos}\t info = Color = {self.diffuse}, Reflectiveness = {self.reflectiveness}"
def intersect(self, ray_to_test):
"""Based on Ray-Sphere Intersection algorithm in Shirley and Marschner, pg 76-77"""
# Put in terms like the book uses
e = ray_to_test.origin # Vec 3
d = ray_to_test.direction # Vec3
c = self.pos # Vec3
r = self.radius # Float
e_min_c = e - c # Vec3
discriminant = pow((d.dot(e_min_c)),2) - d.dot(d)*((e_min_c).dot(e_min_c) - pow(r, 2))
if discriminant < 0:
return False # no real solution
elif -0.0000001 < discriminant < 0.0000001:
t_of_hit = -d.dot(e_min_c)/d.dot(d)
if t_of_hit < ray_to_test.nearest_hit_distance:
ray_to_test.nearest_hit_distance = t_of_hit
return True
else:
rest_of_equ = -d.dot(e_min_c) / d.dot(d)
sqrt_disc = sqrt(discriminant)
smaller_t = rest_of_equ - sqrt_disc
larger_t = rest_of_equ + sqrt_disc
if ray_to_test.initial_offset < smaller_t < ray_to_test.nearest_hit_distance:
ray_to_test.nearest_hit_distance = smaller_t
return True
elif ray_to_test.initial_offset < larger_t < ray_to_test.nearest_hit_distance:
ray_to_test.nearest_hit_distance = larger_t
return True
else:
return False
class CheckeredSphere(Sphere):
def get_color(self, point_hit):
"""Based off of James Bowman's ray tracer at https://github.com/jamesbowman/raytrace/blob/master/rt3.py"""
checker = trunc(point_hit.x /(self.radius / 9999) * 10) % 2 == trunc(point_hit.z / (self.radius / 9999) * 10) % 2
return checker * self.diffuse + (1 - checker) * (Vec3(255, 255, 255) - self.diffuse)
class PointLight:
""" Holds pos, color, and intensity of a "light" """
def __init__(self, position, color, intensity):
self.position = position
self.color = color
self.intensity = intensity