def __add_floor(self): rotate_transform = Transform.rotate(Vector(-1, 0, 0), 90) scale_transform = Transform.scale(Vector(100, 100, 100)) translate_transform = Transform.translate( Vector(0.0, self.floor_height, 0.0)) total_transform = translate_transform * scale_transform\ * rotate_transform if self.scene.active_view.background == "d": reflectance = Spectrum(0.05) elif self.scene.active_view.background == "l": reflectance = Spectrum(0.5) else: reflectance = Spectrum(0.0) floor = self.plgr.create({ "type": "rectangle", "toWorld": total_transform, "bsdf": { "type": "roughdiffuse", "diffuseReflectance": reflectance, "alpha": 0.5 } }) self.mitsuba_scene.addChild(floor)
def __add_lights(self): #TODO: load lights from scene front_light = self.plgr.create({ "type": "sphere", "center": Point(3.0, 6.0, 4.0), "radius": 2.5, "emitter": { "type": "area", "radiance": Spectrum(10.0), "samplingWeight": 10.0 } }) side_light = self.plgr.create({ "type": "point", "position": Point(4.0, 4.0, -1.0), "intensity": Spectrum(5.0) }) back_light = self.plgr.create({ "type": "point", "position": Point(-0, 5.0, -1), "intensity": Spectrum(5.0) }) self.mitsuba_scene.addChild(front_light)
def __add_primitives(self, active_view, parent_transform=None): if len(active_view.subviews) > 0: for view in active_view.subviews: if parent_transform is None: view_transform = self.__get_view_transform(active_view) else: view_transform = parent_transform * self.__get_view_transform( active_view) self.__add_primitives(view, view_transform) return old_active_view = self.scene.active_view self.scene.active_view = active_view scale = active_view.scale normalize_transform = self.__get_normalize_transform(active_view) view_transform = self.__get_view_transform(active_view) if parent_transform is not None: view_transform = parent_transform * view_transform glob_transform = self.__get_glob_transform() total_transform = glob_transform * view_transform * normalize_transform primitives = self.scene.active_view.primitives for shape in primitives: if shape.color[3] <= 0.0: continue color = { "type": "plastic", "diffuseReflectance": Spectrum(shape.color[:3].tolist()) } if shape.color[3] < 1.0: color = { "type": "mask", "opacity": Spectrum(active_view.alpha), "bsdf": color } if isinstance(shape, Cylinder): if shape.radius <= 0.0: continue setting = self.__add_cylinder(shape) setting["bsdf"] = color setting["toWorld"] = total_transform elif isinstance(shape, Cone): if shape.radius <= 0.0: continue setting = self.__add_cone(shape) setting["bsdf"] = color setting["toWorld"] = total_transform * setting["toWorld"] elif isinstance(shape, Sphere): if shape.radius <= 0.0: continue # Due to weird behavior in Mitsuba, all transformation is # applied directly on radius and center variable. setting = self.__add_sphere(shape) setting["radius"] *= scale setting["center"] = total_transform * setting["center"] setting["bsdf"] = color else: raise NotImplementedError( "Unknown primitive: {}".format(shape)) mitsuba_primative = self.plgr.create(setting) self.mitsuba_scene.addChild(mitsuba_primative) self.scene.active_view = old_active_view
def create_spectrum_from_rgb(r: int, g: int, b: int) -> Spectrum: """ Create a Mitsuba Spectrum from r, g, and b values :param r: Red component :param g: Green component :param b: Blue component :return: A Mitsuba Spectrum """ assert (0 <= r <= 255 and 0 <= g <= 255 and 0 <= b <= 255), "Provide integer rgb values in the range 0-255" spectrum = Spectrum() spectrum.fromSRGB(float(r) / 255., float(g) / 255., float(b) / 255.) return spectrum
def __get_material_setting(self, active_view): setting = {} if self.with_texture_coordinates: diffuse_color = { "type": "checkerboard", "color0": Spectrum([1.0, 1.0, 1.0]), "color1": Spectrum([0.5, 0.5, 0.5]), "flipTexCoords": False, } else: if self.with_colors: diffuse_color = {"type": "vertexcolors"} else: diffuse_color = Spectrum(0.2) setting["bsdf"] = { "type": "roughplastic", "distribution": "beckmann", "alpha": 0.2, "diffuseReflectance": diffuse_color, } setting["bsdf"] = { "type": "twosided", "bsdf": setting["bsdf"] } if self.with_alpha: setting["bsdf"] = { "type": "mask", "opacity": Spectrum(active_view.alpha), "bsdf": setting["bsdf"] } if not self.with_colors and \ not self.with_alpha and \ not self.with_texture_coordinates: setting["subsurface"] = { "type": "dipole", "material": "Skimmilk", "scale": 0.5 } elif self.with_texture_coordinates: setting["bsdf"]["bsdf"]["nonlinear"] = True elif not self.with_alpha and not self.with_texture_coordinates: setting["subsurface"] = { "type": "dipole", "material": "sprite", "scale": 0.5 } return setting
def __get_material_setting(self, active_view): setting = {}; if self.with_wire_frame: diffuse_color = { "type": "wireframe", "edgeColor": Spectrum(0.0), "lineWidth": active_view.line_width, }; if self.with_uniform_colors: diffuse_color["interiorColor"] =\ Spectrum(active_view.vertex_colors[0][0].tolist()[0:3]); #elif self.with_colors: # diffuse_color["interiorColor"] = { "type": "vertexcolors" } else: if self.with_colors: diffuse_color = { "type": "vertexcolors" } else: diffuse_color = Spectrum(0.2); setting["bsdf"] = { "type": "roughplastic", "distribution": "beckmann", "alpha": 0.2, "diffuseReflectance": diffuse_color }; setting["bsdf"] = { "type": "twosided", "bsdf": setting["bsdf"] }; if self.with_alpha: setting["bsdf"] = { "type": "mask", "opacity": Spectrum(active_view.alpha), "bsdf": setting["bsdf"] }; if not self.with_colors and not self.with_alpha and not self.with_wire_frame: setting["subsurface"] = { "type": "dipole", "material": "Skimmilk", "scale": 0.5 }; elif not self.with_alpha: setting["subsurface"] = { "type": "dipole", "material": "sprite", "scale": 0.5 }; return setting;
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 __add_primitives(self, active_view): if len(active_view.subviews) > 0: for view in active_view.subviews: self.__add_primitives(view); return; old_active_view = self.scene.active_view; self.scene.active_view = active_view; scale = active_view.scale; normalize_transform = self.__get_normalize_transform(active_view); view_transform = self.__get_view_transform(active_view); glob_transform = self.__get_glob_transform(); total_transform = glob_transform * normalize_transform * view_transform; primitives = self.scene.active_view.primitives; for shape in primitives: if shape.color[3] <= 0.0: continue; color = { "type": "plastic", "diffuseReflectance": Spectrum(shape.color[:3].tolist()) }; if isinstance(shape, Cylinder): if shape.radius <= 0.0: continue; setting = self.__add_cylinder(shape); setting["bsdf"] = color; setting["toWorld"] = total_transform elif isinstance(shape, Cone): if shape.radius <= 0.0: continue; setting = self.__add_cone(shape); setting["bsdf"] = color; setting["toWorld"] = total_transform * setting["toWorld"]; elif isinstance(shape, Sphere): if shape.radius <= 0.0: continue; # Due to weird behavior in Mitsuba, all transformation is # applied directly on radius and center variable. setting = self.__add_sphere(shape); setting["radius"] *= scale; setting["center"] = total_transform * setting["center"]; setting["bsdf"] = color; else: raise NotImplementedError("Unknown primitive: {}".format(shape)); mitsuba_primative = self.plgr.create(setting); self.mitsuba_scene.addChild(mitsuba_primative); self.scene.active_view = old_active_view;
def test_incoming_flux(variant_scalar_rgb, radiance): """ We test the recorded power density of the irradiance meter, by creating a simple scene: The irradiance meter is attached to a sphere with unit radius at the coordinate origin surrounded by a constant environment emitter. We sample a number of rays and average their contribution to the cumulative power density. We expect the average value to be \\pi * L with L the radiance of the constant emitter. """ from mitsuba.core import Spectrum, ScalarVector3f from mitsuba.core.xml import load_dict scene_dict = { "type": "scene", "sensor": sensor_shape_dict(1, ScalarVector3f(0, 0, 0)), "emitter": constant_emitter_dict(radiance) } scene = load_dict(scene_dict) sensor = scene.sensors()[0] power_density_cum = 0.0 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, weight = sensor.sample_ray_differential( 0.0, wav_samples[i], pos_samples[i], dir_samples[i]) intersection = scene.ray_intersect(ray) power_density_cum += weight * \ intersection.emitter(scene).eval(intersection) power_density_avg = power_density_cum / float(num_samples) assert ek.allclose(power_density_avg, Spectrum(ek.pi * radiance))
def test_incoming_flux(variant_scalar_rgb, radiance): """ We test the recorded power density of the irradiance meter, by creating a simple scene: The irradiance meter is attached to a sphere with unit radius at the coordinate origin surrounded by a constant environment emitter. We sample a number of rays and average their contribution to the cumulative power density. We expect the average value to be \\pi * L with L the radiance of the constant emitter. """ from mitsuba.core import Spectrum from mitsuba.core.xml import load_string sensor_xml = f""" <shape version='0.1.0' type="sphere"> <float name="radius" value="1"/> <transform name="to_world"> <translate x="0" y="0" z="0"/> </transform> <sensor type="irradiancemeter"> <film type="hdrfilm"> <integer name="width" value="1"/> <integer name="height" value="1"/> </film> </sensor> </shape> """ emitter_xml = f""" <emitter type="constant"> <spectrum name="radiance" type='uniform'> <float name="value" value="{radiance}"/> </spectrum> </emitter> """ scene_xml = f""" <scene version="0.1.0"> {sensor_xml} {emitter_xml} </scene> """ scene = load_string(scene_xml) sensor = scene.sensors()[0] power_density_cum = 0.0 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, weight = sensor.sample_ray_differential(0.0, wav_samples[i], pos_samples[i], dir_samples[i]) intersection = scene.ray_intersect(ray) power_density_cum += weight * intersection.emitter(scene).eval( intersection) power_density_avg = power_density_cum / float(num_samples) assert ek.allclose(power_density_avg, Spectrum(ek.pi * radiance))
def spectrum_from_stokes(v): res = Spectrum(0.0) for i in range(4): res[i, 0] = v[i] return res
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
def spectrum(self, value, mode=''): if not mode: mode = self.color_mode spec = None if isinstance(value, (dict)): if 'type' in value: if value['type'] in {'rgb', 'srgb', 'spectrum'}: spec = self.spectrum(value['value'], value['type']) elif value['type'] == 'blackbody': spec = Spectrum() spec.fromContinuousSpectrum(BlackBodySpectrum(value['temperature'])) spec.clampNegative() spec = spec * value['scale'] elif isinstance(value, (float, int)): spec = Spectrum(value) elif isinstance(value, (str)): contspec = InterpolatedSpectrum(self.get_export_path(value)) spec = Spectrum() spec.fromContinuousSpectrum(contspec) spec.clampNegative() else: try: items = list(value) for i in items: if not isinstance(i, (float, int, tuple)): raise Exception('Error: spectrum list contains an unknown type') except: items = None if items: totitems = len(items) if isinstance(items[0], (float, int)): if totitems == 3 or totitems == 4: spec = Spectrum() if mode == 'srgb': spec.fromSRGB(items[0], items[1], items[2]) else: spec.fromLinearRGB(items[0], items[1], items[2]) elif totitems == 1: spec = Spectrum(items[0]) else: MtsLog('Expected spectrum items to be 1, 3 or 4, got %d.' % len(items), type(items), items) else: spec = Spectrum() contspec = InterpolatedSpectrum() for spd in items: (wlen, val) = spd contspec.append(wlen, val) spec.fromContinuousSpectrum(contspec) spec.clampNegative() else: MtsLog('Unknown spectrum type.', type(value), value) if spec is None: spec = Spectrum(0.0) return spec
def spectrum(self, value, mode=''): if not mode: mode = self.color_mode spec = None if isinstance(value, (dict)): if 'type' in value: if value['type'] in {'rgb', 'srgb', 'spectrum'}: spec = self.spectrum(value['value'], value['type']) elif value['type'] == 'blackbody': spec = Spectrum() spec.fromContinuousSpectrum( BlackBodySpectrum(value['temperature'])) spec.clampNegative() spec = spec * value['scale'] elif isinstance(value, (float, int)): spec = Spectrum(value) elif isinstance(value, (str)): contspec = InterpolatedSpectrum( self.get_export_path(value)) spec = Spectrum() spec.fromContinuousSpectrum(contspec) spec.clampNegative() else: try: items = list(value) for i in items: if not isinstance(i, (float, int, tuple)): raise Exception( 'Error: spectrum list contains an unknown type' ) except: items = None if items: totitems = len(items) if isinstance(items[0], (float, int)): if totitems == 3 or totitems == 4: spec = Spectrum() if mode == 'srgb': spec.fromSRGB(items[0], items[1], items[2]) else: spec.fromLinearRGB(items[0], items[1], items[2]) elif totitems == 1: spec = Spectrum(items[0]) else: MtsLog( 'Expected spectrum items to be 1, 3 or 4, got %d.' % len(items), type(items), items) else: spec = Spectrum() contspec = InterpolatedSpectrum() for spd in items: (wlen, val) = spd contspec.append(wlen, val) spec.fromContinuousSpectrum(contspec) spec.clampNegative() else: MtsLog('Unknown spectrum type.', type(value), value) if spec is None: spec = Spectrum(0.0) return spec