def interaction(): from mitsuba.core import Frame3f from mitsuba.render import SurfaceInteraction3f si = SurfaceInteraction3f() si.t = 0.1 si.p = [0, 0, 0] si.n = [0, 0, 1] si.sh_frame = Frame3f(si.n) return si
def test02_sample_spectrum(variant_scalar_spectral, obj): from mitsuba.render import SurfaceInteraction3f si = SurfaceInteraction3f() assert ek.allclose(obj.sample_spectrum(si, 0), [500, 150]) assert ek.allclose(obj.sample_spectrum(si, 1), [600, 150]) assert ek.allclose( obj.sample_spectrum(si, .5), [500 + 100 * (ek.sqrt(10) / 2 - 1), 150] )
def test01_eval(variant_packet_spectral, spectrum_key): # Check the correctness of the eval() method from mitsuba.core import warp from mitsuba.core.math import InvFourPi from mitsuba.render import SurfaceInteraction3f emitter, spectrum = create_emitter_and_spectrum(spectrum_key) it = SurfaceInteraction3f.zero() assert ek.allclose(emitter.eval(it), spectrum.eval(it))
def test_eval(variant_packet_spectral, spectrum_key, lookat, cutoff_angle): # Check the correctness of the eval() method from mitsuba.render import SurfaceInteraction3f emitter, spectrum = create_emitter_and_spectrum(lookat, cutoff_angle, spectrum_key) # Check that incident direction in the illuminated direction is zero (because hitting a delta light is impossible) it = SurfaceInteraction3f.zero(3) it.wi = [0, 1, 0] assert ek.allclose(emitter.eval(it), 0.)
def test04_srgb_d65(variant_scalar_spectral): """srgb_d65 emitters should evaluate to the product of D65 and sRGB spectra, with intensity factored out when evaluating the sRGB model.""" from mitsuba.core.xml import load_string from mitsuba.core import MTS_WAVELENGTH_SAMPLES from mitsuba.render import PositionSample3f from mitsuba.render import SurfaceInteraction3f import numpy as np np.random.seed(12345) wavelengths = np.linspace(300, 800, MTS_WAVELENGTH_SAMPLES) wavelengths += (10 * np.random.uniform(size=wavelengths.shape)).astype( np.int) d65 = load_string("<spectrum version='2.0.0' type='d65'/>").expand()[0] ps = PositionSample3f() d65_eval = d65.eval(SurfaceInteraction3f(ps, wavelengths)) for color in [[1, 1, 1], [0.1, 0.2, 0.3], [34, 0.1, 62], [0.001, 0.02, 11.4]]: intensity = (np.max(color) * 2.0) or 1.0 normalized = np.array(color) / intensity srgb_d65 = load_string(""" <spectrum version="2.0.0" type="srgb_d65"> <rgb name="color" value="{}"/> </spectrum> """.format(', '.join(map(str, color)))) srgb = load_string(""" <spectrum version="2.0.0" type="srgb"> <rgb name="color" value="{}"/> </spectrum> """.format(', '.join(map(str, normalized)))) assert ek.allclose(srgb_d65.eval(SurfaceInteraction3f(ps, wavelengths)), d65_eval * intensity * srgb.eval(SurfaceInteraction3f(ps, wavelengths)), atol=1e-5)
def test03_sample_half_wave_local(variant_scalar_mono_polarized): from mitsuba.core import Frame3f, Transform4f, Spectrum from mitsuba.core.xml import load_string from mitsuba.render import BSDFContext, TransportMode, SurfaceInteraction3f def spectrum_from_stokes(v): res = Spectrum(0.0) for i in range(4): res[i, 0] = v[i] return res # Test polarized implementation. Special case of delta = 180˚, also known # as a half-wave plate. (In local BSDF coordinates.) # # Following "Polarized Light - Fundamentals and Applications" by Edward Collett # Chapter 5.3: # # Case 1 & 2) Switch between diagonal linear polarization states (-45˚ & + 45˚) # Case 3 & 4) Switch circular polarization direction linear_pos = spectrum_from_stokes([1, 0, +1, 0]) linear_neg = spectrum_from_stokes([1, 0, -1, 0]) circular_right = spectrum_from_stokes([1, 0, 0, +1]) circular_left = spectrum_from_stokes([1, 0, 0, -1]) bsdf = load_string("""<bsdf version='2.0.0' type='retarder'> <spectrum name="theta" value="0"/> <spectrum name="delta" value="180.0"/> </bsdf>""") # Incident direction wi = [0, 0, 1] ctx = BSDFContext() ctx.mode = TransportMode.Importance si = SurfaceInteraction3f() si.p = [0, 0, 0] si.wi = wi n = [0, 0, 1] si.n = n si.sh_frame = Frame3f(si.n) bs, M = bsdf.sample(ctx, si, 0.0, [0.0, 0.0]) # Case 1) assert ek.allclose(M @ linear_pos, linear_neg, atol=1e-3) # Case 2) assert ek.allclose(M @ linear_neg, linear_pos, atol=1e-3) # Case 3) assert ek.allclose(M @ circular_right, circular_left, atol=1e-3) # Case 4) assert ek.allclose(M @ circular_left, circular_right, atol=1e-3)
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 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 test_eval(variant_scalar_spectral, spectrum_key): # Check correctness of the eval() method from mitsuba.core import Vector3f from mitsuba.render import SurfaceInteraction3f direction = Vector3f([0, 0, -1]) emitter = make_emitter(direction, spectrum_key) spectrum = make_spectrum(spectrum_key) # Incident direction in the illuminated direction wi = [0, 0, 1] it = SurfaceInteraction3f() it.p = [0, 0, 0] it.wi = wi assert ek.allclose(emitter.eval(it), 0.) # Incident direction off the illuminated direction wi = [0, 0, 1.1] it = SurfaceInteraction3f() it.p = [0, 0, 0] it.wi = wi assert ek.allclose(emitter.eval(it), 0.)
def test02_d65(variant_scalar_spectral): """d65: Spot check the model in a few places, the chi^2 test will ensure that sampling works.""" from mitsuba.core.xml import load_string from mitsuba.render import PositionSample3f from mitsuba.render import SurfaceInteraction3f d65 = load_string("<spectrum version='2.0.0' type='d65'/>").expand()[0] ps = PositionSample3f() assert ek.allclose( d65.eval(SurfaceInteraction3f(ps, [350, 456, 700, 840])), ek.scalar.Vector4f([0, 117.49, 71.6091, 0]) / 10568.0)
def test03_blackbody(variant_scalar_spectral): """blackbody: Spot check the model in a few places, the chi^2 test will ensure that sampling works.""" from mitsuba.core.xml import load_string from mitsuba.render import PositionSample3f from mitsuba.render import SurfaceInteraction3f bb = load_string("""<spectrum version='2.0.0' type='blackbody'> <float name='temperature' value='5000'/> </spectrum>""") ps = PositionSample3f() assert ek.allclose(bb.eval(SurfaceInteraction3f(ps, [350, 456, 700, 840])), [0, 10997.9, 11812, 0])
def test05_sample_components(variant_scalar_rgb): from mitsuba.core import Frame3f from mitsuba.render import BSDFFlags, BSDFContext, SurfaceInteraction3f from mitsuba.core.xml import load_string from mitsuba.core.math import InvPi weight = 0.2 bsdf = load_string("""<bsdf version="2.0.0" type="blendbsdf"> <bsdf type="diffuse"> <spectrum name="reflectance" value="0.0"/> </bsdf> <bsdf type="diffuse"> <spectrum name="reflectance" value="1.0"/> </bsdf> <spectrum name="weight" value="{}"/> </bsdf>""".format(weight)) si = SurfaceInteraction3f() si.t = 0.1 si.p = [0, 0, 0] si.n = [0, 0, 1] si.sh_frame = Frame3f(si.n) si.wi = [0, 0, 1] ctx = BSDFContext() # Sample specific components separately using two different values of 'sample1' # and make sure the desired component is chosen always. ctx.component = 0 expected_a = (1-weight)*0.0 # InvPi will cancel out with sampling pdf, but still need to apply weight bs_a, weight_a = bsdf.sample(ctx, si, 0.1, [0.5, 0.5]) assert ek.allclose(weight_a, expected_a) expected_b = (1-weight)*0.0 # InvPi will cancel out with sampling pdf, but still need to apply weight bs_b, weight_b = bsdf.sample(ctx, si, 0.3, [0.5, 0.5]) assert ek.allclose(weight_b, expected_b) ctx.component = 1 expected_a = weight*1.0 # InvPi will cancel out with sampling pdf, but still need to apply weight bs_a, weight_a = bsdf.sample(ctx, si, 0.1, [0.5, 0.5]) assert ek.allclose(weight_a, expected_a) expected_b = weight*1.0 # InvPi will cancel out with sampling pdf, but still need to apply weight bs_b, weight_b = bsdf.sample(ctx, si, 0.3, [0.5, 0.5]) assert ek.allclose(weight_b, expected_b)
def sample_ray( self, time, sample1, # wavelength sample2, # pos sample3, # dir active): wavelengths, spec_weight = self.m_intensity.sample( SurfaceInteraction3f(), ek.arange(sample1), active) trafo = self.m_world_transform.eval(ref.time) ray = Ray3f(trafo * Point3f(0), warp.square_to_uniform_sphere(sample3), time, wavelengths) # print(spec_weight.class_().name()) return (ray, spec_weight * 4.0 * Pi)
def test02_eval(variant_packet_spectral, spectrum_key): # Check that eval() return the same values as the 'radiance' spectrum from mitsuba.render import SurfaceInteraction3f shape, spectrum = create_emitter_and_spectrum(spectrum_key) emitter = shape.emitter() it = SurfaceInteraction3f.zero(3) assert ek.allclose(emitter.eval(it), spectrum.eval(it)) # Check that eval return 0.0 when direction points inside the shape it.wi = ek.normalize([0.2, 0.2, -0.5]) assert ek.allclose(emitter.eval(it), 0.0)
def test05_direction_sample_construction_dynamic_and_slicing( variant_packet_rgb): from mitsuba.render import DirectionSample3f, SurfaceInteraction3f import numpy as np np.random.seed(12345) refs = np.array([[0.0, 0.5, 0.7], [1.0, 1.5, 0.2], [-1.3, 0.0, 99.1]]) its = refs + np.random.uniform(size=refs.shape) directions = its - refs directions /= np.expand_dims(np.linalg.norm(directions, axis=1), 0).T pdfs = [0.99, 1.0, 0.05] records_batch = DirectionSample3f.zero(len(pdfs)) records_batch.p = its records_batch.d = directions records_batch.pdf = pdfs records_individual = DirectionSample3f.zero(len(pdfs)) # Switch back to scalar variant mitsuba.set_variant('scalar_rgb') from mitsuba.render import SurfaceInteraction3f, DirectionSample3f, Interaction3f for i in range(len(pdfs)): it = SurfaceInteraction3f() it.p = its[i, :] # Needs to be a "valid" (surface) interaction, otherwise interaction # will be assumed to have happened on an environment emitter. it.t = 0.1 ref = Interaction3f.zero() ref.p = refs[i, :] r = DirectionSample3f(it, ref) r.pdf = pdfs[i] records_individual[i] = r assert ek.allclose(records_batch.p, its) assert ek.allclose(records_batch.p, records_individual.p) assert ek.allclose(records_batch.d, directions) assert ek.allclose(records_batch.d, records_individual.d, atol=1e-6) assert ek.allclose(records_batch.pdf, pdfs) assert ek.allclose(records_batch.pdf, records_individual.pdf) # Slicing: get a copy of one of the entries single = records_individual[2] assert (ek.allclose(single.d, directions[2]) and ek.allclose(single.pdf, pdfs[2]))
def test_sample_ray(variant_packet_spectral, spectrum_key, wavelength_sample, pos_sample, cutoff_angle, lookat): # Check the correctness of the sample_ray() method import math from mitsuba.core import warp, sample_shifted, Transform4f from mitsuba.render import SurfaceInteraction3f cutoff_angle_rad = math.radians(cutoff_angle) cos_cutoff_angle_rad = math.cos(cutoff_angle_rad) 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)) # Sample a local direction and calculate local angle dir_sample = pos_sample # not being used anyway local_dir = warp.square_to_uniform_cone(pos_sample, cos_cutoff_angle_rad) angle = ek.acos(local_dir[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 ray (position, direction, wavelengths) from the emitter ray, res = emitter.sample_ray(eval_t, wavelength_sample, pos_sample, dir_sample) # Sample wavelengths on the spectrum it = SurfaceInteraction3f.zero() wav, spec = spectrum.sample(it, sample_shifted(wavelength_sample)) it.wavelengths = wav 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 ek.allclose( res, spec / warp.square_to_uniform_cone_pdf( trafo.inverse().transform_vector(ray.d), cos_cutoff_angle_rad)) assert ek.allclose(ray.time, eval_t) assert ek.all(local_dir.z >= cos_cutoff_angle_rad) assert ek.allclose(ray.wavelengths, wav) assert ek.allclose(ray.d, trafo.transform_vector(local_dir)) assert ek.allclose(ray.o, lookat["pos"])
def sample_ray( self, time, sample1, # wavelength sample2, # pos sample3, # dir active): ps = self.m_shape.sample_position(time, sample2, active) local = warp.square_to_cosine_hemisphere(sample3) si = SurfaceInteraction3f(ps, 0) wavelengths, spec_weight = self.m_radiance.sample( si, ek.arange(sample1), active) ray = Ray3f(ps.p, Frame3f(ps.n).to_world(local), time, wavelengths) return (ray, spec_weight * self.m_area_times_pi)
def test01_eval(variant_scalar_spectral, obj): from mitsuba.render import SurfaceInteraction3f si = SurfaceInteraction3f() values = [0, 1, 1.5, 2, .5, 0] for i in range(6): si.wavelengths = 450 + 50 * i assert ek.allclose(obj.eval(si), values[i]) assert ek.allclose(obj.pdf_spectrum(si), values[i] / 212.5) with pytest.raises(RuntimeError) as excinfo: obj.eval_1(si) assert 'not implemented' in str(excinfo.value) with pytest.raises(RuntimeError) as excinfo: obj.eval_3(si) assert 'not implemented' in str(excinfo.value)
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 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 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 test02_position_sample_construction_dynamic(variant_packet_rgb): from mitsuba.render import PositionSample3f, SurfaceInteraction3f n_records = 5 records = PositionSample3f.zero(n_records) # Set properties for all records at once (SoA style) # records.n = np.zeros(shape=(n_records, 3)) # records.pdf = np.zeros(shape=(n_records,)) # records.uv = np.zeros(shape=(n_records, 2)) # records.delta = np.zeros(shape=(n_records,), dtype=np.bool) records.p = np.array([[1.0, 1.0, 1.0], [0.9, 0.9, 0.9], [0.7, 0.7, 0.7], [1.2, 1.5, 1.1], [1.5, 1.5, 1.5]]) records.time = [0.0, 0.5, 0.7, 1.0, 1.5] assert str(records) == """PositionSample3fX[ p = [[1, 1, 1], [0.9, 0.9, 0.9], [0.7, 0.7, 0.7], [1.2, 1.5, 1.1], [1.5, 1.5, 1.5]], n = [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], uv = [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]], time = [0, 0.5, 0.7, 1, 1.5], pdf = [0, 0, 0, 0, 0], delta = [0, 0, 0, 0, 0], object = [nullptr, nullptr, nullptr, nullptr, nullptr] ]""" # SurfaceInteraction constructor si = SurfaceInteraction3f.zero(n_records) si.time = [0.0, 0.5, 0.7, 1.0, 1.5] records = PositionSample3f(si) assert ek.all(records.time == si.time)
def test01_intersection_construction(variant_scalar_rgb): from mitsuba.core import Frame3f from mitsuba.render import SurfaceInteraction3f si = SurfaceInteraction3f() si.shape = None si.t = 1 si.time = 2 si.wavelengths = [] si.p = [1, 2, 3] si.n = [4, 5, 6] si.uv = [7, 8] si.sh_frame = Frame3f([9, 10, 11], [12, 13, 14], [15, 16, 17]) si.dp_du = [18, 19, 20] si.dp_dv = [21, 22, 23] si.duv_dx = [24, 25] si.duv_dy = [26, 27] si.wi = [31, 32, 33] si.prim_index = 34 si.instance = None assert repr(si) == """SurfaceInteraction[
def test03_eval_components(variant_scalar_rgb): from mitsuba.core import Frame3f from mitsuba.render import BSDFFlags, BSDFContext, SurfaceInteraction3f from mitsuba.core.xml import load_string from mitsuba.core.math import InvPi weight = 0.2 bsdf = load_string("""<bsdf version="2.0.0" type="blendbsdf"> <bsdf type="diffuse"> <spectrum name="reflectance" value="0.0"/> </bsdf> <bsdf type="diffuse"> <spectrum name="reflectance" value="1.0"/> </bsdf> <spectrum name="weight" value="{}"/> </bsdf>""".format(weight)) si = SurfaceInteraction3f() si.t = 0.1 si.p = [0, 0, 0] si.n = [0, 0, 1] si.sh_frame = Frame3f(si.n) si.wi = [0, 0, 1] wo = [0, 0, 1] ctx = BSDFContext() # Evaluate the two components separately ctx.component = 0 value0 = bsdf.eval(ctx, si, wo) expected0 = (1-weight) * 0.0*InvPi assert ek.allclose(value0, expected0) ctx.component = 1 value1 = bsdf.eval(ctx, si, wo) expected1 = weight * 1.0*InvPi assert ek.allclose(value1, expected1)
def get_bxdf_s(args, theta_i, phi_i): # Load desired BSDF plugin bsdf = load_string(args.material) # Create a (dummy) surface interaction to use for the evaluation si = SurfaceInteraction3f() # Specify an incident direction with 45 degrees elevation si.wi = sph_dir(theta_i, phi_i) # Create grid in spherical coordinates and map it onto the sphere d2r = ek.pi / 180 theta_s, phi_s = ek.meshgrid( ek.linspace(Float, d2r * args.ts[0], d2r * args.ts[1], args.ts[2]), ek.linspace(Float, d2r * args.ps[0], d2r * args.ps[1], args.ps[2])) ws = sph_dir(theta_s, phi_s) # Evaluate the whole array (18000 directions) at once values = bsdf.eval(BSDFContext(), si, ws) values_r = np.array(values)[:, 0] values_r = values_r.reshape(args.ts[2], args.ps[2]).T return values_r
def test02_eval_pdf(variant_scalar_rgb): from mitsuba.core import Frame3f from mitsuba.render import BSDFContext, BSDFFlags, SurfaceInteraction3f from mitsuba.core.xml import load_string bsdf = load_string("<bsdf version='2.0.0' type='diffuse'></bsdf>") si = SurfaceInteraction3f() si.p = [0, 0, 0] si.n = [0, 0, 1] si.wi = [0, 0, 1] si.sh_frame = Frame3f(si.n) ctx = BSDFContext() for i in range(20): theta = i / 19.0 * (ek.pi / 2) wo = [ek.sin(theta), 0, ek.cos(theta)] v_pdf = bsdf.pdf(ctx, si, wo=wo) v_eval = bsdf.eval(ctx, si, wo=wo)[0] assert ek.allclose(v_pdf, wo[2] / ek.pi) assert ek.allclose(v_eval, 0.5 * wo[2] / ek.pi)
def test04_sample_all(variant_scalar_rgb): from mitsuba.core import Frame3f from mitsuba.render import BSDFFlags, BSDFContext, SurfaceInteraction3f from mitsuba.core.xml import load_string from mitsuba.core.math import InvPi weight = 0.2 bsdf = load_string("""<bsdf version="2.0.0" type="blendbsdf"> <bsdf type="diffuse"> <spectrum name="reflectance" value="0.0"/> </bsdf> <bsdf type="diffuse"> <spectrum name="reflectance" value="1.0"/> </bsdf> <spectrum name="weight" value="{}"/> </bsdf>""".format(weight)) si = SurfaceInteraction3f() si.t = 0.1 si.p = [0, 0, 0] si.n = [0, 0, 1] si.sh_frame = Frame3f(si.n) si.wi = [0, 0, 1] ctx = BSDFContext() # Sample using two different values of 'sample1' and make sure correct # components are chosen. expected_a = 1.0 # InvPi & weight will cancel out with sampling pdf bs_a, weight_a = bsdf.sample(ctx, si, 0.1, [0.5, 0.5]) assert ek.allclose(weight_a, expected_a) expected_b = 0.0 # InvPi & weight will cancel out with sampling pdf bs_b, weight_b = bsdf.sample(ctx, si, 0.3, [0.5, 0.5]) assert ek.allclose(weight_b, expected_b)
def pdf_functor(w, *args): plugin = instantiate(args) si = SurfaceInteraction3f.zero(ek.slices(w)) si.wavelengths = w return plugin.pdf(si)[0]
def make_context(n): si = SurfaceInteraction3f.zero(n) si.wi = wi ek.set_slices(si.wi, n) si.wavelengths = [] return (si, ctx)
def render_sample(scene, sampler, rays, bdata, heightmap_pybind, bssrdf=None): """ Sample RTE TODO: Support multi channel sampling Args: scene: Target scene object sampler: Sampler object for random number rays: Given rays for sampling bdata: BSSRDF Data object heightmap_pybind: Object for getting height map around incident position. Refer src/librender/python/heightmap.cpp Returns: result: Sampling RTE result valid_rays: Mask data whether rays are valid or not scatter: Scatter components of Sampling RTE result non_scatter: Non scatter components of Sampling RTE result invalid_sample: Sampling RTE result with invalid sampled data by VAEBSSRDF """ eta = Float(1.0) emission_weight = Float(1.0) throughput = Spectrum(1.0) result = Spectrum(0.0) scatter = Spectrum(0.0) non_scatter = Spectrum(0.0) invalid_sample = Spectrum(0.0) active = True is_bssrdf = False ##### First interaction ##### si = scene.ray_intersect(rays, active) active = si.is_valid() & active valid_rays = si.is_valid() emitter = si.emitter(scene, active) depth = 0 # Set channel # At and after evaluating BSSRDF, a ray consider only this one channel n_channels = 3 channel = UInt32( ek.min(sampler.next_1d(active) * n_channels, n_channels - 1)) d_out_local = Vector3f().zero() d_out_pdf = Float(0) sss = Mask(False) while (True): depth += 1 if config.aovs and depth == 2: sss = is_bssrdf ##### Interaction with emitters ##### emission_val = emission_weight * throughput * Emitter.eval_vec( emitter, si, active) result += ek.select(active, emission_val, Spectrum(0.0)) invalid_sample += ek.select(active, emission_val, Spectrum(0.0)) scatter += ek.select(active & sss, emission_val, Spectrum(0.0)) non_scatter += ek.select(active & ~sss, emission_val, Spectrum(0.0)) active = active & si.is_valid() # Process russian roulette if depth > config.rr_depth: q = ek.min(ek.hmax(throughput) * ek.sqr(eta), 0.95) active = active & (sampler.next_1d(active) < q) throughput *= ek.rcp(q) # Stop if the number of bouces exceeds the given limit bounce, or # all rays are invalid. latter check is done only when the limit # bounce is infinite if depth >= config.max_depth: break ##### Emitter sampling ##### bsdf = si.bsdf(rays) ctx = BSDFContext() active_e = active & has_flag(BSDF.flags_vec(bsdf), BSDFFlags.Smooth) ds, emitter_val = scene.sample_emitter_direction( si, sampler.next_2d(active_e), True, active_e) active_e &= ek.neq(ds.pdf, 0.0) # Query the BSDF for that emitter-sampled direction wo = si.to_local(ds.d) bsdf_val = BSDF.eval_vec(bsdf, ctx, si, wo, active_e) # Determine density of sampling that same direction using BSDF sampling bsdf_pdf = BSDF.pdf_vec(bsdf, ctx, si, wo, active_e) mis = ek.select(ds.delta, Float(1), mis_weight(ds.pdf, bsdf_pdf)) emission_val = mis * throughput * bsdf_val * emitter_val result += ek.select(active, emission_val, Spectrum(0.0)) invalid_sample += ek.select(active, emission_val, Spectrum(0.0)) scatter += ek.select(active & sss, emission_val, Spectrum(0.0)) non_scatter += ek.select(active & ~sss, emission_val, Spectrum(0.0)) ##### BSDF sampling ##### bs, bsdf_val = BSDF.sample_vec(bsdf, ctx, si, sampler.next_1d(active), sampler.next_2d(active), active) ##### BSSRDF replacing ##### if (config.enable_bssrdf): # Replace bsdf samples by ones of BSSRDF bs.wo = ek.select(is_bssrdf, d_out_local, bs.wo) bs.pdf = ek.select(is_bssrdf, d_out_pdf, bs.pdf) bs.sampled_component = ek.select(is_bssrdf, UInt32(1), bs.sampled_component) bs.sampled_type = ek.select(is_bssrdf, UInt32(+BSDFFlags.DeltaTransmission), bs.sampled_type) ############################ throughput *= ek.select(is_bssrdf, Float(1.0), bsdf_val) active &= ek.any(ek.neq(throughput, 0)) eta *= bs.eta # Intersect the BSDF ray against the scene geometry rays = RayDifferential3f(si.spawn_ray(si.to_world(bs.wo))) si_bsdf = scene.ray_intersect(rays, active) ##### Checking BSSRDF ##### if (config.enable_bssrdf): # Whether the BSDF is BSS RDF or not? is_bssrdf = (active & has_flag(BSDF.flags_vec(bsdf), BSDFFlags.BSSRDF) & (Frame3f.cos_theta(bs.wo) < Float(0.0)) & (Frame3f.cos_theta(si.wi) > Float(0.0))) # Decide whether we should use 0-scattering or multiple scattering is_zero_scatter = utils_render.check_zero_scatter( sampler, si_bsdf, bs, channel, is_bssrdf) is_bssrdf = is_bssrdf & ~is_zero_scatter throughput *= ek.select(is_bssrdf, ek.sqr(bs.eta), Float(1.0)) ########################### ###### Process for BSSRDF ###### if (config.enable_bssrdf and not ek.none(is_bssrdf)): # Get projected samples from BSSRDF projected_si, project_suc, abs_prob = bssrdf.sample_bssrdf( scene, bsdf, bs, si, bdata, heightmap_pybind, channel, is_bssrdf) if config.visualize_invalid_sample and (depth <= 1): active = active & (~is_bssrdf | project_suc) invalid_sample += ek.select((is_bssrdf & (~project_suc)), Spectrum([100, 0, 0]), Spectrum(0.0)) # Sample outgoing direction from projected position d_out_local, d_out_pdf = utils_render.resample_wo( sampler, is_bssrdf) # Apply absorption probability throughput *= ek.select(is_bssrdf, Spectrum(1) - abs_prob, Spectrum(1)) # Replace interactions by sampled ones from BSSRDF si_bsdf = SurfaceInteraction3f().masked_si(si_bsdf, projected_si, is_bssrdf) ################################ # Determine probability of having sampled that same # direction using emitter sampling emitter = si_bsdf.emitter(scene, active) ds = DirectionSample3f(si_bsdf, si) ds.object = emitter delta = has_flag(bs.sampled_type, BSDFFlags.Delta) emitter_pdf = ek.select(delta, Float(0.0), scene.pdf_emitter_direction(si, ds)) emission_weight = mis_weight(bs.pdf, emitter_pdf) si = si_bsdf return result, valid_rays, scatter, non_scatter, invalid_sample