def color(ray: Ray, world: HittableList) -> Vec3: hit_attempt: Optional[HitRecord] = world.hit(ray, 0.0, sys.float_info.max) if hit_attempt is not None: return 0.5 * Vec3(hit_attempt.normal.x + 1, hit_attempt.normal.y + 1, hit_attempt.normal.z + 1) else: unit_direction: Vec3 = ray.direction.unit_vector() t: float = 0.5 * (unit_direction.y + 1) return ((1.0 - t) * UNIT_VEC3) + (t * Vec3(0.5, 0.7, 1.0))
def color(ray: Ray, world: HittableList) -> Vec3: # Some reflected rays hit not at zero but at some near-zero value due to # floating point shennanigans. So we try to compensate for that. hit_attempt: Optional[HitRecord] = world.hit(ray, 0.001, sys.float_info.max) if hit_attempt is not None: target: Vec3 = hit_attempt.p + hit_attempt.normal + random_unit_sphere_point( ) # FIXME mmm recursion # reflector_rate * reflected_color # So in this case, the matterial is a 50% reflector. return 0.5 * color(Ray(hit_attempt.p, target - hit_attempt.p), world) else: unit_direction: Vec3 = ray.direction.unit_vector() t: float = 0.5 * (unit_direction.y + 1) return ((1.0 - t) * UNIT_VEC3) + (t * Vec3(0.5, 0.7, 1.0))
def color(ray: Ray, world: HittableList, depth: int = 0) -> Vec3: # Some reflected rays hit not at zero but at some near-zero value due to # floating point shennanigans. So we try to compensate for that. hit_attempt: Optional[HitRecord] = world.hit(ray, 0.001, sys.float_info.max) if hit_attempt is not None: reflection: ReflectionRecord = hit_attempt.material.scatter( ray, hit_attempt) if depth < 50 and reflection is not None: return reflection.attenuation * color(reflection.scattering, world, depth + 1) else: return Vec3(0, 0, 0) else: unit_direction: Vec3 = ray.direction.unit_vector() t: float = 0.5 * (unit_direction.y + 1) return ((1.0 - t) * UNIT_VEC3) + (t * Vec3(0.5, 0.7, 1.0))
def color(ray: Ray, world: HittableList, depth: int) -> Vec3: # Some reflected rays hit not at zero but at some near-zero value due to # floating point shennanigans. So we try to compensate for that. hit_attempt: Optional[HitRecord] = world.hit(ray, 0.001, sys.float_info.max) if hit_attempt is not None: scattering: ReflectionRecord = hit_attempt.material.scatter(ray, hit_attempt) # FIXME Is it really worthwhile to check if reflection is not None here? # All our Materials assume that a hit has been made, and therefore some # reflection should happen (unless it is Vanta). if depth < 50 and scattering is not None: # Compare this with the hard-coded reflection in 6_matterial. return scattering.attenuation * color(scattering.scattering, world, depth + 1) else: return Vec3(0, 0, 0) else: unit_direction: Vec3 = ray.direction.unit_vector() t: float = 0.5 * (unit_direction.y + 1) return ((1.0 - t) * UNIT_VEC3) + (t * Vec3(0.5, 0.7, 1.0))
depth + 1) else: return Vec3(0, 0, 0) else: unit_direction: Vec3 = ray.direction.unit_vector() t: float = 0.5 * (unit_direction.y + 1) return ((1.0 - t) * UNIT_VEC3) + (t * Vec3(0.5, 0.7, 1.0)) if __name__ == "__main__": width: int = 1200 height: int = 800 sampling_size: int = 10 ppm: PPM = PPM(width, height) spam: List[Hittable] = random_scene(-11, 11, -11, 11) world = HittableList(spam) print(spam) lookfrom: Vec3 = Vec3(9, -2, -1) lookat: Vec3 = Vec3(-4, 1, 0) focus_distance: float = 10.0 aperture: float = 0.1 camera: Camera = PositionableCamera(lookfrom, lookat, Vec3(0, 1, 0), 20, width / height, aperture, focus_distance) for j in range(height - 1, -1, -1): for i in range(width): #print("Tracing on row %s, col %s" % (j, i)) accumulator: Vec3 = Vec3(0, 0, 0)
if __name__ == "__main__": width = 400 height = 200 sampling_size = 200 ppm: PPM = PPM(width, height) lower_left_corner: Vec3 = Vec3(-2, -1, -1) h_movement: Vec3 = Vec3(4, 0, 0) v_movement: Vec3 = Vec3(0, 2, 0) origin: Vec3 = Vec3(0, 0, 0) cam = Camera(lower_left_corner, h_movement, v_movement, origin) hittables: List[Hittable] = [ Sphere(Vec3(0, 0, -1), 0.5), Sphere(Vec3(0, -100.5, -1), 100) ] world: HittableList = HittableList(hittables) for j in range(height - 1, -1, -1): for i in range(width): print("Tracing on row %s, col %s" % (j, i)) print("antialiasing...", end="") accumulator: Vec3 = Vec3(0, 0, 0) for sample in range(sampling_size): # In this instance, instead of u and v being mere ratios to # our distance from the edges, they feature a random "jitter" # which we use to sample the pixels around our current pixel. # In this sense, the current pixel is a combination of its # surroundings. u: float = float(i + random.random()) / float(width) v: float = float(j + random.random()) / float(height) r: Ray = cam.get_ray(u, v)