def matmul_(a0, a1): if not (a0.Size == a1.Size and (a0.IsMatrix or a0.IsVector) \ and (a1.IsMatrix or a1.IsVector)): raise Exception("matmul(): unsupported operand shape!") if a0.IsVector and a1.IsVector: return _ek.dot(a0, a1) elif a0.IsMatrix and a1.IsVector: ar = a0[0] * a1[0] for i in range(1, a1.Size): ar = _ek.fmadd(a0[i], a1[i], ar) return ar elif a0.IsVector and a1.IsMatrix: ar = a1.Value() for i in range(a1.Size): ar[i] = _ek.dot(a0, a1[i]) return ar else: # matrix @ matrix ar, sr = _check2(a0, a1) for j in range(a0.Size): accum = a0[0] * _ek.full(a0.Value, a1[0, j]) for i in range(1, a0.Size): accum = _ek.fmadd(a0[i], _ek.full(a0.Value, a1[i, j]), accum) ar[j] = accum return ar
def eval(self, si, active): cosTheta = ek.dot(si.wi, si.n) tmp2 = ek.select(Frame3f.cos_theta(si.wi) > 0, \ self.m_radiance.eval(si, active)*self.fallof(cosTheta), \ float(0)) return tmp2
def test05_scalar(t): if not ek.is_array_v(t) or ek.array_size_v(t) == 0: return get_class(t.__module__) if ek.is_mask_v(t): assert ek.all_nested(t(True)) assert ek.any_nested(t(True)) assert ek.none_nested(t(False)) assert ek.all_nested(t(False) ^ t(True)) assert ek.all_nested(ek.eq(t(False), t(False))) assert ek.none_nested(ek.eq(t(True), t(False))) if ek.is_arithmetic_v(t): assert t(1) + t(1) == t(2) assert t(3) - t(1) == t(2) assert t(2) * t(2) == t(4) assert ek.min(t(2), t(3)) == t(2) assert ek.max(t(2), t(3)) == t(3) if ek.is_signed_v(t): assert t(2) * t(-2) == t(-4) assert ek.abs(t(-2)) == t(2) if ek.is_integral_v(t): assert t(6) // t(2) == t(3) assert t(7) % t(2) == t(1) assert t(7) >> 1 == t(3) assert t(7) << 1 == t(14) assert t(1) | t(2) == t(3) assert t(1) ^ t(3) == t(2) assert t(1) & t(3) == t(1) else: assert t(6) / t(2) == t(3) assert ek.sqrt(t(4)) == t(2) assert ek.fmadd(t(1), t(2), t(3)) == t(5) assert ek.fmsub(t(1), t(2), t(3)) == t(-1) assert ek.fnmadd(t(1), t(2), t(3)) == t(1) assert ek.fnmsub(t(1), t(2), t(3)) == t(-5) assert (t(1) & True) == t(1) assert (t(1) & False) == t(0) assert (t(1) | False) == t(1) assert ek.all_nested(t(3) > t(2)) assert ek.all_nested(ek.eq(t(2), t(2))) assert ek.all_nested(ek.neq(t(3), t(2))) assert ek.all_nested(t(1) >= t(1)) assert ek.all_nested(t(2) < t(3)) assert ek.all_nested(t(1) <= t(1)) assert ek.select(ek.eq(t(2), t(2)), t(4), t(5)) == t(4) assert ek.select(ek.eq(t(3), t(2)), t(4), t(5)) == t(5) t2 = t(2) assert ek.hsum(t2) == t.Value(2 * len(t2)) assert ek.dot(t2, t2) == t.Value(4 * len(t2)) assert ek.dot_async(t2, t2) == t(4 * len(t2)) value = t(1) value[ek.eq(value, t(1))] = t(2) value[ek.eq(value, t(3))] = t(5) assert value == t(2)
def pdf_direction(self, ref, ds, active): # as the shape init happens after the emitter init if (not (hasattr(self, "m_shape"))): self.set_shape_area() tmp3 = ek.select( ek.dot(ds.d, ds.n) < 0, self.m_shape.pdf_direction(ref, ds, active), float(0)) return tmp3
def sample_direction(self, ref, sample, active): # as the shape init happens after the emitter init if (not (hasattr(self, "m_shape"))): self.set_shape_area() ds = self.m_shape.sample_direction(ref, sample, active) active &= (ek.dot(ds.d, ds.n) < 0) & (ek.neq(ds.pdf, 0)) si = SurfaceInteraction3f(ds, ref.wavelengths) # spatially varying cosTheta = -ek.dot(ds.d, ds.n) fall = self.fallof(cosTheta) spec = self.m_radiance.eval(si, active) * fall / ds.pdf ds.object = 0 # HACK return (ds, ek.select(active, spec, ek.zero(type(spec))))
def det(m): if not _ek.is_matrix_v(m): raise Exception("Unsupported target type!") if m.Size == 1: return m[0, 0] elif m.Size == 2: return _ek.fmsub(m[0, 0], m[1, 1], m[0, 1] * m[1, 0]) elif m.Size == 3: return _ek.dot(m[0], _ek.cross(m[1], m[2])) elif m.Size == 4: col0, col1, col2, col3 = m col1 = _ek.shuffle((2, 3, 0, 1), col1) col3 = _ek.shuffle((2, 3, 0, 1), col3) temp = _ek.shuffle((1, 0, 3, 2), col2 * col3) row0 = col1 * temp temp = _ek.shuffle((2, 3, 0, 1), temp) row0 = _ek.fmsub(col1, temp, row0) temp = _ek.shuffle((1, 0, 3, 2), col1 * col2) row0 = _ek.fmadd(col3, temp, row0) temp = _ek.shuffle((2, 3, 0, 1), temp) row0 = _ek.fnmadd(col3, temp, row0) col1 = _ek.shuffle((2, 3, 0, 1), col1) col2 = _ek.shuffle((2, 3, 0, 1), col2) temp = _ek.shuffle((1, 0, 3, 2), col1 * col3) row0 = _ek.fmadd(col2, temp, row0) temp = _ek.shuffle((2, 3, 0, 1), temp) row0 = _ek.fnmadd(col2, temp, row0) return _ek.dot(col0, row0) else: raise Exception('Unsupported array size!')
def test02_intersection_partials(variant_scalar_rgb): from mitsuba.core import Frame3f, Ray3f, RayDifferential3f from mitsuba.render import SurfaceInteraction3f # Test the texture partial computation with some random data o = [0.44650541, 0.16336525, 0.74225088] d = [0.2956123, 0.67325977, 0.67774232] time = 0.5 w = [] r = RayDifferential3f(o, d, time, w) r.o_x = r.o + [0.1, 0, 0] r.o_y = r.o + [0, 0.1, 0] r.d_x = r.d r.d_y = r.d r.has_differentials = True si = SurfaceInteraction3f() si.p = r(10) si.dp_du = [0.5514372, 0.84608955, 0.41559092] si.dp_dv = [0.14551054, 0.54917541, 0.39286475] si.n = ek.cross(si.dp_du, si.dp_dv) si.n /= ek.norm(si.n) si.t = 0 si.compute_partials(r) # Positions reached via computed partials px1 = si.dp_du * si.duv_dx[0] + si.dp_dv * si.duv_dx[1] py1 = si.dp_du * si.duv_dy[0] + si.dp_dv * si.duv_dy[1] # Manually px2 = r.o_x + r.d_x * \ ((ek.dot(si.n, si.p) - ek.dot(si.n, r.o_x)) / ek.dot(si.n, r.d_x)) py2 = r.o_y + r.d_y * \ ((ek.dot(si.n, si.p) - ek.dot(si.n, r.o_y)) / ek.dot(si.n, r.d_y)) px2 -= si.p py2 -= si.p assert (ek.allclose(px1, px2)) assert (ek.allclose(py1, py2)) si.dp_du = [0, 0, 0] si.compute_partials(r) assert (ek.allclose(px1, px2)) assert (ek.allclose(py1, py2)) si.compute_partials(r) assert (ek.allclose(si.duv_dx, [0, 0])) assert (ek.allclose(si.duv_dy, [0, 0]))
def visible_ndf(D, sigma, theta_i, phi_i, isotropic): from mitsuba.core import MarginalContinuous2D0, Vector2f # Construct projected surface area interpolant data structure m_sigma = MarginalContinuous2D0(sigma, normalize=False) # Create uniform samples and warp by G2 mapping if isotropic: n_theta = n_phi = D.shape[1] else: n_phi = D.shape[0] n_theta = D.shape[1] # Check dimensions of micro-facet model Dvis = np.zeros((phi_i.size, theta_i.size, n_phi, n_theta)) theta = u2theta(np.linspace(0, 1, n_theta)) phi = u2phi(np.linspace(0, 1, n_phi)) # Calculate projected area of micro-facets for i in range(Dvis.shape[0]): # incident elevation for j in range(Dvis.shape[1]): # incident azimuth # Postion for sigma samples sample = Vector2f(theta2u(theta_i[j]), phi2u(phi_i[i])) sigma_i = m_sigma.eval(sample) # Incident direction omega_i = spherical2cartesian(theta_i[j], phi_i[i]) #print(np.degrees(theta_i[j]), np.degrees(phi_i[i])) for k in range(Dvis.shape[2]): # observation azimuth # Observation direction omega = spherical2cartesian(theta, phi[k]) sample = Vector2f(theta2u(theta), phi2u(phi[k])) # NDF at observation directions if isotropic: D_m = D[0] else: D_m = D[k] # Bidirectional NDF Dvis[i, j, k] = ek.max(0, ek.dot(omega, omega_i)) * D_m / sigma_i return Dvis
def test03_sample_ray_differential(variant_packet_spectral, origin, direction): # Check the correctness of the sample_ray_differential() method from mitsuba.core import sample_shifted, sample_rgb_spectrum camera = create_camera(origin, direction) time = 0.5 wav_sample = [0.5, 0.33, 0.1] pos_sample = [[0.2, 0.1, 0.2], [0.6, 0.9, 0.2]] ray, spec_weight = camera.sample_ray_differential(time, wav_sample, pos_sample, 0) # Importance sample wavelength and weight wav, spec = sample_rgb_spectrum(sample_shifted(wav_sample)) assert ek.allclose(ray.wavelengths, wav) assert ek.allclose(spec_weight, spec) assert ek.allclose(ray.time, time) assert ek.allclose(ray.o, origin) # Check that the derivatives are orthogonal assert ek.allclose(ek.dot(ray.d_x - ray.d, ray.d_y - ray.d), 0, atol=1e-7) # Check that a [0.5, 0.5] position_sample generates a ray # that points in the camera direction ray_center, _ = camera.sample_ray_differential(0, 0, [0.5, 0.5], 0) assert ek.allclose(ray_center.d, direction, atol=1e-7) # Check correctness of the ray derivatives # Deltas in screen space dx = 1.0 / camera.film().crop_size().x dy = 1.0 / camera.film().crop_size().y # Sample the rays by offsetting the position_sample with the deltas ray_dx, _ = camera.sample_ray_differential(0, 0, [0.5 + dx, 0.5], 0) ray_dy, _ = camera.sample_ray_differential(0, 0, [0.5, 0.5 + dy], 0) assert ek.allclose(ray_dx.d, ray_center.d_x) assert ek.allclose(ray_dy.d, ray_center.d_y)
def test03_sample_eval_pdf(variant_scalar_rgb, interaction): from mitsuba.core.math import InvPi from mitsuba.core.warp import square_to_uniform_sphere from mitsuba.render import BSDFContext from mitsuba.core.xml import load_string bsdf = load_string("""<bsdf version="2.0.0" type="twosided"> <bsdf type="diffuse"> <rgb name="reflectance" value="0.1, 0.1, 0.1"/> </bsdf> <bsdf type="diffuse"> <rgb name="reflectance" value="0.9, 0.9, 0.9"/> </bsdf> </bsdf>""") n = 5 ctx = BSDFContext() for u in ek.arange(UInt32, n): for v in ek.arange(UInt32, n): interaction.wi = square_to_uniform_sphere([u / float(n-1), v / float(n-1)]) up = ek.dot(interaction.wi, [0, 0, 1]) > 0 for x in ek.arange(UInt32, n): for y in ek.arange(UInt32, n): sample = [x / float(n-1), y / float(n-1)] (bs, s_value) = bsdf.sample(ctx, interaction, 0.5, sample) if ek.any(s_value > 0): # Multiply by square_to_cosine_hemisphere_theta s_value *= bs.wo[2] * InvPi if not up: s_value *= -1 e_value = bsdf.eval(ctx, interaction, bs.wo) p_pdf = bsdf.pdf(ctx, interaction, bs.wo) assert ek.allclose(s_value, e_value, atol=1e-2) assert ek.allclose(bs.pdf, p_pdf) assert not ek.any(ek.isnan(e_value) | ek.isnan(s_value))
def test_sampling(variant_scalar_rgb, center, radius): """ We construct an irradiance meter attached to a sphere and assert that sampled rays originate at the sphere's surface """ from mitsuba.core import Vector3f center_v = Vector3f(center) sphere = example_shape(radius, center_v) sensor = sphere.sensor() num_samples = 100 wav_samples = np.random.rand(num_samples) pos_samples = np.random.rand(num_samples, 2) dir_samples = np.random.rand(num_samples, 2) for i in range(100): ray = sensor.sample_ray_differential(0.0, wav_samples[i], pos_samples[i], dir_samples[i])[0] # assert that the ray starts at the sphere surface assert ek.allclose(ek.norm(center_v - ray.o), radius) # assert that all rays point away from the sphere center assert ek.dot(ek.normalize(ray.o - center_v), ray.d) > 0.0
def check_fov(camera, sample): ray, _ = camera.sample_ray(0, 0, sample, 0) assert ek.allclose( ek.acos(ek.dot(ray.d, direction)) * 180 / ek.pi, fov / 2)
def forward(self): grad_in = self.grad_in('value') grad_out = grad_in * self.inv_norm grad_out -= self.value * (ek.dot(self.value, grad_out) * ek.sqr(self.inv_norm)) self.set_grad_out(grad_out)
def backward(self): grad_out = self.grad_out() grad_in = grad_out * self.inv_norm grad_in -= self.value * (ek.dot(self.value, grad_in) * ek.sqr(self.inv_norm)) self.set_grad_in('value', grad_in)
def inverse_transpose(m): if not _ek.is_matrix_v(m): raise Exception("Unsupported target type!") t = type(m) if m.Size == 1: return t(_ek.rcp(m[0, 0])) elif m.Size == 2: inv_det = _ek.rcp(_ek.fmsub(m[0, 0], m[1, 1], m[0, 1] * m[1, 0])) return t(m[1, 1] * inv_det, -m[1, 0] * inv_det, -m[0, 1] * inv_det, m[0, 0] * inv_det) elif m.Size == 3: col0, col1, col2 = m row0 = _ek.cross(col1, col2) row1 = _ek.cross(col2, col0) row2 = _ek.cross(col0, col1) inv_det = _ek.rcp(_ek.dot(col0, row0)) return t(row0 * inv_det, row1 * inv_det, row2 * inv_det) elif m.Size == 4: col0, col1, col2, col3 = m col1 = _ek.shuffle((2, 3, 0, 1), col1) col3 = _ek.shuffle((2, 3, 0, 1), col3) temp = _ek.shuffle((1, 0, 3, 2), col2 * col3) row0 = col1 * temp row1 = col0 * temp temp = _ek.shuffle((2, 3, 0, 1), temp) row0 = _ek.fmsub(col1, temp, row0) row1 = _ek.shuffle((2, 3, 0, 1), _ek.fmsub(col0, temp, row1)) temp = _ek.shuffle((1, 0, 3, 2), col1 * col2) row0 = _ek.fmadd(col3, temp, row0) row3 = col0 * temp temp = _ek.shuffle((2, 3, 0, 1), temp) row0 = _ek.fnmadd(col3, temp, row0) row3 = _ek.shuffle((2, 3, 0, 1), _ek.fmsub(col0, temp, row3)) temp = _ek.shuffle((1, 0, 3, 2), _ek.shuffle((2, 3, 0, 1), col1) * col3) col2 = _ek.shuffle((2, 3, 0, 1), col2) row0 = _ek.fmadd(col2, temp, row0) row2 = col0 * temp temp = _ek.shuffle((2, 3, 0, 1), temp) row0 = _ek.fnmadd(col2, temp, row0) row2 = _ek.shuffle((2, 3, 0, 1), _ek.fmsub(col0, temp, row2)) temp = _ek.shuffle((1, 0, 3, 2), col0 * col1) row2 = _ek.fmadd(col3, temp, row2) row3 = _ek.fmsub(col2, temp, row3) temp = _ek.shuffle((2, 3, 0, 1), temp) row2 = _ek.fmsub(col3, temp, row2) row3 = _ek.fnmadd(col2, temp, row3) temp = _ek.shuffle((1, 0, 3, 2), col0 * col3) row1 = _ek.fnmadd(col2, temp, row1) row2 = _ek.fmadd(col1, temp, row2) temp = _ek.shuffle((2, 3, 0, 1), temp) row1 = _ek.fmadd(col2, temp, row1) row2 = _ek.fnmadd(col1, temp, row2) temp = _ek.shuffle((1, 0, 3, 2), col0 * col2) row1 = _ek.fmadd(col3, temp, row1) row3 = _ek.fnmadd(col1, temp, row3) temp = _ek.shuffle((2, 3, 0, 1), temp) row1 = _ek.fnmadd(col3, temp, row1) row3 = _ek.fmadd(col1, temp, row3) inv_det = _ek.rcp(_ek.dot(col0, row0)) return t(row0 * inv_det, row1 * inv_det, row2 * inv_det, row3 * inv_det) else: raise Exception('Unsupported array size!')
def projected_area(D, isotropic, projected=True): from mitsuba.core import MarginalContinuous2D0, Vector2f, Vector3f # Check dimensions of micro-facet model sigma = np.zeros(D.shape) # Construct projected surface area interpolant data structure m_D = MarginalContinuous2D0(D, normalize=False) # Create uniform samples and warp by G2 mapping if isotropic: n_theta = n_phi = D.shape[1] else: n_phi = D.shape[0] n_theta = D.shape[1] theta = u2theta(np.linspace(0, 1, n_theta)) phi = u2phi(np.linspace(0, 1, n_phi)) # Temporary values for surface area calculation theta_mean = np.zeros(n_theta + 1) for i in range(n_theta - 1): theta_mean[i + 1] = (theta[i + 1] - theta[i]) / 2. + theta[i] theta_mean[-1] = theta[-1] theta_mean[0] = theta[0] """ Surface area portion of unit sphere. Conditioning better for non vectorized approach. a = sphere_surface_patch(1, theta_next, Vector2f(phi[0], phi[1])) """ a = np.zeros(n_theta) for i in range(n_theta): a[i] = sphere_surface_patch(1, theta_mean[i:i + 2], phi[-3:-1]) # Calculate constants for integration for j in range(n_phi): # Interpolation points o = spherical2cartesian(theta, phi[j]) # Postion for NDF samples u0 = theta2u(theta) u1 = np.ones(n_theta) * phi2u(phi[j]) if j == 0: omega = o u_0 = u0 u_1 = u1 area = a / 2 else: omega = np.concatenate((omega, o)) u_0 = np.concatenate((u_0, u0)) u_1 = np.concatenate((u_1, u1)) if j == n_phi - 1: area = np.concatenate((area, a / 2)) else: area = np.concatenate((area, a)) sample = Vector2f(u_0, u_1) D_s = m_D.eval(sample) omega = Vector3f(omega) P = 1. # Calculate projected area of micro-facets for i in range(sigma.shape[0] - 1): for j in range(sigma.shape[1]): # Get projection factor from incident and outgoind direction if projected: # Incident direction omega_i = spherical2cartesian(theta[j], phi[i]) P = ek.max(0, ek.dot(omega, omega_i)) # Integrate over sphere F = P * D_s sigma[i, j] = np.dot(F, area) if projected: # Normalise sigma[i] = sigma[i] / sigma[i, 0] # TODO: Check for anisotropic case if isotropic: sigma[1] = sigma[0] return sigma
def outgoing_direction(n_phi, n_theta, Dvis_sampler, theta_i, phi_i, isotropic, theta_max=np.pi / 2, all=False): from mitsuba.core import Vector2f, Frame3f from mitsuba.core import MarginalContinuous2D2 print("Max theta angle is %f deg." % np.degrees(theta_max)) phi_o = np.zeros((phi_i.size, theta_i.size, n_phi, n_theta)) theta_o = np.zeros((phi_i.size, theta_i.size, n_phi, n_theta)) invalid = np.ones((phi_i.size, theta_i.size, n_phi, n_theta), dtype='bool') active = np.ones((phi_i.size, theta_i.size, n_phi, n_theta), dtype='bool') # Create uniform samples u_0 = np.linspace(0, 1, n_theta) u_1 = np.linspace(0, 1, n_phi) samples = Vector2f(np.tile(u_0, n_phi), np.repeat(u_1, n_theta)) # Construct projected surface area interpolant data structure params = [phi_i.tolist(), theta_i.tolist()] m_vndf = MarginalContinuous2D2(Dvis_sampler, params, normalize=True) for i in range(phi_i.size): for j in range(theta_i.size): # Warp uniform samples by VNDF distribution (G1 mapping) u_m, ndf_pdf = m_vndf.sample(samples, [phi_i[i], theta_i[j]]) # Convert samples to radians (G2 mapping) theta_m = u2theta(u_m.x) # [0, 1] -> [0, pi] phi_m = u2phi(u_m.y) # [0, 1] -> [0, 2pi] if isotropic: phi_m += phi_i[i] # Phase vector m = spherical2cartesian(theta_m, phi_m) # Incident direction wi = spherical2cartesian(theta_i[j], phi_i[i]) # Outgoing direction (reflection over phase vector) wo = ek.fmsub(m, 2.0 * ek.dot(m, wi), wi) tmp1, tmp2 = cartesian2spherical(wo) # Remove invalid directions act = u_m.y > 0 # covered twice [-pi = pi] inv = Frame3f.cos_theta(wo) < 0 # below surface plane act &= np.invert(inv) # above surface plane act &= tmp1 <= (theta_max + EPSILON) # further angular restriction if isotropic: act &= tmp2 >= 0 if not all: tmp1[~act] = 0 tmp2[~act] = 0 else: tmp1[inv] = 0 tmp2[inv] = 0 # Fit to datashape act = np.reshape(act, (n_phi, n_theta)) inv = np.reshape(inv, (n_phi, n_theta)) tmp1 = np.reshape(tmp1, (n_phi, n_theta)) tmp2 = np.reshape(tmp2, (n_phi, n_theta)) # Append active[i, j] = act invalid[i, j] = inv theta_o[i, j] = tmp1 phi_o[i, j] = tmp2 return [theta_o, phi_o, active, invalid]
def test03_sample_ray_diff(variant_packet_spectral, origin, direction, aperture_rad, focus_dist): # Check the correctness of the sample_ray_differential() method from mitsuba.core import sample_shifted, sample_rgb_spectrum, warp, Vector3f, Transform4f cam = create_camera(origin, direction, aperture=aperture_rad, focus_dist=focus_dist) time = 0.5 wav_sample = [0.5, 0.33, 0.1] pos_sample = [[0.2, 0.1, 0.2], [0.6, 0.9, 0.2]] aperture_sample = [0.5, 0.5] ray, spec_weight = cam.sample_ray_differential(time, wav_sample, pos_sample, aperture_sample) # Importance sample wavelength and weight wav, spec = sample_rgb_spectrum(sample_shifted(wav_sample)) assert ek.allclose(ray.wavelengths, wav) assert ek.allclose(spec_weight, spec) assert ek.allclose(ray.time, time) assert ek.allclose(ray.o, origin) # ----------------------------------------_ # Check that the derivatives are orthogonal assert ek.allclose(ek.dot(ray.d_x - ray.d, ray.d_y - ray.d), 0, atol=1e-7) # Check that a [0.5, 0.5] position_sample and [0.5, 0.5] aperture_sample # generates a ray that points in the camera direction ray_center, _ = cam.sample_ray_differential(0, 0, [0.5, 0.5], [0.5, 0.5]) assert ek.allclose(ray_center.d, direction, atol=1e-7) # ---------------------------------------- # Check correctness of the ray derivatives aperture_sample = [[0.9, 0.4, 0.2], [0.6, 0.9, 0.7]] ray_center, _ = cam.sample_ray_differential(0, 0, [0.5, 0.5], aperture_sample) # Deltas in screen space dx = 1.0 / cam.film().crop_size().x dy = 1.0 / cam.film().crop_size().y # Sample the rays by offsetting the position_sample with the deltas (aperture centered) ray_dx, _ = cam.sample_ray_differential(0, 0, [0.5 + dx, 0.5], aperture_sample) ray_dy, _ = cam.sample_ray_differential(0, 0, [0.5, 0.5 + dy], aperture_sample) assert ek.allclose(ray_dx.d, ray_center.d_x) assert ek.allclose(ray_dy.d, ray_center.d_y) # -------------------------------------- # Check correctness of aperture sampling pos_sample = [0.5, 0.5] aperture_sample = [[0.9, 0.4, 0.2], [0.6, 0.9, 0.7]] ray, _ = cam.sample_ray(time, wav_sample, pos_sample, aperture_sample) ray_centered, _ = cam.sample_ray(time, wav_sample, pos_sample, [0.5, 0.5]) trafo = Transform4f(cam.world_transform().eval(time).matrix.numpy()) tmp = warp.square_to_uniform_disk_concentric(aperture_sample) aperture_v = trafo.transform_vector(aperture_rad * Vector3f(tmp.x, tmp.y, 0)) # NOTE: here we assume near_clip = 1.0 assert ek.allclose(ray.o, ray_centered.o + aperture_v) assert ek.allclose(ray.d, ek.normalize(ray_centered.d * focus_dist - aperture_v))
def check_fov(camera, sample): # aperture position at the center ray, _ = camera.sample_ray(0, 0, sample, [0.5, 0.5]) assert ek.allclose( ek.acos(ek.dot(ray.d, direction)) * 180 / ek.pi, fov / 2)