def intersectRay (self, ray: Ray, isEyeRay:bool=False) -> np.ndarray: rayLength = MV.dot(ray.direction, ray.direction) ** 0.5 ray.direction = MV.normalize(ray.direction) rayOrigin_SphereCoord = MV.transformPoint(self.inverseMat, ray.origin) rayDir_SphereCoord = MV.transformVector(self.inverseMat, ray.direction) a = MV.dot(rayDir_SphereCoord, rayDir_SphereCoord) b = MV.dot(rayDir_SphereCoord, rayOrigin_SphereCoord) c = MV.dot(rayOrigin_SphereCoord, rayOrigin_SphereCoord) - 1**2 d = b * b - a * c if d < 0: return np.inf if d >= 0: t1 = (-b + np.sqrt(d)) / a t2 = (-b - np.sqrt(d)) / a if (isEyeRay): tMin = min (t1, t2) tMax = max (t1, t2) if (tMin > rayLength): return tMin elif (tMax > rayLength): return tMax else: return np.inf answer = min(t1, t2) if answer < 0: return np.inf else: return answer
def isInside (self, point: np.ndarray) -> bool: point_SphereCoord = MV.transformPoint(self.inverseMat, point) return (MV.dot(point_SphereCoord, point_SphereCoord) <= 1)
def rayTracer (ray: Ray, scene: Scene): # find the intersection of the ray with all the object t = np.inf for i, obj in enumerate(scene.objects): # if the ray is eye-ray, check whether t < 1, # if yes, omit it. if (np.array_equal(ray.origin, scene.camPoint)): tObj = obj.intersectRay(ray, True) else: tObj = obj.intersectRay(ray) if tObj < t: t, obj_idx = tObj, i # if there is not any intersection, return if t == np.inf: return obj = scene.objects[obj_idx] # converse the intersection point to Sphere Coordinate Sytem # then calculate the normal vector at the intersection point # finally converse it back to the World Coordinate System P = ray.origin + ray.direction * t P_SphereCoord = MV.transformPoint(obj.inverseMat, P) N_SphereCoord = P_SphereCoord - np.zeros(3) # sphere origin is (0, 0, 0) in sphere coordinate system # N = P - obj.pos # N_SphereCoord = MV.transformVector(obj.inverseMat, N) N = MV.transformVector(obj.transposeInverseMat, N_SphereCoord) N = MV.normalize(N) V = MV.normalize(-P) color = obj.color.copy() ''' PIXEL_COLOR[c] = Ka*Ia[c]*O[c] + for each point light (p) { Kd*Ip[c]*(N dot L)*O[c]+Ks*Ip[c]*(R dot V)n } + Kr*(Color returned from reflection ray) O is the object color (<r> <g> <b>) ''' Ka = scene.ambient * obj.k_a Kd = np.zeros(3) Ks = np.zeros(3) for light in scene.lightSources: L = MV.normalize(light.pos - P) # Shadow: find if the point is shadowed or not. ray = Ray(P + N * .000001, L) l = [sphere.intersectRay(ray) for k, sphere in enumerate(scene.objects) if k != obj_idx] if l and min(l) < np.inf: continue if (obj.isInside(scene.atVector)): if (obj.isInside(light.pos)): N = -N else: continue R = MV.normalize(2 * MV.dot(N, L) * N - L) # Lambert shading (diffuse). Kd += obj.k_d * max(MV.dot(N, L), 0.) * ( light.intensity ) # Blinn-Phong shading (specular) Ks += obj.k_s * ( max(np.dot(R, V), 0.) ** obj.specular ) * ( light.intensity ) pixelColor = Ka * color + Kd * color + Ks return obj, P + N * .000001, N, pixelColor