示例#1
0
	def sample_p(self, pnt: 'geo.Point', u1: FLOAT, u2: FLOAT) -> ['geo.Point', 'geo.Normal']:
		"""
		uniformly sample the sphere
		visible (of certain solid angle)
		to the point
		"""
		# compute coords for sampling
		ctr = self.o2w(geo.Point(0., 0., 0.))
		wc = geo.normalize(ctr - pnt)
		_, wc_x, wc_y = geo.coordinate_system(wc)

		# sample uniformly if p is inside
		if pnt.sq_dist(ctr) - self.radius * self.radius < EPS:
			return self.sample(u1, u2)

		# sample inside subtended cone
		st_max_sq = self.radius * self.radius / pnt.sq_dist(ctr)
		ct_max = np.sqrt(max(0., 1. - st_max_sq))

		from pytracer.montecarlo import uniform_sample_cone
		r = geo.Ray(pnt, uniform_sample_cone(u1, u2, ct_max, wc_x, wc_y, wc), EPS)

		hit, thit, _, _ = self.intersect(r)

		if not hit:
			thit = (ctr - pnt).dot(geo.normalize(r.d))

		ps = r(thit)
		ns = geo.Normal.from_arr(geo.normalize(ps - ctr))
		if self.ro:
			ns *= -1.

		return [ps, ns]
示例#2
0
	def generate_ray(self, sample: 'CameraSample') -> [FLOAT, 'geo.Ray']:
		"""
		Generate ray based on image sample.
		Returned ray direction is normalized
		"""
		# generate raster and camera samples
		Pras = geo.Point(sample.imageX, sample.imageY, 0.)
		Pcam = self.r2c(Pras)

		ray = geo.Ray(geo.Point(0., 0., 0.), geo.Vector.from_arr(geo.normalize(Pcam)), 0., np.inf)  # ray.d is a geo.Vector init from a geo.Point
		# modify ray for dof
		if self.lens_rad > 0.:
			# sample point on lens

			from pytracer.montecarlo import concentric_sample_disk

			lens_u, lens_v = \
				concentric_sample_disk(sample.lens_u, sample.lens_v)
			lens_u *= self.lens_rad
			lens_v *= self.lens_rad

			# compute point on focal plane
			ft = self.focal_dist / ray.d.z
			Pfoc = ray(ft)

			# update ray
			ray.o = geo.Point(lens_u, lens_v, 0.)
			ray.d = geo.normalize(Pfoc - ray.o)

		ray.time = util.lerp(sample.time, self.s_open, self.s_close)
		ray = self.c2w(ray)
		return [1., ray]
示例#3
0
    def look_at(cls,
                pos: 'geo.Point',
                look: 'geo.Point',
                up: 'geo.Vector',
                dtype=FLOAT) -> 'Transform':
        """
		look_at
		Look-at transformation, from camera
		to world
		"""
        w2c = np.eye(4, 4, dtype=dtype)
        c2w = np.eye(4, 4, dtype=dtype)

        zc = geo.normalize(look - pos)
        xc = geo.normalize(geo.normalize(up).cross(zc))
        yc = zc.cross(xc)  # orthogonality

        # c2w translation
        c2w[0][3] = pos.x
        c2w[1][3] = pos.y
        c2w[2][3] = pos.z

        # c2w rotation
        c2w[0][0] = xc.x
        c2w[0][1] = xc.y
        c2w[0][2] = xc.z
        c2w[1][0] = yc.x
        c2w[1][1] = yc.y
        c2w[1][2] = yc.z
        c2w[2][0] = zc.x
        c2w[2][1] = zc.y
        c2w[2][2] = zc.z

        # w2c rotation
        # in effect as camera extrinsic
        w2c[0][0] = xc.x
        w2c[0][1] = yc.x
        w2c[0][2] = zc.x
        w2c[1][0] = xc.y
        w2c[1][1] = yc.y
        w2c[1][2] = zc.y
        w2c[2][0] = xc.z
        w2c[2][1] = yc.z
        w2c[2][2] = zc.z

        # w2c translation
        w2c[0][3] = -(pos.x * xc.x + pos.y * yc.x + pos.z * zc.x)
        w2c[1][3] = -(pos.x * xc.y + pos.y * yc.y + pos.z * zc.y)
        w2c[2][3] = -(pos.x * xc.z + pos.y * yc.z + pos.z * zc.z)

        return cls(c2w, w2c, dtype)
