def path_push(path: Path, ray: Ray): # update stuff appropriately if path.direction == Direction.STORAGE.value: # don't change anything, this is just a stack. this reduces wasted calculation and preserves first-vertex values pass elif path.ray is None: # this shouldn't come up pass else: G = geometry_term(path.ray, ray) path.ray.direction = unit(ray.origin - path.ray.origin) if path.ray.prev is None: # pushing onto stack of 1, just propagate p and color (?) ray.color = path.ray.color * G ray.p = path.ray.p * G else: # pushing onto stack of 2 or more, do some work brdf = BRDF_function(path.ray.material, -1 * path.ray.prev.direction, path.ray.normal, path.ray.direction, path.direction) ray.color = path.ray.local_color * path.ray.color * brdf * G ray.p = path.ray.p * G * BRDF_pdf(path.ray.material, -1 * path.ray.prev.direction, path.ray.normal, path.ray.direction, path.direction) ray.bounces = path.ray.bounces + 1 # store new ray ray.prev = path.ray path.ray = ray
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 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 path_push(path: Path, ray: Ray): # update stuff appropriately G = geometry_term(path.ray, ray) path.ray.direction = unit(ray.origin - path.ray.origin) if path.ray.prev is None: # pushing onto stack of 1, just propagate p and color (?) ray.color = path.ray.color * G ray.p = path.ray.p * G else: # pushing onto stack of 2 or more, do some work brdf = brdf_function(path.ray.material, -1 * path.ray.prev.direction, path.ray.normal, path.ray.direction, path.direction) ray.color = path.ray.local_color * path.ray.color * brdf * G ray.p = path.ray.p * G * brdf_pdf( path.ray.material, -1 * path.ray.prev.direction, path.ray.normal, path.ray.direction, path.direction) ray.bounces = path.ray.bounces + 1 # store new ray ray.prev = path.ray path.ray = ray
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