예제 #1
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(Pcam, geo.Vector(0., 0., 1.), 0., np.inf)

		# 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]
예제 #2
0
	def __init__(self, c2w: 'trans.AnimatedTransform', scr_win: [FLOAT],
				 s_open: FLOAT, s_close: FLOAT, lensr: FLOAT, focald: FLOAT, fov: FLOAT, f: 'Film'):
		super().__init__(c2w, trans.Transform.perspective(fov, .001, 1000.),  # non-raster based, set arbitrarily
						 scr_win, s_open, s_close, lensr, focald, f)
		# compute differential changes in origin
		self.dxCam = self.r2c(geo.Point(1., 0., 0.)) - self.r2c(geo.Point(0., 0., 0.))
		self.dyCam = self.r2c(geo.Point(0., 1., 0.)) - self.r2c(geo.Point(0., 0., 0.))
예제 #3
0
 def test_orthographic(self):
     znear = rng()
     zfar = znear + rng() * VAR
     t = Transform.orthographic(znear, zfar)
     p = geo.Point(rng(), rng(), znear)
     assert_almost_eq(t(p).z, 0.)
     p = geo.Point(rng(), rng(), zfar)
     assert_almost_eq(t(p).z, 1.)
예제 #4
0
 def test_bbox_extent(self):
     p = geo.Point(
         1.,
         1.,
         1.,
     )
     for i in range(3):
         p[i] = 2.
         b = geo.BBox(geo.Point(0., 0., 0.), p)
         assert b.maximum_extent() == i
예제 #5
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]
예제 #6
0
    def test_rotate_bbox(self):
        t = Transform.rotate(180., geo.Vector(1., 1., 1.))
        p1 = geo.Point(0., 0., 0.)
        p2 = geo.Point(1., 1., 1.)
        box = geo.BBox(p1, p2)
        b = t(box)
        pmax = t(box.pMax)
        pmin = t(box.pMin)

        assert_elem_eq(b.pMin, pmin)
        assert_elem_eq(b.pMax, pmax)

        bb = t(b)
        pmax = t(pmax)
        pmin = t(pmin)
        assert_elem_eq(bb.pMin, pmin)
        assert_elem_eq(bb.pMax, pmax)
예제 #7
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]
예제 #8
0
 def __call__(self, dg: 'geo.DifferentialGeometry') -> [FLOAT]:
     v = dg.p - geo.Point(0., 0., 0.)
     return [
         self.ds + v.dot(self.vs), self.dt + v.dot(self.vt),
         dg.dpdx.dot(self.vs),
         dg.dpdx.dot(self.vt),
         dg.dpdy.dot(self.vs),
         dg.dpdy.dot(self.vt)
     ]
예제 #9
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]
예제 #10
0
	def __init__(self, l2w: 'trans.Transform', intensity: 'Spectrum', width: FLOAT, falloff: FLOAT):
		"""
		width: Overall angular width of the cone
		fall: angle at which falloff starts
		"""
		super().__init__(l2w)
		self.pos = l2w(geo.Point(0., 0., 0.)) # where the light is positioned in the world
		self.intensity = intensity
		self.cos_width = np.cos(np.deg2rad(width))
		self.cos_falloff = np.cos(np.deg2rad(falloff))
예제 #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 test_perspective(self):
        znear = rng() * VAR
        zfar = znear + rng() * VAR
        fov = rng() * 45. + 45.

        t = Transform.perspective(fov, znear, zfar)
        p = geo.Point(znear * rng(), znear * rng(),
                      znear + rng() * (zfar - znear))
        pp = t(p)
        assert_almost_eq(pp.z, (p.z - znear) * zfar / ((zfar - znear) * p.z))
        assert_almost_eq(pp.x, p.x / (p.z * np.tan(np.deg2rad(.5 * fov))))
        assert_almost_eq(pp.y, p.y / (p.z * np.tan(np.deg2rad(.5 * fov))))
예제 #13
0
	def pdf_p(self, pnt: 'geo.Point', wi: 'geo.Vector') -> FLOAT:
		ctr = self.o2w(geo.Point(0., 0., 0.))
		# return uniform weight if inside
		if pnt.sq_dist(ctr) - self.radius * self.radius < EPS:
			return super().pdf_p(pnt, wi)

		# general weight
		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_cone_pdf
		return uniform_cone_pdf(ct_max)
