def intersectAll(self, ray, tmin, tmax): g = Group() g.addObject(self.obj1) g.addObject(self.obj2) hits = g.intersectAll(ray, tmin, tmax) # should sort (hit/obj) pairs by hit.t values hits = sorted(hits, key=lambda extendedHit: extendedHit[0].t) if hits == []: return r.Hit() else: if self.op == Operation.UNION: return (hits) elif self.op == Operation.INTERSECTION: # intersection: space belonging to both objects ints = self.intersectionHits(hits, tmin, tmax) ints = sorted(ints, key=lambda hit: hit.t) return ints elif self.op == Operation.DIFFERENCE: difs = self.differenceHits(hits, tmin, tmax) difs = sorted(difs, key=lambda hit: hit.t) if difs == []: return r.Hit() else: return difs
def intersectSide(self, ray, tmin, tmax, points): v1 = points[1].sub(points[0]) v2 = points[2].sub(points[0]) norm = v1.cross(v2).normalize() p = ray.direction.cross(v2) q = (ray.origin.sub(points[0])).cross(v1) # denominator used to find scalar for Barycentric coord den = p.dot(v1) if den == 0: return r.Hit() else: # find scalar, beta, and gamma s = 1 / float(den) bet = s * p.dot(ray.origin.sub(points[0])) gam = s * q.dot(ray.direction) # check barycentric coords for inside-square validity if (bet < 0) | (gam < 0) | (bet > 1) | (gam > 1): return r.Hit() else: # calculate t and return new hit if inside square t = s * q.dot(v2) if (tmin < t) & (t < tmax): return r.Hit(t, self.color, norm) else: return r.Hit()
def intersect(self, ray, tmin, tmax): closest_hit = r.Hit() for o in self.objs: h = o.intersect(ray, closest_hit, tmin, tmax) if (h.t < closest_hit.t) & (tmin < h.t) & (h.t < tmax): closest_hit = h return closest_hit
def intersect(self, ray, hit, tmin, tmax): # transform ray by inverse matrix inv = self.mat.inverse() tro = inv.vecMult(ray.getOrigin()) # tro = Transformed Ray Origin trd = inv.vecMult(ray.getDirection(), 0) # trd = Transformed Ray Direction # normalize transformed direction trdn = trd.normalize() # define transformed ray as a ray tr = r.Ray(trdn, tro) s = trd.length() # Get object hit with transformed ray h = self.obj.intersect(tr, hit, tmin * s, tmax * s) if h.t == float('inf'): return hit else: # transform the normal by transposed inverse matrix invtr = inv.transpose() tnor = invtr.vecMult(h.normal).normalize() t = h.t / float(s) return r.Hit(t, h.color, tnor)
def differenceHits(self, hits, tmin, tmax): ''' This code is used in both intersect and intersectAll. It is defined as a function to avoid duplication of code. takes a list of (hit/object) tuples returns a list of hits ''' inObj1 = False inObj2 = False difs = [] for h in hits: hobj = h[1] if (hobj == self.obj1): inObj1 = not (inObj1) elif (hobj == self.obj2): inObj2 = not (inObj2) else: # if/elif/else used instead of if/else because I felt like it (there is no actual else case) print("Something's wrong with CSG difference!") if (inObj1) & (inObj2 == False) & (tmin < h[0].t) & (h[0].t < tmax): # get the solid geometry from shape being exited if (hobj == self.obj2): difs.append(r.Hit(h[0].t, h[0].color, h[0].normal.neg())) else: difs.append(h[0]) return difs
def intersect(self, ray, hit, tmin, tmax): # move this section to initializer v1 = self.p2.sub(self.p1) v2 = self.p3.sub(self.p1) triNorm = v1.cross(v2).normalize() p = ray.direction.cross(v2) q = (ray.origin.sub(self.p1)).cross(v1) # denominator used to find scalar for Barycentric coord den = p.dot(v1) if den == 0: return hit else: # find scalar, beta, and gamma s = 1 / float(den) bet = s * p.dot(ray.origin.sub(self.p1)) gam = s * q.dot(ray.direction) # check barycentric coords for inside-triangle validity if (bet < 0) | (gam < 0) | (bet + gam > 1): return hit else: # calculate t and return new hit if inside triangle t = s * q.dot(v2) return r.Hit(t, self.color, triNorm)
def intersect(self, ray, hit, tmin, tmax): """intersect takes a ray, hit, tmin, and tmax. intersect returns a new Hit object that represents the closest intersection between tmin and tmax of the ray and the object if that intersection is closer than hit. hit is returned if no intersection is found between tmin and tmax or the intersection is not closer than hit.""" ro = ray.getOrigin().sub(self.center) b = 2 * ray.getDirection().dot(ro) c = ro.dot(ro) - self.radius * self.radius d = b * b - 4 * c if (d < 0): # no real positive root return hit # no new hit d = math.sqrt(d) t1 = (-b + d) * .5 t2 = (-b - d) * .5 if (t2 < t1): # make sure t1<t2 t1, t2 = t2, t1 #make t the smallest valid t value if tmin <= t1 <= tmax: t = t1 elif tmin <= t2 <= tmax: t = t2 else: # no valid t return hit if (t < hit.getT()): # closer than current t # VALID HIT norm = ray.pointAtParameter(t).sub(self.center) return r.Hit(t, self.color, norm) else: return hit
def intersect(self, ray, hit, tmin, tmax): vd = self.normal.dot(ray.getDirection()) if vd >= 0: return hit else: vo = (self.normal.dot(ray.getOrigin()) + self.d) t = vo / vd return r.Hit(t, self.color, self.normal)
def intersectAll(self, ray, tmin, tmax): ''' used by CSG - needs all hits for an obj, not just closest ''' ro = ray.getOrigin().sub(self.center) b = 2 * ray.getDirection().dot(ro) c = ro.dot(ro) - self.radius * self.radius d = b * b - 4 * c if (d < 0): # no real positive root return r.Hit() # no new hit d = math.sqrt(d) t1 = (-b + d) * .5 t2 = (-b - d) * .5 if (t2 < t1): # make sure t1<t2 t1, t2 = t2, t1 norm1 = ray.pointAtParameter(t1).sub(self.center) norm2 = ray.pointAtParameter(t2).sub(self.center) return ((r.Hit(t1, self.color, norm1), r.Hit(t2, self.color, norm2)))
def intersect(self, ray, hit, tmin, tmax): g = Group() g.addObject(self.obj1) g.addObject(self.obj2) closestHit = r.Hit() hits = g.intersectAll(ray, tmin, tmax) # intersectAll returns a list of tuples (hit, object) if hits == []: return closestHit # should sort (hit/obj) pairs by hit.t values hits = sorted(hits, key=lambda extendedHit: extendedHit[0].t) if self.op == Operation.UNION: # since union is all hits for both objects, # use the closest hit (smallest t in hits) for h in hits: if (tmin <= h.t) & (h.t <= tmax): closestHit = h elif self.op == Operation.INTERSECTION: # intersection: space belonging to both objects ints = self.intersectionHits(hits, tmin, tmax) for h in ints: if h.t < closestHit.t: closestHit = h elif self.op == Operation.DIFFERENCE: # difference: space belonging to one object but not the other difs = self.differenceHits(hits, tmin, tmax) for h in difs: if h.t < closestHit.t: closestHit = h else: print("Invalid CSG operation.") return closestHit
def generateImage(width, height, camera, group, dist_min, dist_max, lights, renderMode, bgc=r.Vec3(1, 1, 1)): #create Raster ra = raster.Raster(width, height) #loop over all pixels for x in range(0, width): for y in range(0, height): #create Ray rayX = float(x) / width rayY = float(height - y) / height #ray = camera.screenToPlaneLocation(r.Point2D(rayX,rayY)) ray = camera.generateRay(r.Point2D(rayX, rayY)) #find closest hit h = group.intersect(ray, dist_min, dist_max) #in case no hit is found if h.t != float('inf'): #set pixel color based on render mode if renderMode == RenderMode.COLOR: color = h.color elif renderMode == RenderMode.DISTANCE: gray = (dist_max - h.t) / (dist_max - dist_min) color = r.Vec3(gray, gray, gray) elif renderMode == RenderMode.DIFFUSE: lsum = r.Vec3(0, 0, 0) intersectionPoint = ray.pointAtParameter(h.t) surfaceNormal = h.getNormal().normalize() for l in lights: lco = l.getIntensity(intersectionPoint, surfaceNormal) lsum.x += h.color.x * lco.x lsum.y += h.color.y * lco.y lsum.z += h.color.z * lco.z # Check lsum values (cap at 1) if lsum.x > 1: lsum.x = 1 if lsum.y > 1: lsum.y = 1 if lsum.z > 1: lsum.z = 1 color = lsum elif renderMode == RenderMode.SHADOWS: lsum = r.Vec3(0, 0, 0) intersectionPoint = ray.pointAtParameter(h.t) surfaceNormal = h.getNormal().normalize() for l in lights: lco = l.getIntensity(intersectionPoint, surfaceNormal) h2 = r.Hit() # Use 'try' to assume the light is directional, # so shadows can be calculated try: #mode = 'directional' ray2 = r.Ray(l.direction.normalize(), intersectionPoint) h2 = group.intersect(ray2, 0.001, dist_max) if h2.t == float('inf'): lsum.x += h.color.x * lco.x lsum.y += h.color.y * lco.y lsum.z += h.color.z * lco.z # if code throws an attribute error, light is ambient # (ambient lights have no attribute "direction") except AttributeError: #mode = 'ambient' lsum.x += h.color.x * lco.x lsum.y += h.color.y * lco.y lsum.z += h.color.z * lco.z # Check lsum values (cap at 1) if lsum.x > 1: lsum.x = 1 if lsum.y > 1: lsum.y = 1 if lsum.z > 1: lsum.z = 1 color = lsum else: print("invalid render mode") pass else: color = bgc # color up until this point must be a Vec3 between 0-1 color = color.scalarMult(255) color = (color.x, color.y, color.z) ra.setPixel(x, y, color) ra.display()
def intersectAll(self, ray, tmin, tmax): h = self.intersect(ray, r.Hit(), tmin, tmax) return h