Example #1
0
    def from_intersection(cls, p, dp_du, dp_dv, dn_du, dn_dv, uu, vv, shape=None):
        """Construct a DifferentialGeometry instance.

        Arguments:
        p : Point
        dp_du : Vector
        dp_dv : Vector
        dn_du : Normal
        dn_dv : Normal
        uu : float
        vv : float
        shape : optional Shape

        """
        # call default constructor
        diff_geom = cls()

        diff_geom.p = p
        diff_geom.dp_du = dp_du
        diff_geom.dp_dv = dp_dv
        diff_geom.dn_du = dn_du
        diff_geom.dn_dv = dn_dv

        diff_geom.nn = Normal.from_vector(normalize(cross(dp_du, dp_dv)))
        diff_geom.u = uu
        diff_geom.v = vv
        diff_geom.shape = shape

        if shape and \
           (shape.reverse_orientation ^ shape.transform_swap_handedness):
            diff_geom.nn *= -1.0

        return diff_geom
Example #2
0
    def sample_p(self, p, u1, u2):
        """Sample at point p."""
        # Compute coordinate system for sphere sampling
        p_center = self.object_to_world(Point(0, 0, 0))
        wc = normalize(p_center - p)
        wc_x, wc_y = coordinate_system(wc)

        # Sample uniformly on sphere if $\pt{}$ is inside it
        if (distance_squared(p, p_center) - self.radius * self.radius) < 1e-4:
            return self.sample(u1, u2)

        # Sample sphere uniformly inside subtended cone
        sin_theta_max2 = self.radius * self.radius / distance_squared(
            p, p_center)
        cos_theta_max = math.sqrt(max(0.0, 1.0 - sin_theta_max2))
        raise Exception("next_line")
        # r = Ray(p, uniform_sample_cone(u1, u2, cos_theta_max, wcX, wcY, wc), 1e-3)
        r = Ray(p)
        intersect, t_hit, ray_epsilon, dg_sphere = self.intersect(r)
        if not intersect:
            t_hit = dot(p_center - p, normalize(r.d))
        ps = r(t_hit)
        ns = Normal(normalize(ps - p_center))
        if (self.reverse_orientation):
            ns *= -1.0
        return ps, ns
Example #3
0
 def sample(self, u1, u2):
     """Sample the shape."""
     raise Exception("check_next_line")
     p = Point(0, 0, 0) + self.radius * 1.0  # uniform_sample_sphere(u1, u2)
     ns = normalize(self.object_to_world(Normal(p.x, p.y, p.z)))
     if (self.reverse_orientation):
         ns *= -1.0
     return self.object_to_world(p), ns
Example #4
0
    def test_transform(self):
        p = Point(1, 2, 3)
        p2 = translate(Point(10, 20, 30))(p)
        self.assertTrue(isinstance(p2, Point))
        self.assertEqual(p2, Point(11, 22, 33))
        
        v = Vector(1, 2, 3)
        v2 = translate(Point(10, 20, 30))(v)
        self.assertTrue(isinstance(v2, Vector))
        self.assertEqual(v2, Vector(1, 2, 3))

        self.assertEqual(scale(2, 3, 4)(Point(1, 2, 3)),
                         Point(2, 6, 12))
        self.assertEqual(scale(2, 3, 4)(Vector(1, 2, 3)),
                         Vector(2, 6, 12))
        self.assertEqual(rotate(90, Vector(0, 1, 0))(Normal(1, 0, 0)),
                         Normal(0, 0, -1))
Example #5
0
    def test_normal(self):
        # operator[]
        n = Normal(1.0, 2.0, 3.0)
        self.assertEqual(n[0], 1.0)
        self.assertEqual(n[1], 2.0)
        self.assertEqual(n[2], 3.0)

        # face_forward
        n2 = Normal(1, 0, 0)
        v = Vector(-0.5, -0.1, 0.2)
        self.assertEqual(face_forward(n, v), -n)

        # operator[] for assignments
        n = Normal(1.0, 2.0, 3.0)
        for i in range(3):
            n[i] = 9.0
            self.assertEqual(n[i], 9.0)
Example #6
0
    def __init__(self):
        """Default constructor for DifferentialGeometry."""
        self.p = Point()
        self.nn = Normal()

        self.u = 0.0
        self.v = 0.0

        self.shape = None

        self.dp_du = Vector()
        self.dp_dv = Vector()

        self.dn_du = Normal()
        self.dn_dv = Normal()

        self.dp_dx = Vector()
        self.dp_dy = Vector()

        self.du_dx = 0.0
        self.dv_dx = 0.0

        self.du_dy = 0.0
        self.dv_dy = 0.0