示例#4
0
	def sample_l(self, p: 'geo.Point', pEps: FLOAT, ls: 'LightSample',
			time: FLOAT,) -> ['Spectrum', 'geo.Vector', FLOAT, 'VisibilityTester']:	
		wi = geo.normalize(self.pos - p)
		pdf = 1.
		vis = VisibilityTester()
		vis.set_segment(p, pEps, self.pos, 0., time)
		return [self.intensity / self.__scale(-wi), wi, pdf, vis]
示例#5
0
    def __cylinder(self, p: 'geo.Point') -> [FLOAT]:
        """
		Cylinderical Mapping for single
		point. Returns list
		[s, t].
		"""
        v = geo.normalize(self.w2t(p) - geo.Point(0., 0., 0.))
        return [(PI + self.arctan2(v.y, v.x)) * INV_2PI, v.z]
示例#6
0
	def sample_l(self, p: 'geo.Point', pEps: FLOAT, ls: 'LightSample',
			time: FLOAT,) -> ['Spectrum', 'geo.Vector', FLOAT, 'VisibilityTester']:	
		ps, ns = self.shape_set.sample_p(p, ls)
		wi = geo.normalize(ps - p)
		pdf = self.shape_set.pdf(p, wi)
		vis = VisibilityTester()
		vis.set_segment(p, pEps, ps, EPS, time)
		return [self.l(ps, ns, -wi), wi, pdf, vis]
示例#7
0
 def __init__(self, sig_a: 'Spectrum', sig_s: 'Spectrum', g: FLOAT,
              le: 'Spectrum', extent: 'geo.BBox', v2w: 'trans.Transform',
              a: FLOAT, b: FLOAT, up: 'geo.Vector'):
     super().__init__(sig_a, sig_s, g, le, v2w)
     self.extent = extent
     self.a = a
     self.b = b
     self.up = geo.normalize(up)
示例#8
0
	def generate_ray_differential(self, sample: 'CameraSample') -> [FLOAT, 'geo.RayDifferential']:
		"""
		Generate ray differential.
		"""
		p_ras = geo.Point(sample.imageX, sample.imageY, 0.)
		p_cam = self.r2c(p_ras)

		ray = geo.RayDifferential(geo.Point(0., 0., 0.), geo.Vector.from_arr(geo.normalize(p_cam)), 0., np.inf)  # ray.d is a geo.Vector init from a geo.Point

		from pytracer.montecarlo import concentric_sample_disk
		if self.lens_rad > 0.:
			# depth of field

			lens_u, lens_v = \
				concentric_sample_disk(sample.lens_u, sample.lens_v)
			lens_u *= self.lens_rad
			lens_v *= self.lens_rad

			# compute point on focal plane
			ft = self.focal_dist / ray.d.z
			Pfoc = ray(ft)

			# update ray
			ray.o = geo.Point(lens_u, lens_v, 0.)
			ray.d = geo.normalize(Pfoc - ray.o)

		if self.lens_rad > 0.:
			# with defocus blue
			lens_u, lens_v = concentric_sample_disk(sample.lens_u, sample.lens_v)
			lens_u *= self.lens_rad
			lens_v *= self.lens_rad

			# compute point on focal plane
			dx = geo.normalize(self.dxCam + p_cam)
			ft = self.focal_dist / dx.z
			Pfoc = geo.Point(0., 0., 0.) + ft * dx
			ray.rxOrigin = geo.Point(lens_u, lens_v, 0.)
			ray.rxDirection = geo.normalize(Pfoc - ray.rxOrigin)

			dy = geo.normalize(geo.Vector.from_arr(p_cam + self.dyCam))
			ft = self.focal_dist / dy.z
			Pfoc = geo.Point(0., 0., 0.) + ft * dy
			ray.ryOrigin = geo.Point(lens_u, lens_v, 0.)
			ray.ryDirection = geo.normalize(Pfoc - ray.ryOrigin)

		else:
			ray.rxOrigin = ray.ryOrigin = ray.o
			ray.rxDirection = geo.normalize(self.dxCam + p_cam)  # geo.Vector + geo.Point => geo.Vector
			ray.ryDirection = geo.normalize(self.dyCam + p_cam)

		ray.time = sample.time
		ray = self.c2w(ray)
		ray.has_differentials = True

		return [1., ray]
