def can_see(point, object): cast_ray = Ray() cast_ray.o = point cast_ray.d = Vector3D(-260, -100, 0) cast_ray.d = cast_ray.d.normalize() hit = False for thing in World: intersection = thing.hit(cast_ray) #if it's not False, it hit something. if intersection != False: #is this the first time we've hit something? if hit == False: hit = True hit_distance = cast_ray.o.distance(intersection.hit_point) closest_object = intersection else: #check if the latest hit is closer if cast_ray.o.distance(intersection.hit_point) < hit_distance: closest_object = intersection hit_distance = cast_ray.o.distance(intersection.hit_point) if hit == False: return False else: if closest_object == object: return True else: return False
def render_scene(self,objects,lights,res): #create a viewport and image v = ViewPort(res[0],res[1]) im = Image.new("RGB",(v.w,v.h)) pix = im.load() #define a ray #FIX BUG -- STILL ORTHO ray = Ray(np.array([0,0,0]),np.array([0,0,-1])) # Perform perspective ray-tracing print("...generating "+str(v)+" image") for col in range(v.w): for row in range(v.h): color = np.zeros(3) print(color) ray.o = v.getPixelCenter(col,row) for s in objects: t = s.intersectRay(ray) if (t != None): xp = ray.getPoint(t) for light in lights: color+= phongShader(xp,s.getNormal(xp),s.material,light,self.eye) print(color) #pix[col,v.h-1-row] = color pix[col,v.h-1-row]=(1,1,1) # Show the image in a window im.show()
def get_visible_lights(self, isect): ''' isect is variable of type IntersectionResult. isect.p contains the intersection point. This function should return a python list containing all the lights that are "visible" from isect.p position. A light source is visible if there are no objects between the point and the light source. All light sources are of type Light (Light class is defined in HelperClasses.py). The light sources in the scene is stored in the variable Scene.lights (accessed using self.lights). Your returned list should be a subset of self.lights ''' #you need to loop over the lights and return those that are visible from the position in result visibleLights = [] #====== BEGIN SOLUTION ===== for l in self.lights: ray = Ray() ray.eyePoint = isect.p ray.viewDirection = GT.normalize(l.pointFrom - ray.eyePoint) d = np.linalg.norm(l.pointFrom - ray.eyePoint) i = self.get_nearest_object_intersection(ray) if i.material is None or i.t >= d - 1e-9: visibleLights.append(l) # ===== END SOLUTION HERE ===== return visibleLights
def create_ray(self,row,col): ''' Create ray (i.e. origin and direction) from a pixel specified by (row, col). The ray originates at the camera's eye and goes through the point in space that corresponds to the image pixel coordinate (row, col). Take a look at the Camera class to see the variables that can be used here. ''' ray = Ray() # construct an empty ray ''' The ray origin is set in this starter code. You need to compute and set the ray.viewDirection variable inside the solution block below. Note: GeomTransform.py implements a function called normalize for normalizing vectors (with appropriate check for division by zero). For a vector v, you can get the normalized vector as: normalized_v = GT.normalize(v) ''' cam = self.render.camera # use a local variable to save some typing ray.eyePoint = cam.pointFrom # origin of the ray # ====== BEGIN SOLUTION ====== dx = col / cam.imageWidth dy = row / cam.imageHeight stepx = dx * (cam.right - cam.left) stepy = dy * (cam.top - cam.bottom) ray.viewDirection = GT.normalize(cam.lookat + [cam.left + stepx, cam.top - stepy, 0]) # ====== END SOLUTION ======= return ray
def getReflectedNormal(self, ray, t): p = Point(*(ray.d.scale(t)).v )+ray.o normal = self.normal rr = Ray() rr.o=p rr.d = ray.d - normal.scale(2*ray.d.dot(normal)) rr.d=Vector(*rr.d.v) return rr, normal
def getReflectedNormal(self, ray, t): # need to find the point, then find normal at point #then get reflected ray, origin=pt p = Point(*(ray.d.scale(t)).v )+ray.o normal = Vector(*(p-self.origin).v) normal.normalize() rr = Ray() rr.o=p rr.d = ray.d - normal.scale(2*ray.d.dot(normal)) rr.d=Vector(*rr.d.v) return rr, normal
def create_ray(self,row,col): ''' Create ray (i.e. origin and direction) from a pixel specified by (row, col). The ray originates at the camera's eye and goes through the point in space that corresponds to the image pixel coordinate (row, col). Take a look at the Camera class to see the variables that can be used here. ''' ray = Ray() # construct an empty ray ''' The ray origin is set in this starter code. You need to compute and set the ray.viewDirection variable inside the solution block below. Note: GeomTransform.py implements a function called normalize for normalizing vectors (with appropriate check for division by zero). For a vector v, you can get the normalized vector as: normalized_v = GT.normalize(v) ''' cam = self.render.camera # use a local variable to save some typing ray.eyePoint = cam.pointFrom # origin of the ray #TODO ====== BEGIN SOLUTION ====== # get the camera coordinates cam_x = col*(cam.right-cam.left)/cam.imageWidth + cam.left cam_y = row*-(cam.top-cam.bottom)/cam.imageHeight + cam.top cam_z = cam.near xa = cam.cameraXAxis ya = cam.cameraYAxis za = cam.cameraZAxis R = [ [xa[0], ya[0], za[0], 0], [xa[1], ya[1], za[1], 0], [xa[2], ya[2], za[2], 0], [0, 0, 0, 1] ] T = [ [1, 0, 0, -cam.pointFrom[0]], [0, 1, 0, -cam.pointFrom[1]], [0, 0, 1, -cam.pointFrom[2]], [0, 0, 0, 1] ] cam_to_world = np.linalg.inv(np.dot(np.transpose(R),T)) world_pixels = np.dot( cam_to_world, [cam_x, cam_y, cam_z, 1] ) #world_pixels = world_pixels/world_pixels[3] ray.viewDirection = GT.normalize(world_pixels[0:3] - ray.eyePoint) # ===== END SOLUTION ===== return ray
def raytrace(cast_ray, r = recurse): hit = False for thing in World: intersection = thing.hit(cast_ray) #if it's not False, it hit something. if intersection != False: #is this the first time we've hit something? if hit == False: hit = True hit_distance = cast_ray.o.distance(intersection.hit_point) closest_object = intersection else: #check if the latest hit is closer if cast_ray.o.distance(intersection.hit_point) < hit_distance: closest_object = intersection hit_distance = cast_ray.o.distance(intersection.hit_point) if hit == False: return background_color else: # At this point, closest_object[4] contains the closest item the ray intersects with. #Check for end of recursion if r == 1: #Now we apply lighting lit_color = lighting(closest_object) return RGB(1, 1, 1) return lit_color #Check for Reflectance if closest_object.object.mat.reflect > 0: reflect_ray = Ray() reflect_ray.o = closest_object.hit_point c1 = 0 - (closest_object.normal * cast_ray.d) reflect_ray.d = cast_ray.d + (2 * closest_object.normal * c1) reflect_ray.d = reflect_ray.d.normalize() reflect_color = raytrace(reflect_ray, r - 1) tcolor = lighting(closest_object) tcolor = tcolor + (reflect_color * closest_object.object.mat.reflect) return tcolor else: # The object isn't reflective. lit_color = lighting(closest_object) return lit_color
def Render(): pygame.init() windowSurfaceObj = pygame.display.set_mode((xres, yres)) pygame.display.set_caption("Matt's Ray Tracer") pixArr = pygame.PixelArray(windowSurfaceObj) tRay = Ray() tRay.d = Vector3D(0, 0, -1) #Hardcoded Viewport for now tRay.o.z = Viewportz starttime = time.time() for y in range(yres): for event in pygame.event.get(): if event.type == QUIT: filename = datetime.datetime.strftime(datetime.datetime.now(), "%H.%M.%S_%d-%b-%Y") + '.png' pygame.image.save(windowSurfaceObj, filename) pygame.quit() sys.exit() pygame.display.set_caption("Matt's Ray Tracer - Render in Progress...") tRay.o.y = psize * (y - 0.5 * (yres - 1)) for x in range(xres): tRay.o.x = psize * (x - 0.5 * (xres - 1)) ray_color = raytrace(tRay) tcolor = ray_color.finalcolor() pixArr[x][y] = pygame.Color(tcolor[0], tcolor[1], tcolor[2]) pygame.display.update() del pixArr pygame.display.set_caption("Matt's Ray Tracer - Render Finished - Total time: " + str(time.time() - starttime) + " seconds") print 'Time :', time.time() - starttime filename = datetime.datetime.strftime(datetime.datetime.now(), "%H.%M.%S_%d-%b-%Y") + '.png' pygame.image.save(windowSurfaceObj, filename) while True: for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYDOWN: if event.key == K_ESCAPE: pygame.event.post(pygame.event.Event(QUIT))
def render_scene(objects,res): #create a viewport and image v = ViewPort(res[0],res[1]) im = Image.new("RGB",(v.w,v.h)) pix = im.load() #define a ray ray = Ray(np.array([0,0,0]),np.array([0,0,-1])) # Perform perspective ray-tracing for col in range(v.w): for row in range(v.h): ray.o = v.getPixelCenter(col,row) t = s.intersectRay(ray) if (t != None): xp = ray.getPoint(t) pix[col,(v.h-1)-row] = phongShader(xp,s.getNormal(xp),s.material,light,eye) # Show the image in a window im.show()
def isInShadows(self, ray, hitdist): if hitdist == float('inf'): return False hitpoint = ray.pointatparameter(hitdist) nv = self.licht.pos - hitpoint nnv = nv / np.linalg.norm(nv) lichtRay = Ray(hitpoint, nnv) maxdist = self.licht.distance(lichtRay) for obj in self.objects: hitdist = obj.intersectionparameter(lichtRay) if hitdist: if 0.001 <= hitdist <= maxdist: return True
def initExplosion(self, pos): n = self.POINTS_IN_DOT_WAVEFRONT * 2 for i in range(n): angle = 2 * math.pi * i / n velocity = Vector2(math.cos(angle), math.sin(angle)) self.rays = np.append(self.rays, Ray(pos, velocity)) self.raysNum += 1 for i in range(1, n - 1): self.rays[i].setLeft(self.rays[i - 1]) self.rays[i].setRight(self.rays[i + 1]) self.rays[0].setLeft(self.rays[n - 1]) self.rays[0].setRight(self.rays[1]) self.rays[n - 1].setLeft(self.rays[n - 2]) self.rays[n - 1].setRight(self.rays[0])
def render_image(self): """ converting the position of pixels in a screen 2D to the correlate pixel positions in the viewport """ for i in range(self.size[0]): for j in range(self.size[1]): percentage_pos = self.screen2D.pixel_position_percentage( i, j) #finding pixel position percentage in the screen2D view_port_pixel = self.viewportpos.percentage_to_point( percentage_pos[0], percentage_pos[1] ) #converting pixel position percentage in the screen2D to the correlate pixel position in the viewport ray_dir = view_port_pixel.sub(self.camerapos.clone()) ray = Ray(self.camerapos.clone(), ray_dir.clone()) #defining a ray color = self.scene_intersect(ray) """ assign the color to the screen2D pixels """ self.screen2D.pixels[i, self.size[1] - j - 1] = color self.screen2D.image.show()
class Plane(Object): def __init__(self, point1, point2, color): Object.__init__(self, color, "plane") self.point = point1 self.normal_vector = Ray(point1=point1, point2=point2) def normal(self, point): return self.normal_vector def ray_intersect(self, ray): denom = self.normal_vector.dotproduct(ray) if denom !=0: t = ( ray.origin.directionvector(self.point) ).\ dotproduct( self.normal_vector ) / denom if t >= 0: return t return 0.0
def getpixel(ray): # calculates the color of the current pixel based on interected objects intersect, dist = raytrace(ray, scene.objects) if intersect: intersect_point = ray.intersect_point(dist) light_vector = Ray(point1=scene.light, point2=intersect_point) normal_vector = intersect.normal(intersect_point) if inshadow(intersect_point, normal_vector, light_vector, scene.objects): shade = 0.0 else: shade = lambertshade(light_vector, normal_vector) point_color = intersect.pointcolor(scene.ambient_coefficient, \ scene.diffuse_coefficient(), shade) return point_color # alter for shadow else: return scene.blank_color return
def test_rand_eye_intersection_distance_correctness(self, NTEST = 100): ''' Shoot rays from random eye position towards the center. The intersection point should be radius distance from the center. ''' np.random.seed(9125) for testNo in range(NTEST): # generate a random point outside of the range [-5, 5] sgn = 1 if np.random.rand(1) <= .5 else -1 eye = (np.random.rand(1, 3).flatten() + 5) * sgn # generate a random point viewDir = GT.normalize(-eye) # direction towards the center #print(sgn, eye, viewDir) ray = Ray(eye, viewDir) result = self.sphere.intersect(ray) distance = np.linalg.norm(result.p - self.center) nptest.assert_almost_equal(distance, self.radius)
def initRay(self, x): for y in range(0, self.h): col = Vec3(0, 0, 0) # Aspect correction in the case the output image is not square # This took me super long to figure out mx = ((x + (self.h - self.w) / 2) * self.w/self.h) # average the samples for i in range(self.samples): # calculate direction # Adds the random to get anti-aliasing a = self.getDir(mx + random(), y + random()) # create ray for rendering ray = Ray(orig=self.pos, dir=a) # render! col = col + self.rendererCalcColor(ray, 4, self.tracer) col = col ^ (1 / self.samples) # average the samples self.savePixel(col, x, y) # save pixel return "complete"
def scene_intersect(self, ray): intersect_scale_dic = {} num_of_bounces = 0 for obj in self.list_of_primitives: t = obj.get_intersect(ray.origin.clone(), ray.ray_dir.clone()) if t: intersect_scale_dic[obj] = t if len(intersect_scale_dic) != 0: t_min = min(intersect_scale_dic.itervalues()) intersected_obj = (min(intersect_scale_dic, key=intersect_scale_dic.get)) surface_color = intersected_obj.color intersect_point = ray.origin.clone().add( ray.ray_dir.clone().constant_multiply(t_min)) normal_vector = intersected_obj.surface_normal( point=intersect_point.clone(), ray_origin=self.camerapos.clone(), ray_dir=ray.ray_dir.clone()) if intersected_obj.material == "mirror" and num_of_bounces <= 100: num_of_bounces += 1 def_ray_dir = ray.ray_dir.constant_multiply( -1).clone().reflected_ray_dir(normal_vector.clone()) intersect_point = intersect_point.clone().add( def_ray_dir.normalize().clone().constant_multiply(0.001)) def_ray = Ray(origin=intersect_point.clone(), ray_dir=def_ray_dir.clone()) return self.scene_intersect(def_ray) else: # calling is_in_shadow function to check if the intersect point is in shadow or not is_intersected = self.is_in_shadow(intersect_point.clone(), intersected_obj) if is_intersected: return (0, 0, 0) else: # calling illumination function if the intersect_point light_position Ray does not intersects with any other primitives final_color = self.illumination(ray, normal_vector, intersect_point, surface_color) return final_color else: return (0, 0, 0)
def is_in_shadow(self, intersect_point, obj): """ """ #1. calculate ray from intersect point to light source light_intersect_vector = self.lights[0].position.clone().sub(intersect_point) t_ray_light = light_intersect_vector.mag() light_intersect_ray = Ray(origin =intersect_point.clone(), ray_dir= light_intersect_vector.normalize()) #2. determine if ray intersects any OTHER primitives is_intersected = False for test_obj in self.list_of_primitives: if obj != test_obj and test_obj.material != "glass": t = test_obj.get_intersect(light_intersect_ray.origin.clone(), light_intersect_ray.ray_dir.clone()) if t and t < t_ray_light: is_intersected = True break else: is_intersected = False else: continue return is_intersected
def mapPointToScreen(self, point): if self.ortho: direction = self.direction else: diff = point - self.eye if Roughly(diff.length_2(), 0): return None direction = diff.normalize() ray = Ray(self.eye, direction) hit = rayHitsPlane(-self.direction, self.screenCenter, ray) if hit.hit: assert not hit.inverted # TODO: This won't map points behind the eye. location = ray.origin + ray.offset.scale(hit.distance) offset = self.screenTopLeft - location distanceFromLeft = offset.dot(self.left) distanceFromTop = offset.dot(self.up) pixel = (int(distanceFromLeft / self.w * float(self.cols)), int(distanceFromTop / self.h * float(self.rows))) return pixel return None
def rendererCalcColor(self, ray, numBounce, tracer): """ Calculates a pixel colour given a starting ray using Monte Carlo magik! Parameters: ray: Ray. The ray to be traced numBounce: Int. The number of bounces the ray is allowed to do tracer: Scene. The scene """ # Variables for colour accumulation tCol = Vec3(0, 0, 0) gCol = Vec3(1, 1, 1) for i in range(numBounce): # intersect with seen isec = tracer.worldIntersect(ray) # intersection information sec = isec["t"] # if no intersection if not sec[0]: # stop the accumulation process or return the sky if i == 0: return self.bgColor else: break # Calculate intersection position pos = ray.o + (ray.d ^ sec[1]) # load material material = isec["index"].mat # Load surface colour and compute direct lighting sCol = tracer.materials[material] dCol = self.applyDirectLighting(pos, sec[2], tracer) # Create new ray ray = Ray(orig=pos + (sec[2] ^ 0.1), dir=OrientedHemiDir(sec[2])) # accumulate colours gCol = sCol * gCol tCol += gCol * dCol # return the total colour return tCol
def test_rand_eye_intersection_distance_from_eye_correctness(self, NTEST = 100): ''' Shoot rays from random eye position towards the center. Test whether the distance to the intersection point from the eye position is distance to the center minus the radius.''' np.random.seed(9125) # uses the same random sequence as point to center distance test for testNo in range(NTEST): # generate a random point outside of the range [-5, 5] sgn = 1 if np.random.rand(1) <= .5 else -1 eye = (np.random.rand(1, 3).flatten() + 5) * sgn # generate a random point viewDir = GT.normalize(-eye) # direction towards the center #print(sgn, eye, viewDir) ray = Ray(eye, viewDir) result = self.sphere.intersect(ray) isect_pt_from_eye_distance = np.linalg.norm(result.p - eye) # for sanity check eye_distance_from_center = np.linalg.norm(eye - self.center) #print(isect_pt_from_eye_distance, result.t) nptest.assert_almost_equal(result.t, isect_pt_from_eye_distance) # sanity check nptest.assert_almost_equal(result.t, eye_distance_from_center - self.radius) # sanity check
def getRay(self, x: float, y: float) -> Ray: window_x = ((x + 1) / 2) * self._window_width window_y = ((y + 1) / 2) * self._window_height view_x = (window_x / self._viewport_width) * 2 - 1 view_y = (window_y / self._viewport_height) * 2 - 1 inverted_projection = numpy.linalg.inv( self._projection_matrix.getData().copy()) transformation = self.getWorldTransformation().getData() near = numpy.array([view_x, -view_y, -1.0, 1.0], dtype=numpy.float32) near = numpy.dot(inverted_projection, near) near = numpy.dot(transformation, near) near = near[0:3] / near[3] far = numpy.array([view_x, -view_y, 1.0, 1.0], dtype=numpy.float32) far = numpy.dot(inverted_projection, far) far = numpy.dot(transformation, far) far = far[0:3] / far[3] direction = far - near direction /= numpy.linalg.norm(direction) if self.isPerspective(): origin = self.getWorldPosition() direction = -direction else: # In orthographic mode, the origin is the click position on the plane where the camera resides, and that # plane is parallel to the near and the far planes. projection = numpy.array([view_x, -view_y, 0.0, 1.0], dtype=numpy.float32) projection = numpy.dot(inverted_projection, projection) projection = numpy.dot(transformation, projection) projection = projection[0:3] / projection[3] origin = Vector(data=projection) return Ray(origin, Vector(direction[0], direction[1], direction[2]))
def Refraction(ray, air_ind, glass_ind, env="air"): sphere = Sphere(position=Vector(2, 2, 2), radius=1.0) print "ray.ray_origin", ray.origin print "ray.ray_dir", ray.ray_dir t_min = sphere.get_intersect(ray_origin=ray.origin, ray_dir=ray.ray_dir.normalize()) print "t_min", t_min intersect_point = ray.origin.clone().add( (ray.ray_dir.normalize().clone()).constant_multiply(t_min * 0.001)) print "intersect_point", intersect_point normal_vector = sphere.surface_normal(point=intersect_point.clone(), ray_origin=ray.origin.clone(), ray_dir=ray.ray_dir.clone()) print "normal_vector", normal_vector c_1 = normal_vector.normalize().clone().dot( ray.ray_dir.normalize().clone()) if env == "air": n = air_ind / glass_ind c_1 = -c_1 print "n", n # print "c_1", c_1 else: n = glass_ind / air_ind print "n", n normal_vector = normal_vector.normalize().constant_multiply(-1) k = (1 - (n**2) * (1 - (c_1)**2)) if k < 0: return "K is negative" c_2 = math.sqrt(k) part_1 = ray.ray_dir.normalize().constant_multiply(n) part_2 = normal_vector.normalize().clone().constant_multiply( (n * c_1 - c_2)) ref_ray_dir = (part_1.clone().add(part_2.clone())).normalize() # print "ref_ray_dir", ref_ray_dir ref_ray = Ray(origin=intersect_point, ray_dir=ref_ray_dir) # print "env", env # print "n", n return ref_ray
def compute_DirectLight(hitPointData): """ Methode handled wie die Schnittpunktstelle gefärbt werden soll :param hitPointData: (Objekt, Schnittpunkt, Trefferdistanz, Ray) :return: gibt Farbe am Schnittpunkt mit Objekt zurück """ color = BACKGROUND_COLOR current_obj = hitPointData[0] if hitPointData[0] and current_obj is not None: intersec_Point = hitPointData[1] #Schnittpunkt intersec_Light = Ray(intersec_Point, normalize(light.position - intersec_Point)) #Ray vom Schnittpunkt zum Licht in_shadow = check_Shadow(current_obj,intersec_Light) #Checkt ob Schnittpunkt auf der Oberfläche im Schatten liegt """in_shadow = False""" if in_shadow == True: if isinstance(current_obj,Plane): color = numpy.array(current_obj.rgb.baseColorAt(intersec_Point)) * shadow_ambient #Wenns im Schatten liegt nehme einen geringeren Ambient Faktor else: color = phong_Shader(intersec_Point,hitPointData,shadow_ambient) else: color = phong_Shader(intersec_Point, hitPointData, no_shadow_ambient) #Größerer Ambientfaktor wenns nicht im Schatten liegt return color
def render(self, tracer, imgOut): """ Renders a scene to an image Parameters: tracer: Scene. The scene to be rendered. imgOut: String. The name of the output file """ # loop through all the pixels in the image for x in range(self.w): for y in range(self.h): col = Vec3(0, 0, 0) # Aspect correction in the case the output image is not square # This took me super long to figure out mx = ((x + (self.h - self.w) / 2) * self.w / self.h) # average the samples for i in range(self.samples): # calculate direction # Adds the random to get anti-aliasing a = self.getDir(mx + random(), y + random()) # create ray for rendering ray = Ray(orig=self.pos, dir=a) # render! col = col + self.rendererCalcColor(ray, 4, tracer) col = col ^ (1 / self.samples) # average the samples self.savePixel(col, x, y) # save pixel # ===Update progress bar=== # Clear terminal os.system('cls' if os.name == 'nt' else 'clear') # Calculate progress prog = int(round((x) / self.w * self.barWidth)) # Print progress bar print("[" + "=" * prog + ">" + " " * (self.barWidth - prog) + "] " + str(round(prog * (100 / self.barWidth))) + "% completed") # ===Save Image=== self.saveImage(imgOut) # save image
def calcRay(self, x, y): """calculates a ray for the main""" xcomp = np.multiply(self.s, (x * self.pixelWidth - self.viewWidth / 2)) ycomp = np.multiply(self.u, (y * self.pixelHeigth - self.viewheight / 2)) return Ray(self.e, np.add(self.f, np.add(xcomp, ycomp)))
def calcRay(x, y): """calculates a ray for the main""" xcomp = np.multiply(s, (x * pixelWidth - viewWidth / 2)) ycomp = np.multiply(u, (y * pixelHeigth - viewheight / 2)) return Ray(e, np.add(f, np.add(xcomp, ycomp)))
def optimized_shadows(polygons, light_point, surface, box_borders=(Vector2D(0, 0), Vector2D(1280, 0), Vector2D(1280, 720), Vector2D(0, 720)), color=(0, 0, 0)): # need to understand https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection # need to understand https://thecodingtrain.com/CodingChallenges/145-2d-ray-casting.html for polygon in range(0, len(polygons)): if isinstance(polygons[polygon], Polygon): polygons[polygon] = polygons[polygon].get_points() screen_borders = list(box_borders) for polygon in polygons: casted_points = [None for point in polygon] touched_side = [0 for point in polygon] for point in range(0, len(polygon)): point_to_light = light_point.copy() point_to_light -= polygon[point] ray_light_to_point = Ray(polygon[point], point_to_light) closest_point = None closest_d = 10000 side = 0 for i in range(0, len(screen_borders)): p1 = screen_borders[i] p2 = screen_borders[(i + 1) % len(screen_borders)] hit = ray_light_to_point.cast([p1, p2]) if hit: d = hit.copy() d -= polygon[point] d = d.magnitude if d < closest_d: closest_point = hit.copy() closest_d = d side = i break if closest_point: casted_points[point] = closest_point.copy() touched_side[point] = side for point in range(0, len(polygon)): if casted_points[point] is None or casted_points[ (point + 1) % len(polygon)] is None: continue points = [point, (point + 1) % len(polygon)] if touched_side[points[0]] > touched_side[points[1]]: t = points[0] points[0] = points[1] points[1] = t extra_points = [] if abs(touched_side[points[0]] - touched_side[points[1]]) == 2: # Parallel touching if touched_side[points[0]] % 2 == 0: # Horizontal d = casted_points[points[0]].x if d > light_point.x: extra_points.append(screen_borders[1].list()) extra_points.append(screen_borders[2].list()) else: extra_points.append(screen_borders[0].list()) extra_points.append(screen_borders[3].list()) else: # Vertical d = casted_points[points[0]].y if d > light_point.y: extra_points.append(screen_borders[2].list()) extra_points.append(screen_borders[3].list()) else: extra_points.append(screen_borders[0].list()) extra_points.append(screen_borders[1].list()) elif abs(touched_side[points[0]] - touched_side[points[1]]) == 1: # Contiguous sides extra_points.append( screen_borders[touched_side[points[1]]].list()) elif abs(touched_side[points[0]] - touched_side[points[1]]) == 3: # Contiguous sides extra_points.append(screen_borders[0].list()) polygon_points = [ polygon[points[0]].list(), casted_points[points[0]].list() ] polygon_points.extend(extra_points) polygon_points.extend( [casted_points[points[1]].list(), polygon[points[1]].list()]) try: pygame.draw.polygon(surface, color, polygon_points) except Exception as e: print polygon_points raise e return
def testRaysOppositeEachother(self): ray1 = Ray({"x": 0, "y": 0}, 0, 1) ray2 = Ray({"x": 1, "y": 0}, pi, 1) assert ray1 == ray2
from Vector import Vector from Ray import Ray from Triangle import Triangle from Sphere import Sphere from Triangle import Triangle from Screen2D import Screen2D from Viewport import Viewport from PointLight import PointLight import math import sys ray = Ray(origin=Vector(0.0, 0.0, 0.0), ray_dir=Vector(1.0, 1.0, 0.5)) sphere = Sphere(position=Vector(2, 2, 2), radius=1.0) t_min = sphere.get_intersect(ray_origin=Vector(0.0, 0.0, 0.0), ray_dir=Vector(1.0, 1.0, 0.5)) print t_min intersect_point = ray.origin.clone().add( ray.ray_dir.clone().constant_multiply(t_min)) normal_vector = sphere.surface_normal(point=intersect_point.clone(), ray_origin=ray.origin.clone(), ray_dir=ray.ray_dir.clone()) # print "normal_vector", normal_vector # print "ray_dir", ray.ray_dir # print "ray_dir_normalize", ray.ray_dir.normalize() # print "intersect_point", intersect_point def Refraction(ray, air_ind, glass_ind, env="air"): sphere = Sphere(position=Vector(2, 2, 2), radius=1.0) print "ray.ray_origin", ray.origin print "ray.ray_dir", ray.ray_dir
def get_intersect(self, ray_origin=Vector(0, 0, 0), ray_dir=Vector(1, 1, 1)): """ this method returns the point where a Ray and a sphere intersects. if the Ray does not intersect with the sphere the function returns False following are the calculation steps: (1) the equation of a sphere x^2 + y^2 + z^2 = R^2 (2) the equation of a ray P = O + tD (O is the origin of the ray and D is the normalized vector which corresponds to the ray direction and P is a point at the end of the ray based on the scaling factor t. t is in fact the parametric distance from the origin of the ray to the point of interest along the ray) (3) if x, y, and z in equation 1 are the coordinates of point P in equation 2: P^2 - R^2 = 0 (when the sphere is not centered at the origin, equation 2 is equal to |P - C|^2 - R^2 = 0 (equation 3) (4) equation 3 can be rewritten as |O + tD - C|^2 - R^2 = 0 (equation 4), where C is the center of the sphere in space (5) equation 4 is a quadratic function where a = 1 (dot product of a normalized vector with itself) and b = 2D(O-C) and c = |O-C|^2 - R^2. (6) delta = b^2 - 4*a*c t1, t2 = (-b +- sqrt(delta)/2a if delta > 0, the ray intersects the sphere in two points (t1 and t2) if delta = 0, the ray intersects the sphere in one point only (t1=t2) if delta < 0, the ray does not intersect with the sphere. """ ray = Ray(ray_origin, ray_dir) a = ray.ray_dir.dot( ray.ray_dir ) #a = D^2 = 1 (D is a normalized vector for ray direction) vector_O_C = ray.origin.clone().sub(self.position.clone( )) #the vector between origin of the ray and the center of the circle scaled_ray_direction = ray.ray_dir.clone().constant_multiply(2) # 2*D b = scaled_ray_direction.dot(vector_O_C) mag_vector_O_C = vector_O_C.mag() c = (mag_vector_O_C)**2 - (self.radius)**2 if solve_quadratic(a, b, c) is None: return False t1, t2 = solve_quadratic(a, b, c) if t1 > 0 and t2 > 0: if t1 < t2: return t1 return t2 elif t1 == t2: return t1 elif t1 > 0 and t2 < 0: return t1 elif t2 > 0 and t1 < 0: return t2 else: return False
def getRay(self, x, y): xcomp = self.s * ((x * self.pixelWidth) - (self.width / 2)) ycomp = self.u * ((y * self.pixelHeight) - (self.height / 2)) return Ray(self.e, self.f + xcomp + ycomp)
#We need this to calculate the camera right vector distance = toLookAt.length() toLookAtNormalized = toLookAt.toNormalized() width = math.cos(camera.fov) * distance height = math.cos(camera.fov) * distance #width and height should be the same unless we set different fovs for width and height # TODO: This should be toLookAtNormalize cameraRight = toLookAtNormalized.cross(camera.up) rightWorld = cameraRight.toScaled(width * xPercent) upWorld = camera.up.toScaled(height * yPercent) pixelLookAt = Point3D.fromVector(upWorld.plus(rightWorld)) #We now have our world look at points #We need to generate our look at ray and NORMALIZE IT!!! ray = Ray(camera.origin, pixelLookAt.minus(camera.origin).toNormalized()) # jitter the ray r = 0 g = 0 b = 0 samples = 12 for i in range(samples): ray2 = Ray(Point3D.fromVector(ray.origin.vector.clone()), ray.direction.clone()) ray2.direction.x += (random.random() - .5)*2*width/frame.width ray2.direction.y += (random.random() - .5)*2*height/frame.height ray2.direction = ray2.direction.toNormalized() color = getColor(ray2, None, 4) r += color.x g += color.y
p2, p3, np.array([1.0, 1.0, 1.0]), 0.8, 0.75, 0.05, 100, 0.6, "tri2", texture="triangle1.jpg", origin=1, hori=np.array([20.0, 0.0, 0.0]), verti=np.array([0.0, -20.0, 0.0])) tree.insert(tri) tree.insert(tri1) ray = Ray(np.array([0.0, 0.0, 0.0]), np.array([0.0, 0.0, -1.0])) point = np.array([0.0, -10.0, 0.0]) normal = np.array([0.0, -1.0, 0.0]) p = Plane(point, normal, np.array([1.0, 1.0, 1.0]), 1.0, 1.0, 0.05, 150, 0.6, "plane1") tree.insert(p) radius = 3 center = np.array([-10.0, -7.0, -17.0]) s = Sphere(radius, center, np.array([0.289, 0.2845, 0.3779]), 1.0, 1.0, 1.0, 4,
def testGetEndPointOnAngle(self): expectedPoint = {"x": 1, "y": 1} ray = Ray({"x": 0, "y": 0}, pi / 4, 2**0.5) assert Ray.pointsVeryClose(ray.getEndPoint(), expectedPoint)
def testEmptyConstructor(self): assertThrows(lambda: Ray(), Exception)
def testGetEndPointBackwards(self): expectedPoint = {"x": 1, "y": 1} ray = Ray({"x": 5, "y": 4}, atan(3 / 4) + pi, 5) assert Ray.pointsVeryClose(ray.getEndPoint(), expectedPoint)
def testGetEndPointVerticalAngle(self): expectedPoint = {"x": 5, "y": 5} ray = Ray({"x": 5, "y": 0}, pi / 2, 5) assert Ray.pointsVeryClose(ray.getEndPoint(), expectedPoint)
def testGetEndPointHorizontalAngle(self): expectedPoint = {"x": 5, "y": 5} ray = Ray({"x": 0, "y": 5}, 0, 5) assert Ray.pointsVeryClose(ray.getEndPoint(), expectedPoint)
phi = numpy.arange(tube.start, tube.end, 0.1) rho = numpy.asarray([tube.turn_radius]*numpy.size(phi)) w = (rho) * numpy.cos(phi) x = (rho) * numpy.sin(phi) y = (rho + 2) * numpy.cos(phi) z = (rho + 2) * numpy.sin(phi) return w, x, y, z #define tube tube_ex = SemicircleTube(1, 1000, 1.0, 0, math.pi*2) #define ray ray_ex = Ray(theta_i=.04, p_i=(math.pi,1001)) # propogate for i in range(10): point = interact(tube_ex, ray_ex) # graph tx, ty = tube_ex.plot() #rx, ry = ray_ex.pol_plot() rrx, rry = ray_ex.car_plot() a,b,c,d = plot(tube_ex) #matplotlib.pyplot.plot() #matplotlib.pyplot.plot(rrx, rry) #matplotlib.pyplot.plot(tx, ty) #matplotlib.pyplot.plot(tx, ty)
from Sphere import Sphere from Ray import Ray from ViewPort import ViewPort #create a viewport and image v = ViewPort(500,500) im = Image.new("RGB",(v.w,v.h)) pix = im.load() #define a sphere radius = 1.0 center = np.array([0,0, -2.5]) s = Sphere(radius,center,np.array([255,0,0])) #define a ray ray = Ray(np.array([0,0,0]),np.array([0,0,-1])) # define a light direction ldir = np.array([0,0,1]) #light direction kd = 0.75 #reflectivity illum = 1.0 #light luminosity def phongDiffuse(x,n,mat): """Implements a Phong-style diffuse shading function Args: x: is a point on a surface n: is the unit normal at that point mat: is an RGB tuple of the surface color