Example #7
0
    def from_intersection(cls,
                          p,
                          dp_du,
                          dp_dv,
                          dn_du,
                          dn_dv,
                          uu,
                          vv,
                          shape=None):
        """Construct a DifferentialGeometry instance.

        Arguments:
        p : Point
        dp_du : Vector
        dp_dv : Vector
        dn_du : Normal
        dn_dv : Normal
        uu : float
        vv : float
        shape : optional Shape

        """
        # call default constructor
        diff_geom = cls()

        diff_geom.p = p
        diff_geom.dp_du = dp_du
        diff_geom.dp_dv = dp_dv
        diff_geom.dn_du = dn_du
        diff_geom.dn_dv = dn_dv

        diff_geom.nn = Normal.from_vector(normalize(cross(dp_du, dp_dv)))
        diff_geom.u = uu
        diff_geom.v = vv
        diff_geom.shape = shape

        if shape and \
           (shape.reverse_orientation ^ shape.transform_swap_handedness):
            diff_geom.nn *= -1.0

        return diff_geom
Example #8
0
    def intersect(self, r):
        """Intersect the ray with the shape."""
        # Transform _Ray_ to object space
        ray = self.world_to_object(r)

        # Compute quadratic sphere coefficients
        A = ray.d.x * ray.d.x + ray.d.y * ray.d.y + ray.d.z * ray.d.z
        B = 2 * (ray.d.x * ray.o.x + ray.d.y * ray.o.y + ray.d.z * ray.o.z)
        C = ray.o.x*ray.o.x + ray.o.y*ray.o.y + \
                  ray.o.z*ray.o.z - self.radius*self.radius

        # Solve quadratic equation for _t_ values
        found, t0, t1 = quadratic(A, B, C)
        if not found:
            return False, float('inf'), 0.0, None

        # Compute intersection distance along ray
        if (t0 > ray.maxt or t1 < ray.mint):
            return False, float('inf'), 0.0, None
        t_hit = t0
        if (t0 < ray.mint):
            t_hit = t1
            if (t_hit > ray.maxt):
                return False, float('inf'), 0.0, None

        # Compute sphere hit position and $\phi$
        phi_t = ray(t_hit)
        if (phi_t.x == 0.0 and phi_t.y == 0.0):
            phi_t.x = 1e-5 * self.radius
        phi = math.atan2(phi_t.y, phi_t.x)
        if (phi < 0.0):
            phi += 2.0 * math.pi

        # Test sphere intersection against clipping parameters
        if ((self.z_min > -self.radius and phi_t.z < self.z_min) or \
            (self.z_max <  self.radius and phi_t.z > self.z_max) or \
            phi > self.phi_max):
            if (t_hit == t1):
                return False, float('inf'), 0.0, None
            if (t1 > ray.maxt):
                return False, float('inf'), 0.0, None
            t_hit = t1
            # Compute sphere hit position and $\phi$
            phi_t = ray(t_hit)
            if (phi_t.x == 0.0 and phi_t.y == 0.0):
                phi_t.x = 1e-5 * self.radius
            phi = math.atan2(phi_t.y, phi_t.x)
            if (phi < 0.0):
                phi += 2.0 * math.pi
            if ((self.z_min > -self.radius and phi_t.z < self.z_min) or \
                (self.z_max <  self.radius and phi_t.z > self.z_max) or \
                phi > self.phi_max):
                return False, float('inf'), 0.0, None

        # Find parametric representation of sphere hit
        u = phi / self.phi_max
        theta = math.acos(clamp(phi_t.z / self.radius, -1.0, 1.0))
        v = (theta - self.theta_min) / (self.theta_max - self.theta_min)

        # Compute sphere $\dpdu$ and $\dpdv$
        zradius = math.sqrt(phi_t.x * phi_t.x + phi_t.y * phi_t.y)
        inv_z_radius = 1.0 / zradius
        cos_phi = phi_t.x * inv_z_radius
        sin_phi = phi_t.y * inv_z_radius
        dpdu = Vector(-self.phi_max * phi_t.y, self.phi_max * phi_t.x, 0)
        dpdv = (self.theta_max-self.theta_min) * \
               Vector(phi_t.z * cos_phi,
                      phi_t.z * sin_phi,
                      -self.radius * math.sin(theta))

        # Compute sphere $\dndu$ and $\dndv$
        d2Pduu = -self.phi_max * self.phi_max * Vector(phi_t.x, phi_t.y, 0)
        d2Pduv = (self.theta_max - self.theta_min) * phi_t.z * self.phi_max * \
                 Vector(-sin_phi, cos_phi, 0.0)
        d2Pdvv = -(self.theta_max - self.theta_min) * \
                 (self.theta_max - self.theta_min) * \
                 Vector(phi_t.x, phi_t.y, phi_t.z)

        # Compute coefficients for fundamental forms
        E = dot(dpdu, dpdu)
        F = dot(dpdu, dpdv)
        G = dot(dpdv, dpdv)
        N = normalize(cross(dpdu, dpdv))
        e = dot(N, d2Pduu)
        f = dot(N, d2Pduv)
        g = dot(N, d2Pdvv)

        # Compute $\dndu$ and $\dndv$ from fundamental form coefficients
        invEGF2 = 1.0 / (E * G - F * F)
        dndu = Normal.from_vector((f*F - e*G) * invEGF2 * dpdu + \
                                  (e*F - f*E) * invEGF2 * dpdv)
        dndv = Normal.from_vector((g*F - f*G) * invEGF2 * dpdu + \
                                  (f*F - g*E) * invEGF2 * dpdv)

        # Initialize _DifferentialGeometry_ from parametric information
        o2w = self.object_to_world
        dg = DifferentialGeometry.from_intersection(o2w(phi_t), o2w(dpdu),
                                                    o2w(dpdv), o2w(dndu),
                                                    o2w(dndv), u, v, self)

        # Compute _rayEpsilon_ for quadric intersection
        ray_epsilon = 5e-4 * t_hit
        return True, t_hit, ray_epsilon, dg