示例#9
0
    def pdf(self, wo: 'geo.Vector', wi: 'geo.Vector') -> FLOAT:
        wh = geo.normalize(wo + wi)
        ct = abs_cos_theta(wh)

        if wo.dot(wh) <= 0.:
            return 0.

        return (
            (self.e + 1.) * np.power(ct, self.e)) / (2. * PI * 4. * wo.dot(wh))
示例#10
0
    def sample(self, u1: FLOAT, u2: FLOAT) -> ['geo.Point', 'geo.Normal']:
        z = util.lerp(u1, self.zmin, self.zmax)
        t = u2 * self.phiMax
        p = geo.Point(self.radius * np.cos(t), self.radius * np.sin(t), z)
        Ns = geo.normalize(self.o2w(geo.Normal(p.x, p.y, 0.)))

        if self.ro:
            Ns *= -1.

        return [self.o2w(p), Ns]
示例#11
0
    def __sphere(self, p: 'geo.Point') -> [FLOAT]:
        """
		Spherical Mapping for single
		point. Returns list
		[s, t].
		"""
        v = geo.normalize(self.w2t(p) - geo.Point(0., 0., 0.))
        theta = geo.spherical_theta(v)
        phi = geo.spherical_phi(v)
        return [theta * INV_PI, phi * INV_2PI]
示例#12
0
 def pdf(self, wo: 'geo.Vector', wi: 'geo.Vector') -> FLOAT:
     wh = geo.normalize(wo + wi)
     ct = abs_cos_theta(wh)
     ds = 1. - ct * ct
     if ds > 0. and wo.dot(wh) > 0.:
         return (np.sqrt((self.ex + 1.) * (self.ey + 1.)) * INV_2PI * np.power(ct,
                                                                               (
                                                                               self.ex * wh.x * wh.x + self.ey * wh.y * wh.y) / ds)) / \
                (4. * wo.dot(wh))
     else:
         return 0.
示例#13
0
    def f(self, wo: 'geo.Vector', wi: 'geo.Vector') -> 'Spectrum':
        ct_i = cos_theta(wi)
        ct_o = cos_theta(wo)

        if ct_i == 0. or ct_o == 0.:
            return Spectrum(0.)

        wh = geo.normalize(wi + wo)
        ct_h = wi.dot(wh)
        F = self.fresnel(ct_h)

        return self.R * self.distribution.D(wh) * self.G(wo, wi, wh) * \
               F / (4. * ct_i * ct_o)
示例#14
0
    def f(self, wo: 'geo.Vector', wi: 'geo.Vector') -> 'Spectrum':
        diffuse = (28. / (23. * PI)) * self.Rd * \
                  (Spectrum(1.) - self.Rs) * \
                  (1. - np.power(1. - .5 * abs_cos_theta(wi), 5)) * \
                  (1. - np.power(1. - .5 * abs_cos_theta(wo), 5))

        wh = geo.normalize(wi + wo)

        specular = self.distribution.D(wh) / \
                   (4. * wi.abs_dot(wh) * max(abs_cos_theta(wi), abs_cos_theta(wo))) * \
                   self.schlick(wi.dot(wh))

        return diffuse + specular