예제 #14
0
    def test_bbox_intersect(self):
        b = geo.BBox(geo.Point(1., 1., 1.), geo.Point(2., 2., 2.))
        ray = geo.Ray(geo.Point(0., 0., 0.), geo.Vector(1., 1., 1.))
        hit, t1, t2 = b.intersect_p(ray)
        assert hit
        assert_almost_eq(t1, 1.)
        assert_almost_eq(t2, 2.)

        # ambiguity in this case

        ray = geo.Ray(geo.Point(1. - EPS, 1. - EPS, 0.),
                      geo.Vector(-EPS, -EPS, 1.))
        hit, t1, t2 = b.intersect_p(ray)
        assert not hit
        assert_almost_eq(t1, 0.)
        assert_almost_eq(t2, 0.)

        ray = geo.Ray(geo.Point(1. - EPS, 1. - EPS, 1. - EPS),
                      geo.Vector(-1., -1., -1.))
        hit, t1, t2 = b.intersect_p(ray)
        assert not hit
        assert_almost_eq(t1, 0.)
        assert_almost_eq(t2, 0.)

        ray = geo.Ray(geo.Point(0., 0., 0.), geo.Vector(-1., -1., -1.))
        hit, t1, t2 = b.intersect_p(ray)
        assert not hit
        assert_almost_eq(t1, 0.)
        assert_almost_eq(t2, 0.)
예제 #15
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]
예제 #16
0
    def __call__(self, arg, dtype=None):
        if isinstance(arg, geo.Point) or (isinstance(arg, np.ndarray)
                                          and dtype == geo.Point):
            res = self.m[0:4, 0:3].dot(arg) + self.m[0:4, 3]
            p = geo.Point(res[0], res[1], res[2])
            if util.ne_unity(res[3]):
                p /= res[3]
            return p

        elif isinstance(arg, geo.Vector) or (isinstance(arg, np.ndarray)
                                             and dtype == geo.Vector):
            res = self.m[0:3, 0:3].dot(arg)
            return geo.Vector(res[0], res[1], res[2])

        elif isinstance(arg, geo.Normal) or (isinstance(arg, np.ndarray)
                                             and dtype == geo.Normal):
            # must be transformed by inverse transpose
            res = self.m_inv[0:3, 0:3].T.dot(arg)
            return geo.Normal(res[0], res[1], res[2])

        elif isinstance(arg, geo.RayDifferential):
            r = geo.RayDifferential.from_rd(arg)
            r.o = self(r.o)
            r.d = self(r.d)
            return r

        elif isinstance(arg, geo.Ray):
            r = geo.Ray.from_ray(arg)
            r.o = self(r.o)
            r.d = self(r.d)
            return r

        elif isinstance(arg, geo.BBox):
            res = geo.BBox.from_bbox(arg)
            x = geo.Vector(res.pMax.x - res.pMin.x, 0., 0.)
            y = geo.Vector(0., res.pMax.y - res.pMin.y, 0.)
            z = geo.Vector(0., 0., res.pMax.z - res.pMin.z)
            res.pMin = self(res.pMin)
            x = self(x)
            y = self(y)
            z = self(z)
            res.pMax = res.pMin + (x + y + z)
            return res

        else:
            raise TypeError(
                'Transform can only be called on geo.Point, geo.Vector, geo.Normal, geo.Ray or geo.geo.BBox'
            )
예제 #17
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]
예제 #18
0
def brdf_remap(wo: 'geo.Vector', wi: 'geo.Vector') -> 'geo.Point':
    """
	Mapping regularly sampled BRDF
	using Marschner, 1998
	"""
    dphi = geo.spherical_phi(wi) - geo.spherical_phi(wo)
    if dphi < 0.:
        dphi += 2. * PI
    if dphi > 2. * PI:
        dphi -= 2. * PI
    if dphi > PI:
        dphi = 2. * PI - dphi

    return geo.Point(
        sin_theta(wi) * sin_theta(wo), dphi * INV_PI,
        cos_theta(wi) * cos_theta(wo))
