def make_ray(self, i, j): # was having difficulty making a good mass-ray-generation routine, settled on on-demand # speed is fine and it'll be good for future adaptive sampling stuff n1 = np.random.random() n2 = np.random.random() origin = self.origin + self.dx_dp * (j + n1) + self.dy_dp * (i + n2) ray = Ray(origin, unit(self.focal_point - origin)) ray.i = i ray.j = j return ray
def _trace_ray(self, ray, calc_color, tol, tracing_depth): """ :param ray: object of type Ray :param calc_color: function, which takes Hit object as parameter and returns color of object in this point :return: result of execution calc_color function """ min_dist = 1e8 min_hit = None for obj in self.scene.objects: hit = obj.intersect(ray, tol) if hit is None: continue dist = hit.distance() if dist < min_dist: min_dist = dist min_hit = hit obj_color = calc_color(min_hit) res_shading = np.array([0.0, 0.0, 0.0]) if min_hit is not None: # Tracing shadow rays for light in self.scene.lights: shadow_ray = Ray(min_hit.point, light.origin - min_hit.point) shadow_hit = self._trace_shadow_ray(shadow_ray, tol) if shadow_hit is None: res_shading += light.get_intensity(shadow_ray) * obj_color * max(0, np.dot(min_hit.normal, shadow_ray.direction)) # Tracing reflection ray if tracing_depth > 0: reflection_ray_dir = ray.direction - 2.0 * (np.dot(ray.direction, min_hit.normal)) * min_hit.normal reflection_ray = Ray(min_hit.point, reflection_ray_dir) reflection_shading = self._trace_ray(reflection_ray, calc_color, tol, tracing_depth - 1) res_shading += REFLECTION_COEF * reflection_shading return res_shading
def isValidMove(self, A, B): # get the direction of the movement ray D = B - A dist = LA.norm(D) # trace a ray to get the hit record towards scene objects ray = Ray(A, D / dist) d, hr = self.trace(ray) # we can move if we don't hit a wall return dist < d
def generate_light_ray(box: Box): light_index = np.random.randint(0, len(box.lights)) light = box.lights[light_index] light_origin = light.sample_surface() x, y, z = local_orthonormal_system(light.normal) light_direction = random_hemisphere_uniform_weighted(x, y, z) ray = Ray(light_origin, light_direction) ray.color = light.color ray.local_color = light.color ray.normal = light.normal ray.p = 1 / (2 * np.pi * light.surface_area) return ray
def get_ray(self, point): """ :param point: point of type tuple (x: float, y: float), where -1 <= x, y <= 1 :return: """ fov_x, fov_y = self.fov_angle d = 1 / np.tan(fov_x / 2) x, y = point u, v, w = self.direction aspect = fov_y / fov_x dir = x * u + aspect * y * v + d * w normalized_direction = dir / np.sqrt(np.dot(dir, dir)) return Ray(self.origin, normalized_direction)
def generate_light_ray(emitters): light = emitters[np.random.randint(0, len(emitters))] light_origin = light.sample_surface() x, y, z = local_orthonormal_system(light.normal) light_direction = random_hemisphere_uniform_weighted(x, y, z) ray = Ray(light_origin, light_direction) ray.color = light.color ray.local_color = light.color ray.normal = light.normal # this seems made up ray.p = 1 / (2 * np.pi * light.surface_area) return ray
def extend_path(path: Path, root: Box): triangle, t = traverse_bvh(root, path.ray) if triangle is not None: # generate new ray new_origin = path.ray.origin + path.ray.direction * t new_direction = BRDF_sample(triangle.material, -1 * path.ray.direction, triangle.normal, path.direction) new_ray = Ray(new_origin, new_direction) new_ray.normal = triangle.normal new_ray.material = triangle.material new_ray.local_color = triangle.color if triangle.emitter: path.hit_light = True path_push(path, new_ray) # if path_health_check(path): return True else: return False
def visibility_test(boxes, triangles, ray_a: Ray, ray_b: Ray): delta = ray_b.origin - ray_a.origin least_t = np.linalg.norm(delta) direction = delta / least_t if np.dot(ray_a.normal, direction) <= 0 or np.dot(ray_b.normal, -1 * direction) <= 0: return False test_ray = Ray(ray_a.origin, direction) stack = [boxes[0]] while stack: box = stack.pop() if box.right == 0: if bvh_hit_inner(test_ray, box, least_t): stack.append(boxes[box.left]) stack.append(boxes[box.left + 1]) else: if visibility_hit_leaf(test_ray, box, triangles[box.left:box.right], least_t): return False return True
def extend_path(path: Path, boxes, triangles): triangle, t = traverse_bvh(boxes, triangles, path.ray) if triangle is not None: # generate new ray new_origin = path.ray.origin + path.ray.direction * t new_direction = brdf_sample(triangle.material, -1 * path.ray.direction, triangle.normal, path.direction) new_ray = Ray(new_origin, new_direction) new_ray.normal = triangle.normal new_ray.material = triangle.material new_ray.local_color = triangle.color if triangle.emitter: path.hit_light = True path_push(path, new_ray) return True else: return False
def extend_path(path, root, path_direction): for i in range(MAX_BOUNCES): ray = path[-1] triangle, t = traverse_bvh(root, ray) if triangle is not None: # generate new ray # new vectors origin = ray.origin + ray.direction * t direction = BRDF_sample(triangle.material, -1 * ray.direction, triangle.normal, path_direction) new_ray = Ray(origin, direction) # store info from triangle new_ray.normal = triangle.normal new_ray.material = triangle.material new_ray.local_color = triangle.color if path_direction == Direction.FROM_CAMERA.value and triangle.emitter: new_ray.hit_light = True # probability, weight, and color updates G = geometry_term(ray, new_ray) if i == 0: # only need to multiply by G because p of this direction is already stored at creation new_ray.p = ray.p * G # same deal, "brdf" of source is already in ray.color new_ray.color = ray.color * G new_ray.G = G else: # so the idea here is that each vertex has information about everything up to it but not including it, # because we can't be sure of anything about the final bounce until we know the joining vertex bounce_p = BRDF_pdf(ray.material, -1 * path[-2].direction, ray.normal, ray.direction, path_direction) new_ray.p = ray.p * G * bounce_p new_ray.color = ray.color * ray.local_color * G * BRDF_function( ray.material, -1 * path[-2].direction, ray.normal, ray.direction, path_direction) new_ray.G = G ray.local_p = bounce_p path.append(new_ray) else: break
def visibility_test(root: Box, ray_a: Ray, ray_b: Ray): delta = ray_b.origin - ray_a.origin least_t = np.linalg.norm(delta) direction = delta / least_t if np.dot(ray_a.normal, direction) <= 0 or np.dot(ray_b.normal, -1 * direction) <= 0: return False test_ray = Ray(ray_a.origin, direction) stack = BoxStack() stack.push(root) while stack.size: box = stack.pop() if box.left is not None and box.right is not None: if bvh_hit_inner(test_ray, box, least_t): stack.push(box.left) stack.push(box.right) else: hit, t = bvh_hit_leaf(test_ray, box, least_t) if hit is not None and t < least_t: return False return True
def ray_that_misses(): return Ray(ONES * 5, UNIT_Y)
def ray_inside_box(): return Ray(point(0.5, 0.5, 0.5), UNIT_Z)
def ray_that_hits(): # hits object in center return Ray(point(0.2, 0.2, 5), -1 * UNIT_Z)
def ray_that_barely_hits(): # hits object at origin return Ray(UNIT_Z * 5, -1 * UNIT_Z)