示例#15
0
    def sample(self, u1: FLOAT, u2: FLOAT) -> ['geo.Point', 'geo.Normal']:

        # account for partial disk
        from pytracer.montecarlo import concentric_sample_disk
        x, y = concentric_sample_disk(u1, u2)
        phi = np.arctan2(y, x) * self.phiMax * INV_2PI
        r = self.inner_radius + np.sqrt(x * x + y * y) * (self.radius -
                                                          self.inner_radius)

        p = geo.Point(r * np.cos(phi), r * np.sin(phi), self.height)

        Ns = geo.normalize(self.o2w(p))
        if self.ro:
            Ns *= -1.

        return [self.o2w(p), Ns]
示例#16
0
	def __falloff(self, w: 'geo.Vector') -> FLOAT:
		"""
		__falloff()

		Determines the falloff
		given a vector in the world.
		"""
		wl = geo.normalize(self.w2l(w))
		ct = wl.z
		if ct < self.cos_width:
			return 0.
		if ct > self.cos_falloff:
			return 1.
		# falloff inside the cone
		d = (ct - self.cos_width) / (self.cos_falloff - self.cos_width)
		return d * d * d * d
示例#17
0
    def sample(self, u1: FLOAT, u2: FLOAT) -> ['geo.Point', 'geo.Normal']:
        from pytracer.montecarlo import uniform_sample_triangle
        b1, b2 = uniform_sample_triangle(u1, u2)

        p = b1 * self.mesh.p[self.mesh.vertexIndex[self.v]] + \
            b2 * self.mesh.p[self.mesh.vertexIndex[self.v]+1] + \
            (1. - b1 - b2) * self.mesh.p[self.mesh.vertexIndex[self.v+2]]

        Ns = geo.normalize(geo.Normal.from_arr(
         (self.mesh.p[self.mesh.vertexIndex[self.v+1]] - self.mesh.p[self.mesh.vertexIndex[self.v]]) \
          .cross(self.mesh.p[self.mesh.vertexIndex[self.v+2]] - self.mesh.p[self.mesh.vertexIndex[self.v]])))

        if self.ro:
            Ns *= -1.

        return [p, Ns]
示例#18
0
	def __init__(self, dg: 'geo.DifferentialGeometry', ng: 'geo.Normal', e: FLOAT = 1.):
		"""
		dg: geo.DifferentialGeometry
		ng: Geometric geo.Normal
		e: index of refraction
		"""
		self.dgs = dg
		self.eta = e
		self.ng = ng

		# coord. system
		self.nn = dg.nn
		self.sn = geo.normalize(dg.dpdu)
		self.tn = self.nn.cross(self.sn)

		self.bdfs = []
		self.__nBDF = INT(0)
示例#19
0
	def sample(self, u1: FLOAT, u2: FLOAT) -> ['geo.Point', 'geo.Normal']:
		"""
		account for partial sphere
		"""
		from pytracer.montecarlo import uniform_sample_sphere
		v = uniform_sample_sphere(u1, u2)

		phi = geo.spherical_theta(v) * self.phiMax * INV_2PI
		theta = self.thetaMin + geo.spherical_theta(v) * (self.thetaMax - self.thetaMin)

		v = geo.spherical_direction(np.sin(theta), np.cos(theta), phi) * self.radius
		v.z = self.zmin + v.z * (self.zmax - self.zmin)

		p = geo.Point.from_arr(v)
		Ns = geo.normalize(self.o2w(geo.Normal(p.x, p.y, p.z)))
		if self.ro:
			Ns *= -1.
		return [self.o2w(p), Ns]
