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]
def sample_r(self, scene: 'Scene', ls: 'LightSample', u1: FLOAT, u2: FLOAT, time: FLOAT) -> ['geo.Ray', 'geo.Normal', FLOAT, 'Spectrum']: """ Create a bounding disk and uniformly sample on it. """ # choose point on disk oriented towards light ctr, rad = scene.world_bound().bounding_sphere() _, v1, v2 = geo.coordinate_system(self.di) d1, d2 = mc.concentric_sample_disk(ls.u_pos[0], ls.u_pos[1]) pnt = ctr + rad * (d1 * v1 + d2 * v2) # set ray ray = geo.Ray(pnt + rad * self.di, -self.di, 0., np.inf, time) Ns = geo.Normal.fromVector(ray.d) pdf = 1. / (PI * rad * rad) return [ray, Ns, pdf, self.l]
def sample_hg(w: 'geo.Vector', g: FLOAT, u1: FLOAT, u2: FLOAT) -> 'geo.Vector': """ sample_hg() Sampling from Henyey-Greestein phase function, i.e., $$ \cos(\theta) = \frac{1}{2g}\left(1 + g^2 - \left( \frac{1-g^2}{1-g+2g\zeta} \right) ^2 \right)) $$ """ if np.fabs(g) < EPS: ct = 1. - 2. * u1 else: sqr = (1. - g * g) / (1. - g + 2. * g * u1) ct = (1. + g * g - sqr * sqr) / (2. * g) st = np.sqrt(max(0., 1 - ct * ct)) phi = 2. * PI * u2 _, v1, v2 = geo.coordinate_system(w) return geo.spherical_direction(st, ct, phi, v1, v2, w)
def sample_r(self, scene: 'Scene', ls: 'LightSample', u1: FLOAT, u2: FLOAT, time: FLOAT) -> ['geo.Ray', 'geo.Normal', FLOAT, 'Spectrum']: """ Create a bounding disk and uniformly sample on it. """ # find (u, v) sample coords in inf. light texture uv, pdf = self.dist.sample_cont(ls.u_pos[0], ls.u_pos[1]) if pdf == 0.: return [None, None, 0., Spectrum(0.)] theta = uv[1] * PI phi = uv[0] * 2. * PI ct = np.cos(theta) st = np.sin(theta) sp = np.sin(phi) cp = np.cos(phi) d = -self.l2w(geo.Vector(st * cp, st * sp, ct)) Ns = geo.Normal.fromVector(d) # choose point on disk oriented towards light ctr, rad = scene.world_bound().bounding_sphere() _, v1, v2 = geo.coordinate_system(self.di) d1, d2 = mc.concentric_sample_disk(ls.u_pos[0], ls.u_pos[1]) pnt = ctr + rad * (d1 * v1 + d2 * v2) # set ray ray = geo.Ray(pnt + rad * (-d), d, 0., np.inf, time) # compute pdf dir_pdf = pdf / (2. * PI * PI * st) area_pdf = 1. / (PI * rad * rad) pdf = dir_pdf * area_pdf if st == 0.: pdf == 0. return [ray, Ns, pdf, Spectrum.from_rgb(self.radMap.look_up([uv[0], uv[1]]), SpectrumType.ILLUMINANT)]
def test_coordinate_system(self, vec): x, y, z = geo.coordinate_system(vec) assert_almost_eq(x.dot(y), 0.) assert_almost_eq(x.dot(z), 0.) assert_almost_eq(y.dot(z), 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
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