예제 #19
0
    def generate_ray(self, sample: 'CameraSample') -> [FLOAT, 'geo.Ray']:
        """
		Generate ray based on image sample.
		Returned ray direction is normalized
		"""
        time = util.lerp(sample.time, self.s_open, self.s_close)

        # compute ray direction
        theta = np.pi * sample.imageY / self.film.yResolution
        phi = 2 * np.pi * sample.imageX / self.film.xResolution
        stheta = np.sin(theta)

        ray = self.c2w(
            geo.Ray(
                geo.Point(0., 0., 0.),
                geo.Vector(stheta * np.cos(phi), np.cos(theta),
                           stheta * np.sin(phi)), 0., np.inf, time))
        return [1., ray]
예제 #20
0
	def __init__(self, l2w: 'trans.Transform', intensity: 'Spectrum', texname: str, fov: FLOAT):
		super().__init__(l2w)
		self.pos = l2w(geo.Point(0., 0., 0.)) # where the light is positioned in the world
		self.intensity = intensity

		# create a MIPMap
		try:
			import pytracer.utility.imageio as iio
			texels = iio.read_image(texname)
			width, height = np.shape(texels)
		except:
			print('src.core.texture.{}.get_texture(): cannot process file {}, '
					'use default one-valued MIPMap'.format(self.__class__, texname))
			texels = None
			width, height = 0, 0

		if texels is not None:
			from pytracer.texture import MIPMap
			from pytracer.spectral import RGBSpectrum
			self.projMap = MIPMap(RGBSpectrum, texels)
		else:
			self.projMap = None

		# init projection matrix
		aspect = width / height
		if aspect > 1.:
			self.scr_x0 = -aspect
			self.scr_x1 = aspect
			self.scr_y0 = -1.
			self.scr_y1 = 1.
		else:
			self.scr_x0 = -1.
			self.scr_x1 = 1.
			self.scr_y0 = -1. / aspect
			self.scr_y1 = 1. / aspect

		self.hither = EPS
		self.yon = 1e30
		self.proj_trans = trans.Transform.perspective(fov, self.hither, self.yon)

		# compute cosine of cone
		self.cos_width = np.cos(np.arctan(np.tan(np.deg2rad(fov) / 2.) * np.sqrt(1. + 1. / (aspect * aspect))))
예제 #21
0
	def __init__(self, l2w: 'trans.Transform', intensity: 'Spectrum', texname: str):
		super().__init__(l2w)
		self.pos = l2w(geo.Point(0., 0., 0.)) # where the light is positioned in the world
		self.intensity = intensity

		# create a MIPMap
		try:
			import pytracer.utility.imageio as iio
			texels = iio.read_image(texname)
			width, height = np.shape(texels)
		except:
			print('src.core.texture.{}.get_texture(): cannot process file {}, '
					'use default one-valued MIPMap'.format(self.__class__, texname))
			texels = None		
		
		if texels is not None:
			from pytracer.texture import MIPMap
			from pytracer.spectral import RGBSpectrum
			self.MIPMap = MIPMap(RGBSpectrum, texels)
		else:
			self.MIPMap = None
예제 #22
0
	def __init__(self, l2w: 'trans.Transform', l: 'Spectrum', ns: INT, texmap: str=None):
		"""
		width: Overall angular width of the cone
		fall: angle at which falloff starts
		"""
		super().__init__(l2w, ns)
		self.pos = l2w(geo.Point(0., 0., 0.)) # where the light is positioned in the world
		texels = np.array([[l.to_rgb()]])
		width, height = 1, 1

		if texmap is not None:
			try:
				import pytracer.utility.imageio as iio
				texels = iio.read_image(texmap)
				width, height = np.shape(texels)
			except:
				print('src.core.texture.{}.get_texture(): cannot process file {}, '
				'use default one-valued MIPMap'.format(self.__class__, texmap))
				texels = np.array([[l.to_rgb()]])
				width, height = 1, 1

		from pytracer.texture import MIPMap
		from pytracer.spectral import RGBSpectrum
		self.radMap = MIPMap(RGBSpectrum, texels)


		# init sampling PDFs <725>
		# compute image for envir. map
		img = np.empty([height, width], dtype=FLOAT)
		filt = 1. / max(width, height)

		for v in range(height):
			vp = v / height
			st = np.sin(PI * (v + .5) / height)
			for u in range(width):
				up = u / width
				img[v][u] = self.radMap.look_up([up, vp, filt]).y()

		# compute sampling distribution
		self.dist = mc.Distribution2D(img)
