def trace(self, origin, direction, depth=0): intersection = self._intersector.intersection(origin, direction) if depth >= self._options['max_depth'] or not intersection: # no intersection detected or we have reached max recursive depth for relfection or refraction if self._options['envmap']: return self._scene.envmap(origin, direction) return Vector3(0.2, 0.7, 0.8) refraction = self._ray_refractor.calculate_refraction(origin, direction, intersection, depth) reflection = self._ray_reflector.calculate_reflection(origin, direction, intersection, depth) light_intensity, specular_light_intensity = self._light_tracer.calculate_intensity(origin, direction, intersection, depth) # resut vecotr - it's a material color result_vector = intersection.material.diffuse_color # apply light result_vector = result_vector * light_intensity * intersection.material.albedo[0] # add specular intensity result_vector += Vector3(1, 1, 1) * specular_light_intensity * intersection.material.albedo[1] # add reflection result_vector += reflection * intersection.material.albedo[2] # add refraction result_vector += refraction * intersection.material.albedo[3] return result_vector
def calculate_reflection(self, origin, direction, intersection, depth): if not self._options['reflect']: return Vector3() reflect_dir = self._reflect(direction, intersection.normal).normalize() reflect_origin = utils.build_origin(intersection.point, reflect_dir, intersection.normal) return self._tracer.trace(reflect_origin, reflect_dir, depth + 1)
def cross(v1: Vector3, v2: Vector3) -> Vector3: x = v1._coordinates[1] * v2._coordinates[2] - v1._coordinates[ 2] * v2._coordinates[1] y = v1._coordinates[2] * v2._coordinates[0] - v1._coordinates[ 0] * v2._coordinates[2] z = v1._coordinates[0] * v2._coordinates[1] - v1._coordinates[ 1] * v2._coordinates[0] return Vector3(x, y, z)
def envmap(self, origin, direction): width, height = self._envmap.width, self._envmap.height direction = direction.normalize() x = int((math.atan2(direction.z, direction.x) / (2 * math.pi) + 0.5) * width) y = int(math.acos(direction.y) / math.pi * height) r, g, b = self._envmap.getpixel((x, y)) return Vector3(r / 255, g / 255, b / 255)
def render(self, scene): start = time.time() frame = self._create_frame() total_count = self._width * self._height proceesed_count = 0 percent = 0 ray_tracer = RayTracer(scene, self._options) print('RENDERING: [', end='', flush=True) for j in range(self._height): y = -(2 * (j + 0.5) / self._height - 1) * math.tan(self._fov_hor / 2) for i in range(self._width): x = (2 * (i + 0.5) / self._width - 1) * math.tan( self._fov_vert / 2) origin = Vector3(0, 0, 0) direction = Vector3(x, y, -1).normalize() vector = ray_tracer.trace(origin, direction) frame[i + j * self._width] = self._cast_vector_to_rgb_tuple(vector) proceesed_count += 1 if ((proceesed_count / total_count) * 100) // 10 > percent: percent += 1 print('#', end='', flush=True) print(']') print('[RENDER FINISHED options=%s] took: %.2f sec.' % (self._options, time.time() - start)) return Frame(self._width, self._height, frame)
def refract(v, normal, refractive_index): cos_v = -max(-1.0, min(1.0, v * normal)) etav, etat = 1, refractive_index if cos_v < 0: cos_v = -cos_v etav, etat = etat, etav normal = -normal eta = etav / etat k = 1 - eta * eta * (1 - cos_v**2) if k < 0: res_vector = Vector3(1, 0, 0) else: res_vector = v * eta + normal * (eta * cos_v - math.sqrt(k)) return res_vector