def __init__(self, image_width = 0, image_height = 0, cam = None): self.shapes = [] self.lights = [] if cam is None: self.camera = Camera() else: self.camera = cam self.image_width = image_width self.image_height = image_height self.image_plane = ImagePlane() self.image_plane.setDims(self.camera.fov, self.camera.focal_length, image_width / image_height) self.image_plane.center = self.camera.position - (self.camera.normal() * self.camera.focal_length) self.image_plane.origin = self.image_plane.center - (self.camera.u() * (self.image_plane.width / 2)) - (self.camera.v() * (self.image_plane.height / 2)) self.background_color = color.BLACK self.ambient_light = color.scale(color.WHITE, 0.2) log.info("Scene initialized")
class Scene: def __init__(self, image_width = 0, image_height = 0, cam = None): self.shapes = [] self.lights = [] if cam is None: self.camera = Camera() else: self.camera = cam self.image_width = image_width self.image_height = image_height self.image_plane = ImagePlane() self.image_plane.setDims(self.camera.fov, self.camera.focal_length, image_width / image_height) self.image_plane.center = self.camera.position - (self.camera.normal() * self.camera.focal_length) self.image_plane.origin = self.image_plane.center - (self.camera.u() * (self.image_plane.width / 2)) - (self.camera.v() * (self.image_plane.height / 2)) self.background_color = color.BLACK self.ambient_light = color.scale(color.WHITE, 0.2) log.info("Scene initialized") def fireRay(self, ray, origin, depth): if depth >= MAX_TRACE_DEPTH: log.info("Max trace depth reached") return color.BLACK intersection = None for shape in self.shapes: try: t, point = shape.intersection(ray, origin) except TypeError: continue if intersection is None or t < intersection[0]: intersection = (t, point, shape) if intersection: return self.calculateShading(intersection, depth) return self.background_color def getColorAt(self, x, y): log.debug("Firing ray through ({0}, {1})".format(x, y)) ray = self.getCameraRay(x,y) return self.fireRay(ray, self.camera.position, 1) def getPixelLocation(self, x, y): pixel_width = self.image_plane.width / self.image_width pixel_height = self.image_plane.height / self.image_height # Add 0.5 to pixel numbers to get center of pixel return self.image_plane.origin + (self.camera.u() * (x + 0.5) * pixel_width) + (self.camera.v() * (y + 0.5) * pixel_height) def getCameraRay(self, x, y): return (self.getPixelLocation(x, y) - self.camera.position).normalized() def calculateShading(self, intersection, depth): point = intersection[1] shape = intersection[2] normal = shape.getNormalAtPoint(point) incident_vector = (point - self.camera.position).normalized() shapeColor = shape.getColorAtPoint(point) log.debug("Shading {0} {1}".format(shape, point)) # ambient portion result = color.component_scale(shapeColor, [x/255 for x in self.ambient_light]) specular_lights = [] for light in self.lights: # shadow lightDir = (light.position - point).normalized() lightDist = (light.position - point).magnitude() shadowed = False for shape in self.shapes: try: dist, point = shape.intersection(lightDir, point + (normal * 0.001)) if dist <= lightDist: shadowed = True log.debug("Shadowed by {0} at distance {1}".format(shape, dist)) break except TypeError: pass if not shadowed: # diffuse diffIntensity = min(max(lightDir * normal, 0), 1) diff = color.scale(light.color, diffIntensity) diff = color.component_scale(shapeColor, [x/255 for x in diff]) log.debug("Diff: {0} (intensity {1})".format(diff, diffIntensity)) result = color.add(result, diff) if diffIntensity > 0: specular_lights.append(light) # reflection if shape.material.reflection > 0: reflective_vector = incident_vector - (2 * (incident_vector * normal) * normal) reflective_component = self.fireRay(reflective_vector, point + (normal * 0.001), depth + 1) result = color.scale(result, 1 - shape.material.reflection) result = color.add(result, color.scale(reflective_component, shape.material.reflection)) for light in specular_lights: # blinn-phong viewDir = -incident_vector halfDir = (lightDir + viewDir).normalized() specAngle = min(max(halfDir * normal, 0), 1) specIntensity = specAngle ** shape.material.hardness spec = color.scale(light.color, specIntensity) spec = color.scale(spec, shape.material.specular) result = color.add(result, spec) return result