Example #9
0
    def __call__(self, elt):
        """Overload the operator().

        Supported operations:
        * Transform(Point)
        * Transform(Vector)
        * Transform(Normal)
        * Transform(Ray)
        * Transform(RayDifferential)
        * Transform(BBox)
        
        """
        if isinstance(elt, Point):
            x = elt.x
            y = elt.y
            z = elt.z
            xp = self.m.m[0][0] * x + self.m.m[0][1] * y + self.m.m[0][
                2] * z + self.m.m[0][3]
            yp = self.m.m[1][0] * x + self.m.m[1][1] * y + self.m.m[1][
                2] * z + self.m.m[1][3]
            zp = self.m.m[2][0] * x + self.m.m[2][1] * y + self.m.m[2][
                2] * z + self.m.m[2][3]
            wp = self.m.m[3][0] * x + self.m.m[3][1] * y + self.m.m[3][
                2] * z + self.m.m[3][3]
            if wp == 1.0:
                return Point(xp, yp, zp)
            else:
                return Point(xp, yp, zp) / wp
        elif isinstance(elt, Vector):
            x = elt.x
            y = elt.y
            z = elt.z
            xp = self.m.m[0][0] * x + self.m.m[0][1] * y + self.m.m[0][2] * z
            yp = self.m.m[1][0] * x + self.m.m[1][1] * y + self.m.m[1][2] * z
            zp = self.m.m[2][0] * x + self.m.m[2][1] * y + self.m.m[2][2] * z
            return Vector(xp, yp, zp)
        elif isinstance(elt, Normal):
            x = elt.x
            y = elt.y
            z = elt.z
            return Normal(
                self.m_inv.m[0][0] * x + self.m_inv.m[1][0] * y +
                self.m_inv.m[2][0] * z, self.m_inv.m[0][1] * x +
                self.m_inv.m[1][1] * y + self.m_inv.m[2][1] * z,
                self.m_inv.m[0][2] * x + self.m_inv.m[1][2] * y +
                self.m_inv.m[2][2] * z)
        elif isinstance(elt, RayDifferential):
            ray = RayDifferential.from_ray_differential(elt)
            ray.o = self(ray.o)
            ray.d = self(ray.d)
            ray.rx_origin = self(ray.rx_origin)
            ray.ry_origin = self(ray.ry_origin)
            ray.rx_direction = self(ray.rx_direction)
            ray.ry_direction = self(ray.ry_direction)
            return ray
        elif isinstance(elt, Ray):
            ray = Ray.from_ray(elt)
            ray.o = self(ray.o)
            ray.d = self(ray.d)
            return ray
        elif isinstance(elt, BBox):
            ret = BBox(self(Point(elt.p_min.x, elt.p_min.y, elt.p_min.z)))
            ret = union(ret, self(Point(elt.p_max.x, elt.p_min.y,
                                        elt.p_min.z)))
            ret = union(ret, self(Point(elt.p_min.x, elt.p_max.y,
                                        elt.p_min.z)))
            ret = union(ret, self(Point(elt.p_min.x, elt.p_min.y,
                                        elt.p_max.z)))
            ret = union(ret, self(Point(elt.p_min.x, elt.p_max.y,
                                        elt.p_max.z)))
            ret = union(ret, self(Point(elt.p_max.x, elt.p_max.y,
                                        elt.p_min.z)))
            ret = union(ret, self(Point(elt.p_max.x, elt.p_min.y,
                                        elt.p_max.z)))
            ret = union(ret, self(Point(elt.p_max.x, elt.p_max.y,
                                        elt.p_max.z)))
            return ret
