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
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
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
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))
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)
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
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
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
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