示例#20
0
    def rotate(cls, angle, axis: 'geo.Vector', dtype=FLOAT) -> 'Transform':
        a = geo.normalize(axis)

        s = np.sin(np.deg2rad(angle))
        c = np.cos(np.deg2rad(angle))

        m = np.eye(4, 4, dtype=dtype)

        m[0][0] = a.x * a.x + (1. - a.x * a.x) * c
        m[0][1] = a.x * a.y * (1. - c) - a.z * s
        m[0][2] = a.x * a.z * (1. - c) + a.y * s
        m[1][0] = a.x * a.y * (1. - c) + a.z * s
        m[1][1] = a.y * a.y + (1. - a.y * a.y) * c
        m[1][2] = a.y * a.z * (1. - c) - a.x * s
        m[2][0] = a.x * a.z * (1. - c) - a.y * s
        m[2][1] = a.y * a.z * (1. - c) + a.x * s
        m[2][2] = a.z * a.z + (1. - a.z * a.z) * c

        return cls(m, m.T, dtype)
示例#21
0
	def __scale(self, w: 'geo.Vector') -> 'Spectrum':
		"""
		__scale()

		Utility method to scale
		the amount of light projected
		in the given direction. Assume
		the scale texture is encoded
		using spherical coordinates.
		"""
		if self.MIPMap is None:
			return Spectrum(1.)

		wp = geo.normalize(self.w2l(w))
		wp.z, wp.y = wp.y, wp.z
		theta = geo.spherical_theta(wp)
		phi = geo.spherical_phi(wp)
		s = phi * INV_2PI
		t = theta * INV_PI

		return Spectrum(self.projMap.look_up([s, t]), SpectrumType.ILLUMINANT)
示例#22
0
	def le(self, rd: 'geo.RayDifferential') -> 'Spectrum':
		wh = geo.normalize(self.w2l(rd.d))
		s = geo.spherical_phi(wh) * INV_2PI
		t = geo.spherical_theta(wh) * INV_PI
		return Spectrum.from_rgb(self.radMap.look_up(s, t), SpectrumType.ILLUMINANT)
示例#23
0
    def intersect_p(self, r: 'Ray') -> bool:
        """
		Determine whether intersects
		using Barycentric coordinates
		"""
        # compute s1
        p1 = self.mesh.p[self.mesh.vertexIndex[self.v]]
        p2 = self.mesh.p[self.mesh.vertexIndex[self.v + 1]]
        p3 = self.mesh.p[self.mesh.vertexIndex[self.v + 2]]

        e1 = p2 - p1  # geo.Vector
        e2 = p3 - p1
        s1 = r.d.cross(e2)
        div = s1.dot(e1)

        if div == 0.:
            return False
        divInv = 1. / div

        # compute barycentric coordinate
        ## first one
        d = r.o - p1
        b1 = d.dot(s1) * divInv
        if b1 < 0. or b1 > 1.:
            return False
        ## second one
        s2 = d.cross(e1)
        b2 = r.d.dot(s2) * divInv
        if b2 < 0. or (b1 + b2) > 1.:
            return False

        # compute intersection
        t = e2.dot(s2) * divInv
        if t < r.mint or t > r.maxt:
            return False

        # compute partial derivatives
        uvs = self.get_uvs()
        du1 = uvs[0][0] - uvs[2][0]
        du2 = uvs[1][0] - uvs[2][0]
        dv1 = uvs[0][1] - uvs[2][1]
        dv2 = uvs[1][1] - uvs[2][1]
        dp1 = p1 - p3
        dp2 = p2 - p3

        det = du1 * dv2 - du2 * dv1
        if det == 0.:
            # choose an arbitrary system
            _, dpdu, dpdv = geo.coordinate_system(geo.normalize(e2.cross(e1)))
        else:
            detInv = 1. / det
            dpdu = (dv2 * dp1 - dv1 * dp2) * detInv
            dpdv = (-du2 * dp1 + du1 * dp2) * detInv

        # interpolate triangle parametric coord.
        b0 = 1. - b1 - b2
        tu = b0 * uvs[0][0] + b1 * uvs[1][0] + b2 * uvs[2][0]
        tv = b0 * uvs[0][1] + b1 * uvs[1][1] + b2 * uvs[2][1]

        # test alpha texture
        dg = geo.DifferentialGeometry(r(t), dpdu, dpdv, geo.Normal(0., 0., 0.),
                                      geo.Normal(0., 0., 0.), tu, tv, self)

        if self.mesh.alphaTexture is not None:
            # alpha mask presents
            if self.mesh.alphaTexture.evaluate(dg) == 0.:
                return False

        # have a hit
        return True
