def test03_point_sample_direction_vec(variant_packet_spectral, spectrum_key): from mitsuba.render import SurfaceInteraction3f emitter_pos = [10, -1, 2] emitter, spectrum = create_emitter_and_spectrum(emitter_pos, spectrum_key) # Direction sampling it = SurfaceInteraction3f.zero(3) it.p = [[0.0, 0.0, 0.0], [-2.0, 0.0, -2.0], [4.5, 4.5, 0.0]] # Some positions it.time = [0.3, 0.3, 0.3] # Direction from the position to the point emitter d = -it.p + emitter_pos dist = ek.norm(d) d /= dist # Sample direction on the emitter sample = [0.1, 0.5] ds, res = emitter.sample_direction(it, sample) assert ek.all(ds.time == it.time) assert ek.all(ds.pdf == 1.0) assert ek.all(ds.delta) assert ek.allclose(ds.d, d / ek.norm(d)) # Evalutate the spectrum spec = spectrum.eval(it) / (dist**2) assert ek.allclose(res, spec)
def test_sample_ray(variant_scalar_rgb, direction): import enoki as ek from mitsuba.core import Vector2f, Vector3f emitter = make_emitter(direction=direction) direction = Vector3f(direction) time = 1.0 wavelength_sample = 0.3 directional_sample = [0.3, 0.2] for spatial_sample in [ [0.85, 0.13], [0.16, 0.50], [0.00, 1.00], [0.32, 0.87], [0.16, 0.44], [0.17, 0.44], [0.22, 0.81], [0.12, 0.82], [0.99, 0.42], [0.72, 0.40], [0.01, 0.61], ]: ray, _ = emitter.sample_ray( time, wavelength_sample, spatial_sample, directional_sample) # Check that ray direction is what is expected assert ek.allclose(ray.d, direction / ek.norm(direction)) # Check that ray origin is outside of bounding sphere # Bounding sphere is centered at world origin and has radius 1 without scene assert ek.norm(ray.o) >= 1.
def test02_ray_intersect_transform(variant_scalar_rgb, shape): from mitsuba.core import Ray3f, ScalarVector3f from mitsuba.render import HitComputeFlags trans = ScalarVector3f([0, 1, 0]) angle = 15 for scale in [0.5, 2.7]: s, s_inst = example_scene(shape, scale, trans, angle) # grid size n = 21 inv_n = 1.0 / n for x in range(n): for y in range(n): x_coord = scale * (2 * (x * inv_n) - 1) y_coord = scale * (2 * (y * inv_n) - 1) ray = Ray3f(o=ScalarVector3f([x_coord, y_coord, -12]) + trans, d=[0.0, 0.0, 1.0], time=0.0, wavelengths=[]) si_found = s.ray_test(ray) si_found_inst = s_inst.ray_test(ray) assert si_found == si_found_inst for dn_flags in [ HitComputeFlags.dNGdUV, HitComputeFlags.dNSdUV ]: if si_found: si = s.ray_intersect(ray, HitComputeFlags.All | dn_flags) si_inst = s_inst.ray_intersect( ray, HitComputeFlags.All | dn_flags) assert si.prim_index == si_inst.prim_index assert si.instance is None assert si_inst.instance is not None assert ek.allclose(si.t, si_inst.t, atol=2e-2) assert ek.allclose(si.time, si_inst.time, atol=2e-2) assert ek.allclose(si.p, si_inst.p, atol=2e-2) assert ek.allclose(si.dp_du, si_inst.dp_du, atol=2e-2) assert ek.allclose(si.dp_dv, si_inst.dp_dv, atol=2e-2) assert ek.allclose(si.uv, si_inst.uv, atol=2e-2) assert ek.allclose(si.wi, si_inst.wi, atol=2e-2) if ek.norm(si.dn_du) > 0.0 and ek.norm(si.dn_dv) > 0.0: assert ek.allclose(si.dn_du, si_inst.dn_du, atol=2e-2) assert ek.allclose(si.dn_dv, si_inst.dn_dv, atol=2e-2)
def test02_point_sample_direction(variant_scalar_spectral, spectrum_key): # Check the correctness of the sample_direction() method from mitsuba.render import SurfaceInteraction3f emitter_pos = [10, -1, 2] emitter, spectrum = create_emitter_and_spectrum(emitter_pos, spectrum_key) # Direction sampling it = SurfaceInteraction3f.zero() it.p = [0.0, -2.0, 4.5] # Some position it.time = 0.3 # Direction from the position to the point emitter d = -it.p + emitter_pos dist = ek.norm(d) d /= dist # Sample a direction on the emitter sample = [0.1, 0.5] ds, res = emitter.sample_direction(it, sample) assert ds.time == it.time assert ds.pdf == 1.0 assert ds.delta assert ek.allclose(ds.d, d) # Evalutate the spectrum spec = spectrum.eval(it) / (dist**2) assert ek.allclose(res, spec)
def test_sample_direction(variant_scalar_spectral, spectrum_key, direction): # Check correctness of sample_direction() and pdf_direction() methods from mitsuba.render import SurfaceInteraction3f from mitsuba.core import Vector3f direction = Vector3f(direction) emitter = make_emitter(direction, spectrum_key) spectrum = make_spectrum(spectrum_key) it = SurfaceInteraction3f.zero() # Some position inside the unit sphere (i.e. within the emitter's default bounding sphere) it.p = [-0.5, 0.3, -0.1] it.time = 1.0 # Sample direction samples = [0.85, 0.13] ds, res = emitter.sample_direction(it, samples) # Direction should point *towards* the illuminated direction assert ek.allclose(ds.d, -direction / ek.norm(direction)) assert ek.allclose(ds.pdf, 1.) assert ek.allclose(emitter.pdf_direction(it, ds), 0.) assert ek.allclose(ds.time, it.time) # Check spectrum (no attenuation vs distance) spec = spectrum.eval(it) assert ek.allclose(res, spec)
def test04_normal_weighting_scheme(variant_scalar_rgb): from mitsuba.core import Vector3f from mitsuba.render import Mesh import numpy as np """Tests the weighting scheme that is used to compute surface normals.""" m = Mesh("MyMesh", 5, 2, has_vertex_normals=True) vertices = m.vertex_positions_buffer() normals = m.vertex_normals_buffer() a, b = 1.0, 0.5 vertices[:] = [0, 0, 0, -a, 1, 0, a, 1, 0, -b, 0, 1, b, 0, 1] n0 = Vector3f(0.0, 0.0, -1.0) n1 = Vector3f(0.0, 1.0, 0.0) angle_0 = ek.pi / 2.0 angle_1 = ek.acos(3.0 / 5.0) n2 = n0 * angle_0 + n1 * angle_1 n2 /= ek.norm(n2) n = np.vstack([n2, n0, n0, n1, n1]).transpose() m.faces_buffer()[:] = [0, 1, 2, 0, 3, 4] m.recompute_vertex_normals() for i in range(5): assert ek.allclose(normals[i * 3:(i + 1) * 3], n[:, i], 5e-4)
def test04_direction_sample_construction_single(variant_scalar_rgb): from mitsuba.render import Interaction3f, DirectionSample3f, SurfaceInteraction3f # Default constructor record = DirectionSample3f() record.p = [1, 2, 3] record.n = [4, 5, 6] record.uv = [7, 8] record.d = [0, 42, -1] record.pdf = 0.002 record.dist = 0.13 assert ek.allclose(record.d, [0, 42, -1]) assert str(record) == """DirectionSample3f[ p = [1, 2, 3], n = [4, 5, 6], uv = [7, 8], time = 0, pdf = 0.002, delta = 0, object = nullptr, d = [0, 42, -1], dist = 0.13 ]""" # Construct from two interactions: ds.d should start from the reference its. its = SurfaceInteraction3f.zero() its.p = [20, 3, 40.02] its.t = 1 ref = Interaction3f.zero() ref.p = [1.6, -2, 35] record = DirectionSample3f(its, ref) d = (its.p - ref.p) / ek.norm(its.p - ref.p) assert ek.allclose(record.d, d)
def test01_ray_intersect(variant_scalar_rgb, shape): from mitsuba.core import Ray3f from mitsuba.render import HitComputeFlags s, s_inst = example_scene(shape) # grid size n = 21 inv_n = 1.0 / n for x in range(n): for y in range(n): x_coord = (2 * (x * inv_n) - 1) y_coord = (2 * (y * inv_n) - 1) ray = Ray3f(o=[x_coord, y_coord + 1, -8], d=[0.0, 0.0, 1.0], time=0.0, wavelengths=[]) si_found = s.ray_test(ray) si_found_inst = s_inst.ray_test(ray) assert si_found == si_found_inst if si_found: si = s.ray_intersect( ray, HitComputeFlags.All | HitComputeFlags.dNSdUV) si_inst = s_inst.ray_intersect( ray, HitComputeFlags.All | HitComputeFlags.dNSdUV) assert si.prim_index == si_inst.prim_index assert si.instance is None assert si_inst.instance is not None assert ek.allclose(si.t, si_inst.t, atol=2e-2) assert ek.allclose(si.time, si_inst.time, atol=2e-2) assert ek.allclose(si.p, si_inst.p, atol=2e-2) assert ek.allclose(si.sh_frame.n, si_inst.sh_frame.n, atol=2e-2) assert ek.allclose(si.dp_du, si_inst.dp_du, atol=2e-2) assert ek.allclose(si.dp_dv, si_inst.dp_dv, atol=2e-2) assert ek.allclose(si.uv, si_inst.uv, atol=2e-2) assert ek.allclose(si.wi, si_inst.wi, atol=2e-2) if ek.norm(si.dn_du) > 0.0 and ek.norm(si.dn_dv) > 0.0: assert ek.allclose(si.dn_du, si_inst.dn_du, atol=2e-2) assert ek.allclose(si.dn_dv, si_inst.dn_dv, atol=2e-2)
def test_sample_ray(variant_scalar_spectral, direction, spectrum_key): from mitsuba.core import Vector2f, Vector3f, sample_shifted from mitsuba.render import SurfaceInteraction3f emitter = make_emitter(direction=direction, spectrum_key=spectrum_key) spectrum = make_spectrum(spectrum_key) direction = Vector3f(direction) time = 1.0 for wavelength_sample, spatial_sample, directional_sample in zip( cycle([0.3, 0.7]), [ [0.85, 0.13], [0.16, 0.50], [0.00, 1.00], [0.32, 0.87], [0.16, 0.44], [0.17, 0.44], [0.22, 0.81], [0.12, 0.82], [0.99, 0.42], [0.72, 0.40], [0.01, 0.61], ], cycle([[0.3, 0.2]]) ): ray, _ = emitter.sample_ray( time, wavelength_sample, spatial_sample, directional_sample) # Check that ray direction is what is expected assert ek.allclose(ray.d, direction / ek.norm(direction)) # Check that ray origin is outside of bounding sphere # Bounding sphere is centered at world origin and has radius 1 without scene assert ek.norm(ray.o) >= 1. # Check that passed irradiance spectrum is used for wavelength sampling it = SurfaceInteraction3f.zero() wav, spec = spectrum.sample_spectrum( it, sample_shifted(wavelength_sample)) assert ek.allclose(ray.wavelengths, wav)
def test_sample_ray(variant_scalar_rgb, spatial_sample, direction): import enoki as ek from mitsuba.core import Vector2f, Vector3f emitter = make_emitter(direction=direction) direction = Vector3f(direction) time = 1.0 wavelength_sample = 0.3 directional_sample = [0.3, 0.2] ray, wavelength = emitter.sample_ray( time, wavelength_sample, spatial_sample, directional_sample) # Check that ray direction is what is expected assert ek.allclose(ray.d, direction / ek.norm(direction)) # Check that ray origin is outside of bounding sphere # Bounding sphere is centered at world origin and has radius 1 without scene assert ek.norm(ray.o) >= 1.
def abs_(a0): if not a0.IsArithmetic: raise Exception("abs(): requires arithmetic operands!") if not a0.IsSpecial or a0.IsMatrix: ar, sr = _check1(a0) for i in range(sr): ar[i] = _ek.abs(a0[i]) return ar elif a0.IsSpecial: return _ek.norm(a0) else: raise Exception('abs(): unsupported array type!')
def test_sample_direction(variant_scalar_spectral, spectrum_key, it_pos, wavelength_sample, cutoff_angle, lookat): # Check the correctness of the sample_direction() method import math from mitsuba.core import sample_shifted, Transform4f from mitsuba.render import SurfaceInteraction3f cutoff_angle_rad = math.radians(cutoff_angle) beam_width_rad = cutoff_angle_rad * 0.75 inv_transition_width = 1 / (cutoff_angle_rad - beam_width_rad) emitter, spectrum = create_emitter_and_spectrum(lookat, cutoff_angle, spectrum_key) eval_t = 0.3 trafo = Transform4f(emitter.world_transform().eval(eval_t)) # Create a surface iteration it = SurfaceInteraction3f.zero() it.p = it_pos it.time = eval_t # Sample a wavelength from spectrum wav, spec = spectrum.sample(it, sample_shifted(wavelength_sample)) it.wavelengths = wav # Direction from the position to the point emitter d = -it.p + lookat["pos"] dist = ek.norm(d) d /= dist # Calculate angle between lookat direction and ray direction angle = ek.acos((trafo.inverse().transform_vector(-d))[2]) angle = ek.select( ek.abs(angle - beam_width_rad) < 1e-3, beam_width_rad, angle) angle = ek.select( ek.abs(angle - cutoff_angle_rad) < 1e-3, cutoff_angle_rad, angle) # Sample a direction from the emitter ds, res = emitter.sample_direction(it, [0, 0]) # Evalutate the spectrum spec = spectrum.eval(it) spec = ek.select( angle <= beam_width_rad, spec, spec * ((cutoff_angle_rad - angle) * inv_transition_width)) spec = ek.select(angle <= cutoff_angle_rad, spec, 0) assert ds.time == it.time assert ds.pdf == 1.0 assert ds.delta assert ek.allclose(ds.d, d) assert ek.allclose(res, spec / (dist**2))
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 log_(a0): if not a0.IsFloat: raise Exception("log(): requires floating point operands!") ar, sr = _check1(a0) if not a0.IsSpecial: for i in range(sr): ar[i] = _ek.log(a0[i]) elif a0.IsComplex: ar.real = .5 * _ek.log(_ek.squared_norm(a0)) ar.imag = _ek.arg(a0) elif a0.IsQuaternion: qi_n = _ek.normalize(a0.imag) rq = _ek.norm(a0) acos_rq = _ek.acos(a0.real / rq) log_rq = _ek.log(rq) ar.imag = qi_n * acos_rq ar.real = log_rq else: raise Exception("log(): unsupported array type!") return ar
def compute_groundtruth(make_scene, integrator, spp, passes, epsilon): """Render groundtruth radiance and gradient image using finite difference""" from mitsuba.python.autodiff import render def render_offset(offset): scene = make_scene(integrator, spp, offset) fsize = scene.sensors()[0].film().size() values = render(scene) for i in range(passes-1): values += render(scene) values /= passes return values.numpy().reshape(fsize[1], fsize[0], -1) gradient = (render_offset(epsilon) - render_offset(-epsilon)) / (2.0 * ek.norm(epsilon)) image = render_offset(0.0) return image, gradient[:, :, [0]]
def sample_direction(self, ref, sample, active): trafo = self.m_world_transform.eval(ref.time, active) ds = DirectionSample3f() ds.p = trafo.matrix[3][:3] ds.n = 0 ds.uv = 0 ds.time = ref.time ds.pdf = 1 ds.delta = True ds.d = ds.p - ref.p ds.dist = ek.norm(ds.d) inv_dist = ek.rcp(ds.dist) ds.d *= inv_dist si = SurfaceInteraction3f() si.wavelengths = ref.wavelengths spec = self.m_intensity.eval(si, active) * (inv_dist * inv_dist) return (ds, spec)
def test04_normal_weighting_scheme(variant_scalar_rgb): from mitsuba.core import Struct, float_dtype, Vector3f from mitsuba.render import Mesh import numpy as np """Tests the weighting scheme that is used to compute surface normals.""" vertex_struct = Struct() \ .append("x", Struct.Type.Float32) \ .append("y", Struct.Type.Float32) \ .append("z", Struct.Type.Float32) \ .append("nx", Struct.Type.Float32) \ .append("ny", Struct.Type.Float32) \ .append("nz", Struct.Type.Float32) index_struct = Struct() \ .append("i0", Struct.Type.UInt32) \ .append("i1", Struct.Type.UInt32) \ .append("i2", Struct.Type.UInt32) m = Mesh("MyMesh", vertex_struct, 5, index_struct, 2) v = m.vertices() a, b = 1.0, 0.5 v['x'] = Float([0, -a, a, -b, b]) v['y'] = Float([0, 1, 1, 0, 0]) v['z'] = Float([0, 0, 0, 1, 1]) n0 = Vector3f(0.0, 0.0, -1.0) n1 = Vector3f(0.0, 1.0, 0.0) angle_0 = ek.pi / 2.0 angle_1 = ek.acos(3.0 / 5.0) n2 = n0 * angle_0 + n1 * angle_1 n2 /= ek.norm(n2) n = np.vstack([n2, n0, n0, n1, n1]) f = m.faces() f[0] = (0, 1, 2) f[1] = (0, 3, 4) m.recompute_vertex_normals() assert ek.allclose(v['nx'], n[:, 0], 5e-4) assert ek.allclose(v['ny'], n[:, 1], 5e-4) assert ek.allclose(v['nz'], n[:, 2], 5e-4)
def exp_(a0): if not a0.IsFloat: raise Exception("exp(): requires floating point operands!") ar, sr = _check1(a0) if not a0.IsSpecial: for i in range(sr): ar[i] = _ek.exp(a0[i]) elif a0.IsComplex: s, c = _ek.sincos(a0.imag) exp_r = _ek.exp(a0.real) ar.real = exp_r * c ar.imag = exp_r * s elif a0.IsQuaternion: qi = a0.imag ri = _ek.norm(qi) exp_w = _ek.exp(a0.real) s, c = _ek.sincos(ri) ar.imag = qi * (s * exp_w / ri) ar.real = c * exp_w else: raise Exception("exp(): unsupported array type!") return ar
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 sqrt_(a0): if not a0.IsFloat: raise Exception("sqrt(): requires floating point operands!") ar, sr = _check1(a0) if not a0.IsSpecial: for i in range(sr): ar[i] = _ek.sqrt(a0[i]) elif a0.IsComplex: n = abs(a0) m = a0.real >= 0 zero = _ek.eq(n, 0) t1 = _ek.sqrt(.5 * (n + abs(a0.real))) t2 = .5 * a0.imag / t1 im = _ek.select(m, t2, _ek.copysign(t1, a0.imag)) ar.real = _ek.select(m, t1, abs(t2)) ar.imag = _ek.select(zero, 0, im) elif a0.IsQuaternion: ri = _ek.norm(a0.imag) cs = _ek.sqrt(a0.Complex(a0.real, ri)) ar.imag = a0.imag * (_ek.rcp(ri) * cs.imag) ar.real = cs.real else: raise Exception("sqrt(): unsupported array type!") return ar
def timestep(self, pos, vel, dt=0.02, mu=.1, g=9.81): acc = -mu * vel * ek.norm(vel) - m.Array2f(0, g) pos_out = pos + dt * vel vel_out = vel + dt * acc return pos_out, vel_out
def eval(self, value): self.value = value self.inv_norm = ek.rcp(ek.norm(value)) return value * self.inv_norm
pos += sampler.next_2d() # Sample rays starting from the camera sensor rays, weights = sensor.sample_ray_differential(time=0, sample1=sampler.next_1d(), sample2=pos * scale, sample3=0) # Intersect rays with the scene geometry surface_interaction = scene.ray_intersect(rays) dragon_c = scene.shapes()[6].bbox().center() c = Vector3f(dragon_c) d = c - surface_interaction.p result = ek.norm(d) max_dist = ek.hmax(result) result /= max_dist result = 0.3 * (1.0 - result**(2.0)) result[~surface_interaction.is_valid()] = 0 block = ImageBlock(film.crop_size(), channel_count=5, filter=film.reconstruction_filter(), border=False) block.clear() # ImageBlock expects RGB values (Array of size (n, 3)) block.put(pos, rays.wavelengths, Vector3f(result, result, result), 1) # Write out the result from the ImageBlock