예제 #23
0
	def __projection(self, w: 'geo.Vector') -> 'Spectrum':
		"""
		__projection()

		Utility method to determine
		the amount of light projected
		in the given direction.
		"""
		wl = self.w2l(w)
		# discard directions behind proj light
		if wl.z < self.hither:
			return Spectrum(0.)

		# project point onto plane
		pl = self.proj_trans(geo.Point(wl.x, wl.y, wl.z))
		if pl.x < self.scr_x0 or pl.x > self.scr_x1 or \
				pl.y < self.scr_y0 or pl.y > self.scr_y1:
			return Spectrum(0.)
		if self.projMap is None:
			return Spectrum(1.)

		s = (pl.x - self.scr_x0) / (self.scr_x1 - self.scr_x0)
		t = (pl.y - self.scr_y0) / (self.scr_y1 - self.scr_y0)
		return Spectrum(self.projMap.look_up([s, t]), SpectrumType.ILLUMINANT) 
예제 #24
0
 def object_bound(self) -> 'geo.BBox':
     return geo.BBox(geo.Point(-self.radius, -self.radius, self.zmin),
                     geo.Point(self.radius, self.radius, self.zmax))
예제 #25
0
 def object_bound(self) -> 'geo.BBox':
     return geo.BBox(geo.Point(-self.radius, -self.radius, self.height),
                     geo.Point(self.radius, self.radius, self.height))
예제 #26
0
 def test_bbox_bounding_sphere(self):
     b = geo.BBox(geo.Point(0., 0., 0.), geo.Point(1., 1., 1.))
     ctr, rad = b.bounding_sphere()
     assert ctr == geo.Point(.5, .5, .5)
     assert_almost_eq(rad, np.sqrt(3) / 2)
예제 #27
0
 def test_bbox_lerp(self):
     b = geo.BBox(geo.Point(0., 0., 0.), geo.Point(1., 1., 1.))
     tx, ty, tz = rng(3)
     p = b.lerp(tx, ty, tz)
     assert_almost_eq(p.sq_length(), tx * tx + ty * ty + tz * tz)
예제 #28
0
 def test_bbox_offset(self):
     b = geo.BBox(geo.Point(0., 0., 0.), geo.Point(1., 1., 1.))
     tx, ty, tz = rng(3)
     p = b.lerp(tx, ty, tz)
     v = b.offset(p)
     assert_elem_eq(v, [tx, ty, tz])
예제 #29
0
from pytracer.transform import Transform
import pytracer.transform.quat as quat

N_TEST_CASE = 5
VAR = 10.
EPS = 6
np.random.seed(1)
rng = np.random.rand

test_data = {
    'vector':
    [geo.Vector(rng(), rng(), rng()) * VAR for _ in range(N_TEST_CASE)],
    'normal':
    [geo.Normal(rng(), rng(), rng()) * VAR for _ in range(N_TEST_CASE)],
    'point':
    [geo.Point(rng(), rng(), rng()) * VAR for _ in range(N_TEST_CASE)],
    'quat': [
        quat.Quaternion(rng(), rng(), rng(), rng()) * VAR
        for _ in range(N_TEST_CASE)
    ],
}
test_data['vector'].extend([
    geo.Vector(0., 0., 0.),
    geo.Vector(1., 0., 0.),
    geo.Vector(
        0.,
        1.,
        0.,
    ),
    geo.Vector(1., 0., 0.)
])
예제 #30
0
	def __init__(self, l2w: 'trans.Transform', intensity: 'Spectrum'):
		super().__init__(l2w)
		self.pos = l2w(geo.Point(0., 0., 0.)) # where the light is positioned in the world
		self.intensity = intensity