示例#24
0
	def __init__(self, l2w: 'trans.Transform', radiance: 'Spectrum', di: 'geo.Vector'):
		super().__init__(l2w)
		self.di = geo.normalize(l2w(di))
		self.l = radiance
示例#25
0
    def get_shading_geometry(
            self, o2w: 'trans.Transform',
            dg: 'geo.DifferentialGeometry') -> 'geo.DifferentialGeometry':
        if self.mesh.n is None or self.mesh.s is None:
            return dg
        # compute barycentric coord
        uvs = self.get_uvs()
        A = np.array([[uvs[1][0] - uvs[0][0], uvs[2][0] - uvs[0][0]],
                      [uvs[1][1] - uvs[0][1], uvs[2][1] - uvs[0][1]]],
                     dtype=FLOAT)
        C = np.array([dg.u - uvs[0][0], dg.v - uvs[0][1]])
        try:
            b = np.linalg.solve(A, C)
        except:
            b = [1. / 3, 1. / 3, 1. / 3]

        else:
            b = [1. - b[0] - b[1], b[0], b[1]]

        # compute shading tangents
        if self.mesh.n is not None:
            ns = geo.normalize(
                o2w(b[0] * self.mesh.n[self.v] +
                    b[1] * self.mesh.n[self.v + 1] +
                    b[2] * self.mesh.n[self.v + 2]))
        else:
            ns = dg.nn

        if self.mesh.s is not None:
            ss = geo.normalize(
                o2w(b[0] * self.mesh.s[self.v] +
                    b[1] * self.mesh.s[self.v + 1] +
                    b[2] * self.mesh.s[self.v + 2]))
        else:
            ss = geo.normalize(dg.dpdu)

        ts = ss.cross(ns)
        if ts.sq_length() > 0.:
            ts.normalize()
            ss = ts.cross(ns)
        else:
            _, ss, ts = geo.coordinate_system(ns)

        # compute dndu and dndv
        if self.mesh.n is not None:
            du1 = uvs[0][0] - uvs[2][0]
            du2 = uvs[1][0] - uvs[2][0]
            dv1 = uvs[0][1] - uvs[2][1]
            dv2 = uvs[1][1] - uvs[2][1]
            dn1 = self.mesh.n[self.mesh.vertexIndex[self.v]] - self.mesh.n[
                self.mesh.vertexIndex[self.v + 2]]
            dn2 = self.mesh.n[self.mesh.vertexIndex[self.v + 1]] - self.mesh.n[
                self.mesh.vertexIndex[self.v + 2]]
            det = du1 * dv2 - du2 * dv1
            if det == 0.:
                # choose an arbitrary system
                dndu = dndv = geo.Normal(0., 0., 0.)
            else:
                detInv = 1. / det
                dndu = (dv2 * dn1 - dv1 * dn2) * detInv
                dndv = (-du2 * dn1 + du1 * dn2) * detInv
        else:
            dndu = geo.Normal(0., 0., 0.)
            dndv = geo.Normal(0., 0., 0.)

        dgs = geo.DifferentialGeometry(dg.p, ss, ts, self.mesh.o2w(dndu),
                                       self.mesh.o2w(dndv), dg.u, dg.v,
                                       dg.shape)
        dgs.dudx = dg.dudx
        dgs.dvdx = dg.dvdx
        dgs.dudy = dg.dudy
        dgs.dvdy = dg.dvdy
        dgs.dpdx = dg.dpdx
        dgs.dpdy = dg.dpdy

        return dgs
