def random_in_unit_disk(self): while True: p = Vec3(random.random(), random.random(), 0) * 2.0 - Vec3(1, 1, 0) if (p.dot(p) < 1.0): break return p
def readObj(self, path): with open(path, "rt") as file: for line in file: if line.startswith("v "): info = line.split() self.vertices.append(Vec3(float(info[1]), float(info[2]), float(info[3]))) elif line.startswith("vt "): info = line.split() self.tex_coords.append(Vec3(float(info[1]), float(info[2]), 0.0)) self.has_tex_coords = True elif line.startswith("vn "): info = line.split() self.normals.append(Vec3(float(info[1]), float(info[2]), float(info[3]))) self.has_normals = True elif line.startswith("f "): info = line.split() indices_of_vertices_of_face = [] for i in info: if i != "f": vertex_data = [] for j in i.split("/"): if j == '': vertex_data.append('') else: vertex_data.append(int(j) - 1) indices_of_vertices_of_face.append(vertex_data) self.faces.append(indices_of_vertices_of_face) elif line.startswith("o"): info = line.split() self.name = info[1] else: continue
def color(r, world, depth): """Get colour for: r ray of interest world set of hitable objects depth max interaction count Returns colour as Vec3. """ rec = Hit_Record() if world.hit(r, 0.001, MAXFLOAT, rec): # print('rec=%s' % str(rec)) sys.stdout.flush() # scattered = Ray() attenuation = Vec3() if depth < 50.0 and rec.mat_ptr.scatter(r, rec, attenuation, scattered): print('depth=%s' % str(depth)) return attenuation * color(scattered, world, depth + 1) return Vec3(0, 0, 0) else: unit_direction = r.direction.unit_vector t = 0.5 * (unit_direction.y + 1.0) return Vec3(1, 1, 1) * (1.0 - t) + Vec3(0.5, 0.7, 1.0) * t
def main(): with open("output2.ppm", "w") as f: nx, ny, ns = 200, 100, 100 header = "P3\n{} {}\n255\n".format(nx, ny) f.write(header) sphere1 = Sphere(Vec3(0, 0, -1), 0.5) sphere2 = Sphere(Vec3(0, -100.5, -1), 100) world = Hitable_list([sphere1, sphere2]) for j in range(ny - 1, -1, -1): for i in range(0, nx): col = Vec3(0, 0, 0) for s in range(ns): u = float(i + random()) / float(nx) v = float(j + random()) / float(ny) ray = Camera().get_ray(u, v) col += color(ray, world) col /= ns col = Vec3(sqrt(col.r()), sqrt(col.g()), sqrt(col.b())) ir = int(255.99 * col.r()) ig = int(255.99 * col.g()) ib = int(255.99 * col.b()) line = "{} {} {}\n".format(ir, ig, ib) f.write(line)
def cornell_smoke(): '''Generate a scene with a Cornell box where the boxes are filled with smoke''' world = HittableList() red = Lambertian(Color(0.65, 0.05, 0.05)) white = Lambertian(Color(0.73, 0.73, 0.73)) green = Lambertian(Color(0.12, 0.45, 0.15)) light = DiffuseLight(Color(7, 7, 7)) box1 = Box(Point3(0, 0, 0), Point3(165, 330, 165), white) box1 = RotateY(box1, 15) box1 = Translate(box1, Vec3(265, 0, 295)) box2 = Box(Point3(0, 0, 0), Point3(165, 165, 165), white) box2 = RotateY(box2, -18) box2 = Translate(box2, Vec3(130, 0, 65)) world.add(yzRect(0, 555, 0, 555, 555, green)) world.add(yzRect(0, 555, 0, 555, 0, red)) world.add(xzRect(0, 555, 0, 555, 555, white)) world.add(xzRect(0, 555, 0, 555, 0, white)) world.add(xyRect(0, 555, 0, 555, 555, white)) world.add(ConstantMedium(box1, 0.01, Color(0, 0, 0))) world.add(ConstantMedium(box2, 0.01, Color(1, 1, 1))) world.add(xzRect(113, 443, 127, 432, 554, light)) return world
def blue_to_white(i, j, nx, ny): r = origin_to_pixel(i, j, nx, ny) v = r.direction().unit_vector( ) # use y-coordinate of unit direction to scale blueness t = (v.y() + 1) / 2 # ranges between 0 and 1 return Vec3(255.99, 255.99, 255.99) * (1 - t) + Vec3( 255.99 * 0.5, 255.99 * 0.7, 255.99) * t
def diffuse_sphere(i, j, nx, ny, num_samples=100): s1 = Sphere(Vec3(-0.75, 0.75, -1), 0.5, Lambertian(Vec3(0.8, 0.3, 0.3))) s2 = Sphere(Vec3(0, -100.5, -1), 100, Lambertian(Vec3(0.2, 0.6, 0.2))) spheres = HitableList([s1, s2]) cam = Camera() def color(r, world): if world.hit(r, 0.001, float("inf")): scattered, attenuation = world.matrl.scatter( r, world.p, world.normal) if scattered: return color(scattered, world) * attenuation else: return Vec3(0, 0, 0) else: v = r.direction().unit_vector( ) # use y-coordinate of unit direction to scale blueness t = (v.y() + 1) / 2 # ranges between 0 and 1 return Vec3(255.99, 255.99, 255.99) * (1 - t) + Vec3( 255.99 * 0.5, 255.99 * 0.7, 255.99) * t col = Vec3(0, 0, 0) for k in range(num_samples): col += color( cam.get_ray((i + random.random()) / nx, (j + random.random()) / ny), spheres) return col / num_samples
def test_finte(): q1 = Quat(10000000000, 210000000000, 310000000000, -2147483647) q2 = Quat(np.nan, 210000000000, 310000000000, -2147483647) q3 = Quat(np.inf, 210000000000, 310000000000, -2147483647) q4 = Quat(0.0, 210000000000, np.NINF, -2147483647) v1 = Vec3(10000000000, 210000000000, 310000000000) v2 = Vec3(np.nan, 210000000000, 310000000000) v3 = Vec3(np.inf, 210000000000, 310000000000) v4 = Vec3(0.0, 210000000000, np.NINF) t1 = Transform(q1, v1) assert t1.is_finite() assert not t1.is_nan() t2 = Transform(q1, v2) assert not t2.is_finite() assert t2.is_nan() t3 = Transform(q3, v1) assert not t3.is_finite() assert t3.is_nan() t4 = Transform(q4, v4) assert not t4.is_finite() assert t4.is_nan()
def scatter(self, r_in, rec): outward_normal = Vec3() reflected = Vec3.reflect(r_in.direction,rec["normal"]) ni_over_nt = 0.0 attenuation = Vec3(1.0,1.0,1.0) cosine = 0.0 if Vec3.dot(r_in.direction,rec["normal"]) > 0: outward_normal = -rec["normal"] ni_over_nt = self.ref_idx cosine = self.ref_idx * Vec3.dot(r_in.direction,rec["normal"])/ r_in.direction.length() else: outward_normal = rec["normal"] ni_over_nt = 1.0 / self.ref_idx cosine = -Vec3.dot(r_in.direction,rec["normal"])/ r_in.direction.length() scattered = Ray() reflect_prob = 0.0 refracted = Vec3.refract(r_in.direction,outward_normal,ni_over_nt) if refracted is not None: reflect_prob = schlick(cosine,self.ref_idx) else: reflect_prob = 1.0 if random() < reflect_prob: scattered = Ray(rec["p"],reflected) else: scattered = Ray(rec["p"],refracted) return attenuation, scattered
def colored_sphere(i, j, nx, ny): s = Sphere(Vec3(0, 0, -1), 0.5) r = origin_to_pixel(i, j, nx, ny) if s.hit(r, 0, float("inf")): return (s.normal + Vec3(1, 1, 1)) * 255.99 / 2 else: return blue_to_white(i, j, nx, ny)
def get_plane(self, sp1, sp2, sp3, dist_21, dist_23, ang_13): """ Returns the plane object defined by three points projected onto some unknown perspective. :param sp1: screen point 1 :param sp2: screen point 2 :param sp3: screen point 3 :param dist_21: real distance from 2 to 1 :param dist_23: real distance from 2 to 3 :param ang_13: angle between vectors sp1-sp2 and sp2-sp3 :return: plane in real coordinates """ l01 = self.line(sp1) l02 = self.line(sp2) l03 = self.line(sp3) param = 0 a = l02.get_point(param) t1 = l01.get_point_at_dist(a, dist_21, line.Direction.POSITIVE) t3 = l03.get_point_at_dist(a, dist_23, line.Direction.POSITIVE) ang = Vec3.angle_between(t1 - a, t3 - a) while abs(ang - ang_13) > 0.001: param += 10.0 * abs(ang - ang_13) while True: a = l02.get_point(param) try: t1 = l01.get_point_at_dist(a, dist_21, line.Direction.POSITIVE) t3 = l03.get_point_at_dist(a, dist_23, line.Direction.POSITIVE) break except ValueError: param -= 0.1 * abs(ang - ang_13) ang = Vec3.angle_between(t1 - a, t3 - a) i = inspect.getframeinfo(inspect.currentframe()) return Plane(a, t1 - a, t3 - a)
def test_rotate_vec(): v = Vec3(1, 1, 0) q1 = Quat.from_rotation_on_axis(0, np.pi / 3) q2 = Quat.from_rotation_on_axis(1, np.pi / 3) q3 = Quat.from_rotation_on_axis(2, np.pi / 3) # forward rotation v1 = q1.rotate(v) v2 = q2.rotate(v) v3 = q3.rotate(v) theta1 = (60) * np.pi / 180.0 v1_true = Vec3(1, math.cos(theta1), math.sin(theta1)) v2_true = Vec3(math.cos(theta1), 1, -math.sin(theta1)) theta2 = (45 + 60) * np.pi / 180.0 v3_true = math.sqrt(2) * Vec3(math.cos(theta2), math.sin(theta2), 0) assert v1 == v1_true assert v2 == v2_true assert v3 == v3_true # inverse rotate v1_rev = q1.inverse_rotate(v1_true) v2_rev = q2.inverse_rotate(v2_true) v3_rev = q3.inverse_rotate(v3_true) assert v == v1_rev assert v == v2_rev assert v == v3_rev
def random_in_unit_sphere_3(): """Get a random vector inside the 3d unit sphere.""" while True: p = Vec3(random(), random(), random()) * 2.0 - Vec3(1, 1, 1) if p.squared_length < 1.0: return p
def scatter(self, r_in, rec, attenuation, scattered): reflected = reflect(r_in.direction, rec.normal) attenuation = Vec3(1, 1, 1) refracted = Vec3() if r_in.direction.dot(rec.normal) > 0.0: outward_normal = -rec.normal ni_over_nt = self.ref_idx cosine = r_in.direction.dot(rec.normal) / r_in.direction.length cosine = sqrt(1.0 - self.ref_idx * self.ref_idx * (1.0 - cosine * cosine)) else: outward_normal = rec.normal ni_over_nt = 1.0 / self.ref_idx cosine = -r_in.direction.dot(rec.normal) / r_in.direction.length if refract(r_in.direction, outward_normal, ni_over_nt, refracted): reflect_prob = schlick(cosine, self.ref_idx) else: reflect_prob = 1.0 if random() < reflect_prob: scattered.update(rec.p, reflected) else: scattered.update(rec.p, refracted) return True
def main(): # G = 1 # M1 = 1 # M2 = 1 R = 1 v = math.sqrt(1 / (4 * R)) r1 = Vec3(-R, 0, 0) r2 = Vec3(R, 0, 0) v1 = Vec3(0, -v, 0) v2 = Vec3(0, v, 0) time_step = 0.01 r1_data, r2_data = [], [] n_steps = 100000 for i in range(n_steps + 1): r1_data.append(r1) r2_data.append(r2) r1, r2, v1, v2 = leapfrog(r1, r2, v1, v2, time_step) # print("\rcalculation progress: t = {:.2f} / {:.2f} ({:.2f}%)".format(i * time_step, n_steps * time_step, # 100 * i / n_steps), # end="") print(runge(r1, v1)) print(runge(r2, v2)) x1_data = [p.x for p in r1_data] y1_data = [p.y for p in r1_data] x2_data = [p.x for p in r2_data] y2_data = [p.y for p in r2_data] plt.figure(figsize=(8, 8)) plt.plot(x1_data, y1_data) plt.plot(x2_data, y2_data) plt.xlim(-5, 5) plt.ylim(-5, 5) plt.show()
def __init__(self, look_from: Vec3 = Vec3(3.0, 3.0, 2.0), look_at: Vec3 = Vec3(0.0, 0.0, -1.0), vec_up: Vec3 = Vec3(0.0, 1.0, 0.0), v_fov: float = 90.0, aspect: float = 1.0, aperture: float = 0.0, focus_dist: float = 1.0): self.lens_radius = aperture / 2.0 theta = v_fov * 3.14159 / 180.0 half_height = math.tan(theta / 2.0) half_width = aspect * half_height w = unit_vector(look_from - look_at) self.u = unit_vector(vec_up.cross(w)) self.v = w.cross(self.u) self.origin = look_from self.upper_left_corner = look_from - \ half_width*self.u*focus_dist + \ half_height*self.v*focus_dist - w*focus_dist self.horizontal = 2 * half_width * self.u * focus_dist self.vertical = -2 * half_height * self.v * focus_dist
def two_spheres(): l = [] checker = CheckerTexture(ConstantTexture(Vec3(0.2, 0.3, 0.1)), ConstantTexture(Vec3(0.9, 0.9, 0.9))) l.append(Sphere(Vec3(0.0, -10.0, 0.0), 10, Lambertian(checker))) l.append(Sphere(Vec3(0.0, 10.0, 0.0), 10, Lambertian(checker))) return HitableList(l)
def random_in_unit_disk_2(): """Get a random vector inside the unit sphere.""" while True: p = Vec3(random(), random(), 0) * 2.0 - Vec3(1, 1, 0) if p.dot(p) < 1.0: return p
def main(): nx = 200 ny = 100 ns = 30 f = open("generated_images/first_world.ppm","w") f.write("P3\n%d %d\n255\n"%(nx,ny)) cam = Camera() world = Object3DList( [Sphere(Vec3(0.0,0.0,-1.0), 0.5), Sphere(Vec3(0.0,-100.5,-1.0),100)]) # Note break with guide convention, vertical pixels start with index 0 at top for y in range(0,ny): for x in range(0,nx): col = Vec3(0.0,0.0,0.0) for _ in range(0, ns): u = (float(x)+random.random())/float(nx) v = (float(y)+random.random())/float(ny) r = cam.get_ray(u, v) col += color(r, world) col /= ns f.write(col.color_string(scale=255.99)) f.close()
def scatter(self, ray, rec, srec): srec.is_specular = True srec.pdf_ptr = 0 srec.attenuation = Vec3(1.0, 1.0, 1.0) outward_normal = Vec3() reflected = ray.dir.reflect(rec.normal) refracted = Vec3() if ray.dir.dot(rec.normal) > 0: outward_normal = -rec.normal ni_over_nt = self.ref_idx cosine = self.ref_idx * ray.dir.dot(rec.normal) / ray.dir.length() else: outward_normal = rec.normal ni_over_nt = 1.0 / self.ref_idx cosine = -ray.dir.dot(rec.normal) / ray.dir.length() if self.refract(ray.dir, outward_normal, ni_over_nt, refracted): reflect_prob = self.schlick(cosine, self.ref_idx) else: reflect_prob = 1.0 if random() < reflect_prob: srec.specular_ray = Ray(rec.p, reflected) else: srec.specular_ray = Ray(rec.p, refracted) return True
def getSegmentsFromIntersections( solidXIntersectionList, y, z ): "Get endpoint segments from the intersections." segments = [] xIntersectionList = [] fill = False solid = False solidTable = {} solidXIntersectionList.sort( compareSolidXByX ) for solidX in solidXIntersectionList: solidXYInteger = int( solidX.imag ) if solidXYInteger >= 0: toggleHashtable( solidTable, solidXYInteger, "" ) else: fill = not fill oldSolid = solid solid = ( len( solidTable ) == 0 and fill ) if oldSolid != solid: xIntersectionList.append( solidX.real ) for xIntersectionIndex in range( 0, len( xIntersectionList ), 2 ): firstX = xIntersectionList[ xIntersectionIndex ] secondX = xIntersectionList[ xIntersectionIndex + 1 ] endpointFirst = Endpoint() endpointSecond = Endpoint().getFromOtherPoint( endpointFirst, Vec3( secondX, y, z ) ) endpointFirst.getFromOtherPoint( endpointSecond, Vec3( firstX, y, z ) ) segment = ( endpointFirst, endpointSecond ) segments.append( segment ) return segments
def main(): with open("output.ppm", "w") as f: nx, ny, ns = 200, 100, 100 header = "P3\n{} {}\n255\n".format(nx, ny) f.write(header) camera = Camera() sphere1 = Sphere(Vec3(0.0, 0.0, -1.0), 0.5, Lambertian(Vec3(0.8, 0.3, 0.3))) sphere2 = Sphere(Vec3(0.0, -100.5, -1.0), 100.0, Lambertian(Vec3(0.8, 0.8, 0.0))) sphere3 = Sphere(Vec3(1.0, 0.0, -1.0), 0.5, Metal(Vec3(0.8, 0.6, 0.2))) sphere4 = Sphere(Vec3(-1.0, 0.0, -1.0), 0.5, Metal(Vec3(0.8, 0.8, 0.8))) world = Hitable_list([sphere1, sphere2, sphere3, sphere4]) for j in range(ny - 1, -1, -1): for i in range(nx): col = Vec3(0.0, 0.0, 0.0) for k in range(0, ns): u = float(i + random()) / float(nx) v = float(j + random()) / float(ny) ray = camera.get_ray(u, v) col += color(ray, world, 0) col /= float(ns) col = Vec3(sqrt(col.e0), sqrt(col.e1), sqrt(col.e2)) ir = int(255.99 * col.r()) ig = int(255.99 * col.g()) ib = int(255.99 * col.b()) line = "{} {} {}\n".format(ir, ig, ib) f.write(line)
def cornell_box(): '''Generate a scene with a Cornell box, using rectangles and boxes''' world = HittableList() red = Lambertian(Color(0.65, 0.05, 0.05)) white = Lambertian(Color(0.73, 0.73, 0.73)) green = Lambertian(Color(0.12, 0.45, 0.15)) light = DiffuseLight(Color(15, 15, 15)) aluminum = Metal(Color(0.8, 0.85, 0.88), 0.0) glass = Dielectric(1.5) box1 = Box(Point3(0, 0, 0), Point3(165, 330, 165), aluminum) box1 = RotateY(box1, 15) box1 = Translate(box1, Vec3(265, 0, 295)) box2 = Box(Point3(0, 0, 0), Point3(165, 165, 165), white) box2 = RotateY(box2, -18) box2 = Translate(box2, Vec3(130, 0, 65)) world.add(yzRect(0, 555, 0, 555, 555, green)) world.add(yzRect(0, 555, 0, 555, 0, red)) world.add(xzRect(0, 555, 0, 555, 555, white)) world.add(xzRect(0, 555, 0, 555, 0, white)) world.add(xyRect(0, 555, 0, 555, 555, white)) world.add(box1) world.add(box2) world.add(FlipFace(xzRect(213, 343, 227, 332, 554, light))) return world
def hit_sphere(center,radius,r): oc = r.origin -center a = Vec3.dot(r.direction,r.direction) b = 2.0 * Vec3.dot(oc,r.direction) c = Vec3.dot(oc,oc) - radius * radius discriminant = b*b - 4*a*c return discriminant > 0
def hit(self,r,t_min,t_max): rec = None oc = r.origin - self.center(r.time) a = Vec3.dot(r.direction,r.direction) b = Vec3.dot(oc,r.direction) c = Vec3.dot(oc,oc) - (self.radius*self.radius) discriminant = (b*b) - (a*c) if discriminant > 0: rec = {} temp = (-b - sqrt(discriminant)) / a if t_min < temp < t_max: rec["t"] = temp rec["p"] = r.point_at_parameter(rec["t"]) rec["u"],rec["v"] =getSphereUv((rec["p"] - self.center) / self.radius) rec["normal"] = (rec["p"] - self.center(r.time)) / self.radius rec["material"] = self.material return rec temp = (-b + sqrt(discriminant)) / a if t_min < temp < t_max: rec["t"] = temp rec["p"] = r.point_at_parameter(rec["t"]) rec["u"],rec["v"] =getSphereUv((rec["p"] - self.center) / self.radius) rec["normal"] = (rec["p"] - self.center(r.time)) / self.radius rec["material"] = self.material return rec return None
def random_in_unit_sphere(): """Return a random vector in the unit sphere.""" while True: p = Vec3(random(), random(), random()) * 2.0 - Vec3(1.0, 1.0, 1.0) if p.squared_length < 1.0: return p
def __init__(self): """A camera clas.""" self.origin = Vec3(0.0, 0.0, 0.0) self.lower_left_corner = Vec3(-2.0, -1.0, -1.0) self.horizontal = Vec3(4.0, 0.0, 0.0) self.vertical = Vec3(0.0, 2.0, 0.0)
def test_dot(): v1 = Vec3(0, 1, 2) v2 = Vec3(3, 4, 5) res = v1.dot(v2) assert res == 14
def __init__(self, h): nz = cos(radians(h)) nx = sqrt(1 - nz * nz) height = nz / nx #print(nx, nz, height) self.lside = Plane(Vec3(0.0, 0.0, -height), Vec3(nx, 0.0, nz)) self.rside = Plane(Vec3(0.0, 0.0, -height), Vec3(-nx, 0.0, nz))
def __init__(self): self.position = Vec3() self.normal = Vec3() self.t = 0.0 self.frontFacing = False
def random_in_unit_sphere(): # p = Vec3(0, 0, 0) while True: p = Vec3(random(), random(), random()) * 2.0 - Vec3(1.0, 1.0, 1.0) if p.squared_length < 1.0: break return p
def main(): nx = 200 ny = 150 ns = 100 print("P3\n", nx, " ", ny, "\n255") world = two_spheres() lookfrom = Vec3(13, 2, 3) lookat = Vec3(0, 0, 0) dist_to_focus = 10.0 aperture = 0.0 cam = Camera(lookfrom, lookat, Vec3(0, 1, 0), 20, nx / ny, aperture, dist_to_focus, 0.0, 1.0) for j in reversed(range(ny)): for i in range(nx): col = Vec3(0, 0, 0) for s in range(ns): u = (i + random()) / nx v = (j + random()) / ny r = cam.get_ray(u, v) p = r.point_at_parameter(2.0) col += color(r, world, 0) col /= ns col = Vec3(sqrt(col[0]), sqrt(col[1]), sqrt(col[2])) ir = int(255.99 * col[0]) ig = int(255.99 * col[1]) ib = int(255.99 * col[2]) print(ir, " ", ig, " ", ib)
def scatter(self, r_in,rec): reflected = Vec3.reflect(Vec3.unit_vector(r_in.direction),rec["normal"]) scattered = Ray(rec["p"],reflected + self.fuzz * Material.random_in_unit_sphere()) attenuation = self.albedo if Vec3.dot(scattered.direction,rec["normal"]) > 0: return attenuation,scattered else: return None,None
def color(ray): t = hit_sphere(Vec3(0, 0, -1), 0.5, ray) if t > 0.0: N = (ray.point_at_parameter(t) - Vec3(0, 0, -1)).unit_vector() return Vec3(N.x() + 1, N.y() + 1, N.z() + 1) * 0.5 unit_direction = ray.direction.unit_vector() t = 0.5 * (unit_direction.y() + 1.0) return Vec3(1.0, 1.0, 1.0) * (1.0 - t) + Vec3(0.5, 0.7, 1.0) * t
def __init__(self,lookfrom,lookat,vup,vfov,aspect,aperture,focus_dist): self.lens_radius = aperture /2 theta = vfov * math.pi/180 half_height = math.tan(theta/2) half_width = aspect * half_height self.origin = lookfrom self.w = Vec3.unit_vector(lookfrom -lookat) self.u = Vec3.unit_vector(Vec3.cross(vup,self.w)) self.v = Vec3.cross(self.w,self.u) self.lower_left_corner = self.origin - half_width*focus_dist*self.u -half_height*focus_dist*self.v -focus_dist*self.w self.horizontal = 2*half_width*focus_dist*self.u self.vertical = 2*half_height*focus_dist*self.v
def color(r,world): rec = world.hit(r,0.0,float('inf')) if rec is not None: target = rec["p"]+rec["normal"] + random_in_unit_sphere() return 0.5 * color(Ray(rec["p"],target-rec["p"]),world) else: unit_direction = Vec3.unit_vector(r.direction) t = 0.5*(unit_direction.y +1.0) return (1.0-t)*Vec3(1.0,1.0,1.0) + t*Vec3(0.5,0.7,1.0)
def hit(self,r,t_min,t_max): rec = None oc = r.origin - self.center a = Vec3.dot(r.direction,r.direction) b = Vec3.dot(oc,r.direction) c = Vec3.dot(oc,oc) - (self.radius*self.radius) discriminant = (b*b) - (a*c) if discriminant > 0: rec = {} temp = (-b - sqrt(discriminant)) / a if t_min < temp < t_max: rec["t"] = temp rec["p"] = r.point_at_parameter(rec["t"]) rec["normal"] = (rec["p"] - self.center) / self.radius return rec temp = (-b + sqrt(discriminant)) / a if t_min < temp < t_max: rec["t"] = temp rec["p"] = r.point_at_parameter(rec["t"]) rec["normal"] = (rec["p"] - self.center) / self.radius return rec return None
def color(r,world,depth): rec = world.hit(r,0.001,float('inf')) if rec is not None: if depth >= 50: return Vec3(0,0,0) attenuation,scattered =rec["material"].scatter(r,rec) if attenuation is not None: return attenuation * color(scattered,world,depth +1) else: return Vec3(0,0,0) else: unit_direction = Vec3.unit_vector(r.direction) t = 0.5*(unit_direction.y +1.0) return (1.0-t)*Vec3(1.0,1.0,1.0) + t*Vec3(0.5,0.7,1.0)
def color(r): unit_direction = Vec3.unit_vector(r.direction) t = 0.5*(unit_direction.y +1.0) return (1.0-t)*Vec3(1.0,1.0,1.0) + t*Vec3(0.5,0.7,1.0)
def get_dist_to_point(self, point): return abs(Vec3.from_to(point, self.get_closest_point_to(point)))
def get_closest_point_to(self, point): n = self.get_normal() return point - (Vec3.dot(Vec3.from_to(self.origin, point), n)*n)
def get_normal(self): return Vec3.cross(self.direction1, self.direction2).get_unit()
def random_in_unit_sphere(cls): while True: p = 2.0*Vec3(random(),random(),random()) - Vec3(1,1,1) if Vec3.dot(p,p) < 1.0: return p
def random_in_unit_disk(): while True: p = 2.0 * Vec3(random(),random(),0) -Vec3(1,1,0) if Vec3.dot(p,p) < 1.0: return p
def color(r): if hit_sphere(Vec3(0,0,-1),0.5,r): return Vec3(1,0,0) unit_direction = Vec3.unit_vector(r.direction) t = 0.5*(unit_direction.y +1.0) return (1.0-t)*Vec3(1.0,1.0,1.0) + t*Vec3(0.5,0.7,1.0)
def get_closest_point_to(self, point): l = self.direction.get_unit() return self.origin + (Vec3.dot(Vec3.from_to(self.origin, point), l)*l)