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 quat_to_euler(q): name = _ek.detail.array_name('Array', q.Type, [3], q.IsScalar) module = _modules.get(q.__module__) Array3f = getattr(module, name) sinp = 2 * _ek.fmsub(q.w, q.y, q.z * q.x) gimbal_lock = _ek.abs(sinp) > (1.0 - 5e-8) # roll (x-axis rotation) q_y_2 = _ek.sqr(q.y) sinr_cosp = 2 * _ek.fmadd(q.w, q.x, q.y * q.z) cosr_cosp = _ek.fnmadd(2, _ek.fmadd(q.x, q.x, q_y_2), 1) roll = _ek.select(gimbal_lock, 2 * _ek.atan2(q.x, q.w), _ek.atan2(sinr_cosp, cosr_cosp)) # pitch (y-axis rotation) pitch = _ek.select(gimbal_lock, _ek.copysign(0.5 * _ek.Pi, sinp), _ek.asin(sinp)) # yaw (z-axis rotation) siny_cosp = 2 * _ek.fmadd(q.w, q.z, q.x * q.y) cosy_cosp = _ek.fnmadd(2, _ek.fmadd(q.z, q.z, q_y_2), 1) yaw = _ek.select(gimbal_lock, 0, _ek.atan2(siny_cosp, cosy_cosp)) return Array3f(roll, pitch, yaw)
def fallof(self, cosTheta): # delta = (cosTheta - self.cosTotalWidth)/(self.cosFallOffStart - self.cosTotalWidth) delta = (self.cutOffAngle - ek.acos(cosTheta)) * self.m_invTransitionWidth delta = ek.select(cosTheta > self.cosFallOffStart, ek.full(type(delta), 1), delta) delta = ek.select(cosTheta < self.cosTotalWidth, ek.zero(type(delta)), delta) return delta
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_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 test16_custom(cname): t = get_class(cname) v1 = ek.zero(t, 100) v2 = ek.empty(t, 100) assert len(v1.state) == 100 assert len(v2.inc) == 100 v2.state = v1.state v1.state = ek.arange(type(v1.state), 100) v3 = ek.select(v1.state < 10, v1, v2) assert v3.state[3] == 3 assert v3.state[11] == 0 assert ek.width(v3) == 100 v4 = ek.zero(t, 1) ek.schedule(v4) ek.resize(v4, 200) assert ek.width(v4) == 200 assert ek.width(v3) == 100 v4 = ek.zero(t, 1) ek.resize(v4, 200) assert ek.width(v4) == 200 index = ek.arange(type(v1.state), 100) ek.scatter(v4, v1, index) v5 = ek.gather(t, v4, index) ek.eval(v5) assert v5.state == v1.state and v5.inc == v1.inc
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 collatz(value: p.Int): counter = p.Int(0) loop = p.Loop(value, counter) while (loop.cond(ek.neq(value, 1))): is_even = ek.eq(value & 1, 0) value.assign(ek.select(is_even, value // 2, 3 * value + 1)) counter += 1 return counter
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 pdf(self, ctx, si, wo, active): if not ctx.is_enabled(BSDFFlags.DiffuseReflection): return Vector3f(0) cos_theta_i = Frame3f.cos_theta(si.wi) cos_theta_o = Frame3f.cos_theta(wo) pdf = warp.square_to_cosine_hemisphere_pdf(wo) return ek.select((cos_theta_i > 0.0) & (cos_theta_o > 0.0), pdf, 0.0)
def eval(self, ctx, si, wo, active): if not ctx.is_enabled(BSDFFlags.DiffuseReflection): return Vector3f(0) cos_theta_i = Frame3f.cos_theta(si.wi) cos_theta_o = Frame3f.cos_theta(wo) value = self.m_reflectance.eval(si, active) * math.InvPi * cos_theta_o return ek.select((cos_theta_i > 0.0) & (cos_theta_o > 0.0), value, Vector3f(0))
def sample(self, ctx, si, sample1, sample2, active): bs = BSDFSample3f() bs.wo = mr.retro_transmit(si.wi) bs.pdf = 1 bs.sampled_type = +BSDFFlags.DeltaTransmission bs.sampled_component = 0 bs.eta = 1 value = self.m_retro_transmittance.eval(si, active) return (bs, ek.select(active, value, 0))
def sincos(x): Float = type(x) Int = ek.int_array_t(Float) xa = ek.abs(x) j = Int(xa * 1.2732395447351626862) j = (j + Int(1)) & ~Int(1) y = Float(j) Shift = Float.Type.Size * 8 - 3 sign_sin = ek.reinterpret_array(Float, j << Shift) ^ x sign_cos = ek.reinterpret_array(Float, (~(j - Int(2)) << Shift)) y = xa - y * 0.78515625 \ - y * 2.4187564849853515625e-4 \ - y * 3.77489497744594108e-8 z = y * y z |= ek.eq(xa, ek.Infinity) s = poly2(z, -1.6666654611e-1, 8.3321608736e-3, -1.9515295891e-4) * z c = poly2(z, 4.166664568298827e-2, -1.388731625493765e-3, 2.443315711809948e-5) * z s = ek.fmadd(s, y, y) c = ek.fmadd(c, z, ek.fmadd(z, -0.5, 1)) polymask = ek.eq(j & Int(2), ek.zero(Int)) return ( ek.mulsign(ek.select(polymask, s, c), sign_sin), ek.mulsign(ek.select(polymask, c, s), sign_cos) )
def integrator_sample(scene, sampler, rays, medium, active=True): si = scene.ray_intersect(rays) active = si.is_valid() & active # Visible emitters emitter_vis = si.emitter(scene, active) result = ek.select(active, Emitter.eval_vec(emitter_vis, si, active), Vector3f(0.0)) ctx = BSDFContext() bsdf = si.bsdf(rays) # Emitter sampling sample_emitter = active & has_flag(BSDF.flags_vec(bsdf), BSDFFlags.Smooth) ds, emitter_val = scene.sample_emitter_direction(si, sampler.next_2d(sample_emitter), True, sample_emitter) active_e = sample_emitter & ek.neq(ds.pdf, 0.0) wo = si.to_local(ds.d) bsdf_val = BSDF.eval_vec(bsdf, ctx, si, wo, active_e) bsdf_pdf = BSDF.pdf_vec(bsdf, ctx, si, wo, active_e) mis = ek.select(ds.delta, Float(1), mis_weight(ds.pdf, bsdf_pdf)) result += ek.select(active_e, emitter_val * bsdf_val * mis, Vector3f(0)) # BSDF sampling active_b = active bs, bsdf_val = BSDF.sample_vec(bsdf, ctx, si, sampler.next_1d(active), sampler.next_2d(active), active_b) si_bsdf = scene.ray_intersect(si.spawn_ray(si.to_world(bs.wo)), active_b) emitter = si_bsdf.emitter(scene, active_b) active_b &= ek.neq(emitter, 0) emitter_val = Emitter.eval_vec(emitter, si_bsdf, active_b) delta = has_flag(bs.sampled_type, BSDFFlags.Delta) ds = DirectionSample3f(si_bsdf, si) ds.object = emitter emitter_pdf = ek.select(delta, Float(0), scene.pdf_emitter_direction(si, ds, active_b)) result += ek.select(active_b, bsdf_val * emitter_val * mis_weight(bs.pdf, emitter_pdf), Vector3f(0)) return result, si.is_valid(), ek.select(si.is_valid(), si.t, Float(0.0))
def matrix_to_quat(m): if not _ek.is_matrix_v(m): raise Exception('Unsupported type!') name = _ek.detail.array_name('Quaternion', m.Type, [4], m.IsScalar) module = _modules.get(m.__module__) Quat4f = getattr(module, name) o = 1.0 t0 = o + m[0, 0] - m[1, 1] - m[2, 2] q0 = Quat4f(t0, m[1, 0] + m[0, 1], m[0, 2] + m[2, 0], m[2, 1] - m[1, 2]) t1 = o - m[0, 0] + m[1, 1] - m[2, 2] q1 = Quat4f(m[1, 0] + m[0, 1], t1, m[2, 1] + m[1, 2], m[0, 2] - m[2, 0]) t2 = o - m[0, 0] - m[1, 1] + m[2, 2] q2 = Quat4f(m[0, 2] + m[2, 0], m[2, 1] + m[1, 2], t2, m[1, 0] - m[0, 1]) t3 = o + m[0, 0] + m[1, 1] + m[2, 2] q3 = Quat4f(m[2, 1] - m[1, 2], m[0, 2] - m[2, 0], m[1, 0] - m[0, 1], t3) mask0 = m[0, 0] > m[1, 1] t01 = _ek.select(mask0, t0, t1) q01 = _ek.select(mask0, q0, q1) mask1 = m[0, 0] < -m[1, 1] t23 = _ek.select(mask1, t2, t3) q23 = _ek.select(mask1, q2, q3) mask2 = m[2, 2] < 0.0 t0123 = _ek.select(mask2, t01, t23) q0123 = _ek.select(mask2, q01, q23) return q0123 * (_ek.rsqrt(t0123) * 0.5)
def eval(self, ctx, si, wo, active): """ Emitter sampling """ if not ctx.is_enabled(BSDFFlags.DiffuseReflection): return Vector3f(0) cos_theta_i = Frame3f.cos_theta(si.wi) cos_theta_o = Frame3f.cos_theta(wo) value = self.get_btf(si.wi, wo, si.uv) * math.InvPi return ek.select((cos_theta_i > 0.0) & (cos_theta_o > 0.0), value, Vector3f(0))
def estimate(self, in_pos, im, props, sigma_n, active): """ Estimate output position and absorption with VAE Notice that the types of arguments are in pytorch (tensor) except sigma_n, but the ones of returns are in mitsuba (Vector3f, Float) Args: in_pos: Incident position in local mesh coordinates, and the type of this is tensor im: Height map around incident position ranging to multiple of sigma_n. This map can be generated by clip_scaled_map() from data_handler, and the type is tensor props: Medium properties vector including (and following this order) - effective albedo - g - eta - incident angle (xyz) - max height , and the type of this argument is tensor sigma_n: Standard deviation of the range of medium scattering. In this method, this value is used as scale factor of coordinates in vae active: Boolean mask which indicates whether a ray is applied VAE or not Return: recon_pos: estimated outgoing position (Vector3f) recon_abs: estimated absorption probability (Spectrum) """ n_sample, _, _, _ = im.shape pos = Vector3f(in_pos) abs_prob = Float().zero(n_sample) self.model.eval() with torch.no_grad(): # Feature conversion feature = self.model.feature_conversion( im.to(self.device, dtype=torch.float), props.to(self.device, dtype=torch.float)) # Sample latent variable from normal distribution z = torch.randn(n_sample, 4).to(self.device, dtype=torch.float) # Decode and get reconstructed position and absorption recon_pos, recon_abs = self.model.decode(feature, z) # Convert from tensor to Vector3f and Float recon_pos = Vector3f(recon_pos) abs_prob = Spectrum(recon_abs.view(1, -1).squeeze()) # Reconstruct real scale position in mesh local coordinates pos += ek.select(active, sigma_n * recon_pos, 0) return pos, abs_prob
def test47_nan_propagation(m): for i in range(2): x = ek.arange(m.Float, 10) ek.enable_grad(x) f0 = m.Float(0) y = ek.select(x < (20 if i == 0 else 0), x, x * (f0 / f0)) ek.backward(y) g = ek.grad(x) if i == 0: assert g == 1 else: assert ek.all(ek.isnan(g)) for i in range(2): x = ek.arange(m.Float, 10) ek.enable_grad(x) f0 = m.Float(0) y = ek.select(x < (20 if i == 0 else 0), x, x * (f0 / f0)) ek.forward(x) g = ek.grad(y) if i == 0: assert g == 1 else: assert ek.all(ek.isnan(g))
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 sample(self, ctx, si, sample1, sample2, active): cos_theta_i = Frame3f.cos_theta(si.wi) active &= cos_theta_i > 0 bs = BSDFSample3f() bs.wo = warp.square_to_cosine_hemisphere(sample2) bs.pdf = warp.square_to_cosine_hemisphere_pdf(bs.wo) bs.eta = 1.0 bs.sampled_type = +BSDFFlags.DiffuseReflection bs.sampled_component = 0 value = self.m_reflectance.eval(si, active) return (bs, ek.select(active & (bs.pdf > 0.0), value, Vector3f(0)))
def sample_bssrdf(self, scene, bsdf, bs, si, bdata, heightmap_pybind, channel, active): """ Get projected sample position and absorption probability form VAE BSSRDF Args: scene: rendered scene object bsdf: BSDF information of each ray bs: BSDF Sample object of each ray si: Surface interaction object of each ray bdata: BSSRDF data object in scene data from data_pipeline.py channel: RGB channel of interest TODO: Support multi channel sampling active: Mask data whether a ray needs to process BSSRDF or not Returns: projected_si: Projected surface interaction object from BSSRDF proj_suc: Mask data indicating projection succeed or not abs_recon: absorption probability from BSSRDF """ # Get ID of BSSDRF mesh for each sur face interactions mesh_id = BSDF.mesh_id_vec(bsdf, active) # Convert incident position into local coordinates of mesh of interested as tensor in_pos = ek.select(active, si.to_mesh_local(bs), Vector3f(0)) # Get properties, e.g., medium params and incident angle as tensor props, sigma_n = get_props(bs, si, channel) # Get height map around incident position as tensor im_bind = heightmap_pybind.get_height_map(in_pos.torch().cpu(), mesh_id.torch().cpu()) im = torch.tensor(im_bind) # Estimate position and absorption probability with VAE as mitsuba types recon_pos_local, abs_recon = self.estimate(in_pos.torch(), im, props, sigma_n, active) # Convert from mesh coordinates to world coordinates recon_pos_world = si.to_mesh_world(bs, recon_pos_local) # Project estimated position onto nearest mesh projected_si, proj_suc = si.project_to_mesh_normal( scene, recon_pos_world, bs, channel, active) return projected_si, proj_suc, abs_recon
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 mis_weight(pdf_a, pdf_b): pdf_a *= pdf_a pdf_b *= pdf_b return ek.select(pdf_a > 0.0, pdf_a / (pdf_a + pdf_b), Float(0.0))
def postprocess_render(results, weights, blocks, pos, aovs=False, invalid_sample=False): """postprocessing for sampling result""" result = results[0] valid_rays = results[1] result *= weights xyz = Color3f(srgb_to_xyz(result)) aovs_result = [ xyz[0], xyz[1], xyz[2], ek.select(valid_rays, Float(1.0), Float(0.0)), 1.0 ] if not aovs and not invalid_sample: block = blocks else: block = blocks["result"] block.put(pos, aovs_result) if aovs: scatter = results[2] non_scatter = results[3] scatter *= weights non_scatter *= weights xyz_scatter = Color3f(srgb_to_xyz(scatter)) xyz_nonscatter = Color3f(srgb_to_xyz(non_scatter)) aovs_scatter = [ xyz_scatter[0], xyz_scatter[1], xyz_scatter[2], ek.select(valid_rays, Float(1.0), Float(0.0)), 1.0 ] aovs_nonscatter = [ xyz_nonscatter[0], xyz_nonscatter[1], xyz_nonscatter[2], ek.select(valid_rays, Float(1.0), Float(0.0)), 1.0 ] block_scatter = blocks["scatter"] block_nonscatter = blocks["non_scatter"] block_scatter.put(pos, aovs_scatter) block_nonscatter.put(pos, aovs_nonscatter) if invalid_sample: invalid = results[4] invalid *= weights xyz_invalid = Color3f(srgb_to_xyz(invalid)) aovs_invalid = [ xyz_invalid[0], xyz_invalid[1], xyz_invalid[2], ek.select(valid_rays, Float(1.0), Float(0.0)), 1.0 ] block_invalid = blocks["invalid"] block_invalid.put(pos, aovs_invalid)
def select_(a0, a1, a2): ar, sr = _check3_select(a0, a1, a2) for i in range(sr): ar[i] = _ek.select(a0[i], a1[i], a2[i]) return ar
def sample(self, scene, sampler, ray, medium=None, active=True): result = Vector3f(0.0) si = scene.ray_intersect(ray, active) active = si.is_valid() & active # emitter = si.emitter(scene) # result = ek.select(active, Emitter.eval_vec(emitter, si, active), Vector3f(0.0)) # z_axis = np.array([0, 0, 1]) # vertex = si.p.numpy() # v_count = vertex.shape[0] # vertex = np.expand_dims(vertex, axis=1) # vertex = np.repeat(vertex, self.light.vertex_count(), axis=1) # light_vertices = self.light.vertex_positions_buffer().numpy().reshape(self.light.vertex_count(), 3) # light_vertices = np.expand_dims(light_vertices, axis=0) # light_vertices = np.repeat(light_vertices, v_count, axis=0) # sph_polygons = light_vertices - vertex # sph_polygons = sph_polygons / np.linalg.norm(sph_polygons, axis=2, keepdims=True) # z_axis = np.repeat( np.expand_dims(z_axis, axis=0), v_count, axis=0 ) # result_np = np.zeros(v_count, dtype=np.double) # for idx in range( self.light.vertex_count() ): # idx1 = (idx+1) % self.light.vertex_count() # idx2 = (idx) % self.light.vertex_count() # dp = np.sum( sph_polygons[:, idx1, :] * sph_polygons[:, idx2, :], axis=1 ) # acos = np.arccos(dp) # cp = np.cross( sph_polygons[:, idx1, :], sph_polygons[:, idx2, :] ) # cp = cp / np.linalg.norm(cp, axis=1, keepdims=True) # dp = np.sum( cp * z_axis, axis=1 ) # result_np += acos * dp # result_np *= 0.5 * 1.0/math.pi # result_np = np.repeat( result_np.reshape((v_count, 1)), 3, axis=1 ) # fin = self.light_radiance * Vector3f(result_np) # fin[fin < 0] = 0 # result += ek.select(active, fin, Vector3f(0.0)) ctx = BSDFContext() bsdf = si.bsdf(ray) # bs, bsdf_val = BSDF.sample_vec(bsdf, ctx, si, sampler.next_1d(active), sampler.next_2d(active), active) # pdf = bs.pdf # wo = si.to_world(bs.wo) ds, emitter_val = scene.sample_emitter_direction( si, sampler.next_2d(active), True, active) pdf = ds.pdf active_e = active & ek.neq(ds.pdf, 0.0) wo = ds.d bsdf_val = BSDF.eval_vec(bsdf, ctx, si, wo, active_e) emitter_val[emitter_val > 1.0] = 1.0 bsdf_val *= emitter_val # _, wi_theta, wi_phi = sph_convert(si.to_world(si.wi)) _, wo_theta, wo_phi = sph_convert(wo) y_0_0_ = y_0_0(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) y_1_n1_ = y_1_n1(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) y_1_0_ = y_1_0(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) y_1_p1_ = y_1_p1(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) y_2_n2_ = y_2_n2(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) y_2_n1_ = y_2_n1(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) y_2_0_ = y_2_0(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) y_2_p1_ = y_2_p1(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) y_2_p2_ = y_2_p2(bsdf_val, wo_theta, wo_phi) / ek.select( pdf > 0, pdf, Float(0.01)) return result, si.is_valid(), [ Float(y_0_0_[0]), Float(y_0_0_[1]), Float(y_0_0_[2]),\ Float(y_1_n1_[0]), Float(y_1_n1_[1]), Float(y_1_n1_[2]),\ Float(y_1_0_[0]), Float(y_1_0_[1]), Float(y_1_0_[2]),\ Float(y_1_p1_[0]), Float(y_1_p1_[1]), Float(y_1_p1_[2]),\ Float(y_2_n2_[0]), Float(y_2_n2_[1]), Float(y_2_n2_[2]),\ Float(y_2_n1_[0]), Float(y_2_n1_[1]), Float(y_2_n1_[2]),\ Float(y_2_0_[0]), Float(y_2_0_[1]), Float(y_2_0_[2]),\ Float(y_2_p1_[0]), Float(y_2_p1_[1]), Float(y_2_p1_[2]),\ Float(y_2_p2_[0]), Float(y_2_p2_[1]), Float(y_2_p2_[2]) ]
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