示例#26
0
    def intersect(
            self,
            r: 'geo.Ray') -> (bool, FLOAT, FLOAT, 'geo.DifferentialGeometry'):
        """
		Returns:
		 - bool: specify whether intersects
		 - tHit: hit point param
		 - rEps: error tolerance
		 - dg: geo.DifferentialGeometry object
		"""
        # transform ray to object space
        ray = self.w2o(r)

        # solve quad eqn
        A = ray.d.x * ray.d.x + ray.d.y * ray.d.y
        B = 2 * (ray.d.x * ray.o.x + ray.d.y * ray.o.y)
        C = ray.o.x * ray.o.x + ray.o.y * ray.o.y - self.radius * self.radius

        D = B * B - 4. * A * C
        if D <= 0.:
            return [False, None, None, None]

        # validate solutions
        [t0, t1] = np.roots([A, B, C])
        if t0 > t1:
            t0, t1 = t1, t0
        if t0 > ray.maxt or t1 < ray.mint:
            return [False, None, None, None]
        thit = t0
        if t0 < ray.mint:
            thit = t1
            if thit > ray.maxt:
                return [False, None, None, None]

        # cylinder hit position
        phit = ray(thit)
        phi = np.arctan2(phit.y, phit.x)
        if phi < 0.:
            phi += 2. * np.pi

        # test intersection against clipping params
        if phit.z < self.zmin or phit.z > self.zmax or phi > self.phiMax:
            if thit == t1 or t1 > ray.maxt:
                return [False, None, None, None]

            # try again with t1
            thit = t1
            phit = ray(thit)
            phi = np.arctan2(phit.y, phit.x)
            if phi < 0.:
                phi += 2. * np.pi

            if phit.z < self.zmin or phit.z > self.zmax or phi > self.phiMax:
                if thit == t1 or t1 > ray.maxt:
                    return [False, None, None, None]

        # otherwise ray hits the cylinder
        # initialize the differential structure
        u = phi / self.phiMax
        v = (phit.z - self.zmin) / (self.thetaMax - self.thetaMin)

        # find derivatives
        dpdu = geo.Vector(-self.phiMax * phit.y, self.phiMax * phit.x, 0.)
        dpdv = geo.Vector(0., 0., self.zmax - self.zmin)

        # derivative of geo.Normals
        # given by Weingarten Eqn
        d2pduu = -self.phiMax * self.phiMax * geo.Vector(phit.x, phit.y, 0.)
        d2pduv = geo.Vector(0., 0., 0.)
        d2pdvv = geo.Vector(0., 0., 0.)

        # fundamental forms
        E = dpdu.dot(dpdu)
        F = dpdu.dot(dpdv)
        G = dpdv.dot(dpdv)
        N = geo.normalize(dpdu.cross(dpdv))
        e = N.dot(d2pduu)
        f = N.dot(d2pduv)
        g = N.dot(d2pdvv)

        invEGFF = 1. / (E * G - F * F)
        dndu = geo.Normal.from_arr((f * F - e * G) * invEGFF * dpdu +
                                   (e * F - f * E) * invEGFF * dpdv)

        dndv = geo.Normal.from_arr((g * F - f * G) * invEGFF * dpdu +
                                   (f * F - g * E) * invEGFF * dpdv)

        o2w = self.o2w
        dg = geo.DifferentialGeometry(o2w(phit), o2w(dpdu), o2w(dpdv),
                                      o2w(dndu), o2w(dndv), u, v, self)
        return True, thit, EPS * thit, dg
