예제 #1
0
    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
예제 #2
0
    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()
예제 #3
0
 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
예제 #4
0
    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)
예제 #5
0
    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
예제 #6
0
    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)
예제 #7
0
    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
예제 #8
0
 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)
예제 #9
0
    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)))
예제 #10
0
    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
예제 #11
0
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()
예제 #12
0
 def intersectAll(self, ray, tmin, tmax):
     h = self.intersect(ray, r.Hit(), tmin, tmax)
     return h