Example #10
0
    def intersect(self, r):
        """Intersect the ray with the shape."""
        # Transform _Ray_ to object space
        ray = self.world_to_object(r)

        # Compute quadratic sphere coefficients
        A = ray.d.x * ray.d.x + ray.d.y * ray.d.y + ray.d.z * ray.d.z
        B = 2 * (ray.d.x * ray.o.x + ray.d.y * ray.o.y + ray.d.z * ray.o.z)
        C = ray.o.x * ray.o.x + ray.o.y * ray.o.y + ray.o.z * ray.o.z - self.radius * self.radius

        # Solve quadratic equation for _t_ values
        found, t0, t1 = quadratic(A, B, C)
        if not found:
            return False, float("inf"), 0.0, None

        # Compute intersection distance along ray
        if t0 > ray.maxt or t1 < ray.mint:
            return False, float("inf"), 0.0, None
        t_hit = t0
        if t0 < ray.mint:
            t_hit = t1
            if t_hit > ray.maxt:
                return False, float("inf"), 0.0, None

        # Compute sphere hit position and $\phi$
        phi_t = ray(t_hit)
        if phi_t.x == 0.0 and phi_t.y == 0.0:
            phi_t.x = 1e-5 * self.radius
        phi = math.atan2(phi_t.y, phi_t.x)
        if phi < 0.0:
            phi += 2.0 * math.pi

        # Test sphere intersection against clipping parameters
        if (
            (self.z_min > -self.radius and phi_t.z < self.z_min)
            or (self.z_max < self.radius and phi_t.z > self.z_max)
            or phi > self.phi_max
        ):
            if t_hit == t1:
                return False, float("inf"), 0.0, None
            if t1 > ray.maxt:
                return False, float("inf"), 0.0, None
            t_hit = t1
            # Compute sphere hit position and $\phi$
            phi_t = ray(t_hit)
            if phi_t.x == 0.0 and phi_t.y == 0.0:
                phi_t.x = 1e-5 * self.radius
            phi = math.atan2(phi_t.y, phi_t.x)
            if phi < 0.0:
                phi += 2.0 * math.pi
            if (
                (self.z_min > -self.radius and phi_t.z < self.z_min)
                or (self.z_max < self.radius and phi_t.z > self.z_max)
                or phi > self.phi_max
            ):
                return False, float("inf"), 0.0, None

        # Find parametric representation of sphere hit
        u = phi / self.phi_max
        theta = math.acos(clamp(phi_t.z / self.radius, -1.0, 1.0))
        v = (theta - self.theta_min) / (self.theta_max - self.theta_min)

        # Compute sphere $\dpdu$ and $\dpdv$
        zradius = math.sqrt(phi_t.x * phi_t.x + phi_t.y * phi_t.y)
        inv_z_radius = 1.0 / zradius
        cos_phi = phi_t.x * inv_z_radius
        sin_phi = phi_t.y * inv_z_radius
        dpdu = Vector(-self.phi_max * phi_t.y, self.phi_max * phi_t.x, 0)
        dpdv = (self.theta_max - self.theta_min) * Vector(
            phi_t.z * cos_phi, phi_t.z * sin_phi, -self.radius * math.sin(theta)
        )

        # Compute sphere $\dndu$ and $\dndv$
        d2Pduu = -self.phi_max * self.phi_max * Vector(phi_t.x, phi_t.y, 0)
        d2Pduv = (self.theta_max - self.theta_min) * phi_t.z * self.phi_max * Vector(-sin_phi, cos_phi, 0.0)
        d2Pdvv = (
            -(self.theta_max - self.theta_min) * (self.theta_max - self.theta_min) * Vector(phi_t.x, phi_t.y, phi_t.z)
        )

        # Compute coefficients for fundamental forms
        E = dot(dpdu, dpdu)
        F = dot(dpdu, dpdv)
        G = dot(dpdv, dpdv)
        N = normalize(cross(dpdu, dpdv))
        e = dot(N, d2Pduu)
        f = dot(N, d2Pduv)
        g = dot(N, d2Pdvv)

        # Compute $\dndu$ and $\dndv$ from fundamental form coefficients
        invEGF2 = 1.0 / (E * G - F * F)
        dndu = Normal.from_vector((f * F - e * G) * invEGF2 * dpdu + (e * F - f * E) * invEGF2 * dpdv)
        dndv = Normal.from_vector((g * F - f * G) * invEGF2 * dpdu + (f * F - g * E) * invEGF2 * dpdv)

        # Initialize _DifferentialGeometry_ from parametric information
        o2w = self.object_to_world
        dg = DifferentialGeometry.from_intersection(o2w(phi_t), o2w(dpdu), o2w(dpdv), o2w(dndu), o2w(dndv), u, v, self)

        # Compute _rayEpsilon_ for quadric intersection
        ray_epsilon = 5e-4 * t_hit
        return True, t_hit, ray_epsilon, dg