示例#27
0
	def intersect(self, r: 'geo.Ray') -> [bool, FLOAT, FLOAT, 'geo.DifferentialGeometry']:
		"""
		Returns:
		 - bool: specify whether intersects
		 - tHit: hit point param
		 - rEps: error tolerance
		 - dg: geo.DifferentialGeometry object
		"""
		# transform ray to object space
		ray = self.w2o(r)

		# solve quad eqn
		A = ray.d.sq_length()
		B = 2 * ray.d.dot(ray.o)
		C = ray.o.sq_length() - self.radius * self.radius

		D = B * B - 4. * A * C
		if D <= 0.:
			return [False, None, None, None]

		# validate solutions
		[t0, t1] = np.roots([A, B, C])
		if t0 > t1:
			t0, t1 = t1, t0
		if t0 > ray.maxt or t1 < ray.mint:
			return [False, None, None, None]
		thit = t0
		if t0 < ray.mint:
			thit = t1
			if thit > ray.maxt:
				return [False, None, None, None]

		# sphere hit position
		phit = ray(thit)
		if phit.x == 0. and phit.y == 0.:
			phit.x = EPS * self.radius
		phi = np.arctan2(phit.y, phit.x)
		if phi < 0.:
			phi += 2. * np.pi

		# test intersection against clipping params
		if (self.zmin > -self.radius and phit.z < self.zmin) or \
				(self.zmax < self.radius and phit.z > self.zmax) or \
						phi > self.phiMax:
			if thit == t1 or t1 > ray.maxt:
				return [False, None, None, None]

			# try again with t1
			thit = t1
			phit = ray(thit)
			if phit.x == 0. and phit.y == 0.:
				phit.x = EPS * self.radius
			phi = np.arctan2(phit.y, phit.x)
			if phi < 0.:
				phi += 2. * np.pi
			if (self.zmin > -self.radius and phit.z < self.zmin) or \
					(self.zmax < self.radius and phit.z > self.zmax) or \
							phi > self.phiMax:
				return [False, None, None, None]

		# otherwise ray hits the sphere
		# initialize the differential structure
		u = phi / self.phiMax
		theta = np.arccos(np.clip(phit.z / self.radius, -1., 1.))
		delta_theta = self.thetaMax - self.thetaMin
		v = (theta - self.thetaMin) * delta_theta

		# find derivatives
		dpdu = geo.Vector(-self.phiMax * phit.y, self.phiMax * phit.x, 0.)
		zrad = np.sqrt(phit.x * phit.x + phit.y * phit.y)
		inv_zrad = 1. / zrad
		cphi = phit.x * inv_zrad
		sphi = phit.y * inv_zrad
		dpdv = delta_theta \
		       * geo.Vector(phit.z * cphi, phit.z * sphi, -self.radius * np.sin(theta))

		# derivative of Normals
		# given by Weingarten Eqn
		d2pduu = -self.phiMax * self.phiMax * geo.Vector(phit.x, phit.y, 0.)
		d2pduv = delta_theta * phit.z * self.phiMax * geo.Vector(-sphi, cphi, 0.)
		d2pdvv = -delta_theta * delta_theta * geo.Vector(phit.x, phit.y, phit.z)

		# fundamental forms
		E = dpdu.dot(dpdu)
		F = dpdu.dot(dpdv)
		G = dpdv.dot(dpdv)
		N = geo.normalize(dpdu.cross(dpdv))
		e = N.dot(d2pduu)
		f = N.dot(d2pduv)
		g = N.dot(d2pdvv)

		invEGFF = 1. / (E * G - F * F)
		dndu = geo.Normal.from_arr((f * F - e * G) * invEGFF * dpdu +
		                         (e * F - f * E) * invEGFF * dpdv)
		dndv = geo.Normal.from_arr((g * F - f * G) * invEGFF * dpdu +
		                         (f * F - g * E) * invEGFF * dpdv)

		o2w = self.o2w
		dg = geo.DifferentialGeometry(o2w(phit), o2w(dpdu), o2w(dpdv), o2w(dndu), o2w(dndv), u, v, self)
		return True, thit, EPS * thit, dg
示例#28
0
 def test_normalize(self, vec):
     v = geo.normalize(vec)
     if vec.length() != 0.:
         vec /= vec.length()
         assert_elem_eq(vec, v)