void DirectLightingIntegrator::take_single_bsdf_or_light_sample( SamplingContext& sampling_context, Spectrum& radiance, SpectrumStack& aovs) { radiance.set(0.0f); aovs.set(0.0f); sampling_context.split_in_place(1, 1); if (sampling_context.next_double2() < 0.5) { sampling_context.split_in_place(3, m_light_sample_count); take_single_light_sample( sampling_context, DirectLightingIntegrator::mis_balance, radiance, aovs); } else { take_single_bsdf_sample( sampling_context, DirectLightingIntegrator::mis_balance, radiance, aovs); } }
Vector3d sample_lens(SamplingContext& sampling_context) const { if (m_diaphragm_map_bound) { sampling_context.split_in_place(2, 1); const Vector2d s = sampling_context.next_vector2<2>(); Vector2d v; size_t y; double prob_xy; m_importance_sampler->sample(s, v, y, prob_xy); const Vector2d lens_point = m_lens_radius * v; return Vector3d(lens_point.x, lens_point.y, 0.0); } else if (m_diaphragm_blade_count == 0) { sampling_context.split_in_place(2, 1); const Vector2d s = sampling_context.next_vector2<2>(); const Vector2d lens_point = m_lens_radius * sample_disk_uniform(s); return Vector3d(lens_point.x, lens_point.y, 0.0); } else { sampling_context.split_in_place(3, 1); const Vector3d s = sampling_context.next_vector2<3>(); const Vector2d lens_point = m_lens_radius * sample_regular_polygon_uniform( s, m_diaphragm_vertices.size(), &m_diaphragm_vertices.front()); return Vector3d(lens_point.x, lens_point.y, 0.0); } }
void Camera::initialize_ray( SamplingContext& sampling_context, ShadingRay& ray) const { ray.m_tmin = 0.0; ray.m_tmax = numeric_limits<double>::max(); ray.m_flags = VisibilityFlags::CameraRay; ray.m_depth = 0; if (m_shutter_open_time == m_shutter_close_time) { ray.m_time = ShadingRay::Time::create_with_normalized_time( 0.0, m_shutter_open_time, m_shutter_close_time); } else { sampling_context.split_in_place(1, 1); ray.m_time = ShadingRay::Time::create_with_normalized_time( sampling_context.next_double2(), m_shutter_open_time, m_shutter_close_time); } }
Vector3d sample_lens(SamplingContext& sampling_context) const { if (m_diaphragm_map_bound) { sampling_context.split_in_place(2, 1); const Vector2f s = sampling_context.next2<Vector2f>(); size_t x, y; Vector2d payload; float prob_xy; m_importance_sampler->sample(s, x, y, payload, prob_xy); const Vector2d lens_point = m_lens_radius * payload; return Vector3d(lens_point.x, lens_point.y, 0.0); } else if (m_diaphragm_blade_count == 0) { sampling_context.split_in_place(2, 1); const Vector2d s = sampling_context.next2<Vector2d>(); const Vector2d lens_point = m_lens_radius * sample_disk_uniform(s); return Vector3d(lens_point.x, lens_point.y, 0.0); } else { sampling_context.split_in_place(3, 1); const Vector3d s = sampling_context.next2<Vector3d>(); const Vector2d lens_point = m_lens_radius * sample_regular_polygon_uniform( s, m_diaphragm_vertices.size(), &m_diaphragm_vertices.front()); return Vector3d(lens_point.x, lens_point.y, 0.0); } }
void DirectLightingIntegrator::compute_outgoing_radiance_light_sampling( SamplingContext& sampling_context, const MISHeuristic mis_heuristic, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { radiance.set(0.0f); aovs.set(0.0f); sampling_context.split_in_place(3, m_light_sample_count); for (size_t i = 0; i < m_light_sample_count; ++i) { take_single_light_sample( sampling_context, mis_heuristic, outgoing, radiance, aovs); } if (m_light_sample_count > 1) { const float rcp_light_sample_count = 1.0f / m_light_sample_count; radiance *= rcp_light_sample_count; aovs *= rcp_light_sample_count; } }
bool DipoleBSSRDF::sample( SamplingContext& sampling_context, const void* data, BSSRDFSample& sample) const { const DipoleBSSRDFInputValues* values = reinterpret_cast<const DipoleBSSRDFInputValues*>(data); if (values->m_weight == 0.0) return false; sampling_context.split_in_place(3, 1); const Vector3d s = sampling_context.next_vector2<3>(); // Sample a channel. const size_t channel = sample_cdf( &values->m_channel_cdf[0], &values->m_channel_cdf[0] + values->m_channel_cdf.size(), s[0]); // Sample a radius. const double sigma_tr = values->m_sigma_tr[channel]; const double radius = sample_exponential_distribution(s[1], sigma_tr); // Sample an angle. const double phi = TwoPi * s[2]; sample.m_eta = values->m_eta; sample.m_channel = channel; sample.m_point = Vector2d(radius * cos(phi), radius * sin(phi)); sample.m_rmax2 = values->m_rmax2; return true; }
void DirectLightingIntegrator::compute_outgoing_radiance_single_sample( SamplingContext& sampling_context, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { radiance.set(0.0f); aovs.set(0.0f); if (m_light_sampler.get_emitting_triangle_count() > 0) { sampling_context.split_in_place(1, 1); if (sampling_context.next_double2() < 0.5) { sampling_context.split_in_place(3, 1); take_single_light_sample( sampling_context, MISBalance, outgoing, radiance, aovs); } else { take_single_bsdf_sample( sampling_context, MISBalance, outgoing, radiance, aovs); } radiance *= 2.0f; aovs *= 2.0f; } else { take_single_light_sample( sampling_context, MISNone, outgoing, radiance, aovs); } }
double compute_fast_ambient_occlusion( const SamplingContext& sampling_context, const AOVoxelTreeIntersector& intersector, const Vector3d& point, const Vector3d& geometric_normal, const Basis3d& shading_basis, const double max_distance, const size_t sample_count, double& min_distance) { // Create a sampling context. SamplingContext child_sampling_context = sampling_context.split(2, sample_count); // Construct the ambient occlusion ray. ShadingRay::RayType ray; ray.m_org = point; ray.m_tmin = 0.0; ray.m_tmax = max_distance; size_t computed_samples = 0; size_t occluded_samples = 0; min_distance = max_distance; for (size_t i = 0; i < sample_count; ++i) { // Generate a cosine-weighted direction over the unit hemisphere. ray.m_dir = sample_hemisphere_cosine(child_sampling_context.next_vector2<2>()); // Transform the direction to world space. ray.m_dir = shading_basis.transform_to_parent(ray.m_dir); // Don't cast rays on or below the geometric surface. if (dot(ray.m_dir, geometric_normal) <= 0.0) continue; // Count the number of computed samples. ++computed_samples; // Trace the ambient occlusion ray and count the number of occluded samples. double distance; if (intersector.trace(ray, true, distance)) { ++occluded_samples; min_distance = min(min_distance, distance); } } // Compute occlusion as a scalar between 0.0 and 1.0. double occlusion = static_cast<double>(occluded_samples); if (computed_samples > 1) occlusion /= computed_samples; assert(occlusion >= 0.0); assert(occlusion <= 1.0); return occlusion; }
void trace_non_physical_light_photon( SamplingContext& sampling_context, const LightSample& light_sample) { // Sample the light. InputEvaluator input_evaluator(m_texture_cache); SamplingContext child_sampling_context = sampling_context.split(2, 1); Vector3d emission_position, emission_direction; Spectrum light_value; double light_prob; light_sample.m_light->sample( input_evaluator, child_sampling_context.next_vector2<2>(), emission_position, emission_direction, light_value, light_prob); // Transform the emission position and direction from assembly space to world space. emission_position = light_sample.m_light_transform.point_to_parent(emission_position); emission_direction = normalize(light_sample.m_light_transform.vector_to_parent(emission_direction)); // Compute the initial particle weight. Spectrum initial_flux = light_value; initial_flux /= static_cast<float>(light_sample.m_probability * light_prob); // Build the photon ray. child_sampling_context.split_in_place(1, 1); const ShadingRay ray( emission_position, emission_direction, child_sampling_context.next_double2(), ~0); // Build the path tracer. const bool cast_indirect_light = (light_sample.m_light->get_flags() & EDF::CastIndirectLight) != 0; PathVisitor path_visitor( initial_flux, m_params.m_dl_mode == SPPMParameters::SPPM, // store direct lighting photons? cast_indirect_light, m_params.m_enable_caustics, m_local_photons); PathTracer<PathVisitor, true> path_tracer( // true = adjoint path_visitor, m_params.m_photon_tracing_rr_min_path_length, m_params.m_photon_tracing_max_path_length, m_params.m_max_iterations); // Trace the photon path. path_tracer.trace( child_sampling_context, m_intersector, m_texture_cache, ray); }
size_t generate_non_physical_light_sample( SamplingContext& sampling_context, const LightSample& light_sample, SampleVector& samples) { // Sample the light. InputEvaluator input_evaluator(m_texture_cache); sampling_context.split_in_place(2, 1); Vector3d emission_position, emission_direction; Spectrum light_value; double light_prob; light_sample.m_light->sample( input_evaluator, sampling_context.next_vector2<2>(), emission_position, emission_direction, light_value, light_prob); // Transform the emission position and direction from assembly space to world space. emission_position = light_sample.m_light_transform.point_to_parent(emission_position); emission_direction = normalize(light_sample.m_light_transform.vector_to_parent(emission_direction)); // Compute the initial particle weight. Spectrum initial_flux = light_value; initial_flux /= static_cast<float>(light_sample.m_probability * light_prob); // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay light_ray( emission_position, emission_direction, sampling_context.next_double2(), m_ray_dtime, ShadingRay::LightRay); // Build the path tracer. PathVisitor path_visitor( m_params, m_scene, m_frame, m_shading_context, samples, initial_flux); PathTracerType path_tracer( path_visitor, m_params.m_rr_min_path_length, m_params.m_max_path_length, m_params.m_max_iterations); // Handle the light vertex separately. Spectrum light_particle_flux = light_value; light_particle_flux /= static_cast<float>(light_sample.m_probability); path_visitor.visit_non_physical_light_vertex( emission_position, light_particle_flux, light_ray.m_time); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray); // Update path statistics. ++m_path_count; m_path_length.insert(path_length); // Return the number of samples generated when tracing this light path. return path_visitor.get_sample_count(); }
size_t generate_emitting_triangle_sample( SamplingContext& sampling_context, LightSample& light_sample, SampleVector& samples) { // Make sure the geometric normal of the light sample is in the same hemisphere as the shading normal. light_sample.m_geometric_normal = flip_to_same_hemisphere( light_sample.m_geometric_normal, light_sample.m_shading_normal); const Material* material = light_sample.m_triangle->m_material; const EDF* edf = material->get_edf(); // Evaluate the EDF inputs. InputEvaluator input_evaluator(m_texture_cache); // TODO: refactor this code (est.). ShadingPoint shading_point; light_sample.make_shading_point( shading_point, light_sample.m_shading_normal, m_shading_context.get_intersector()); #ifdef WITH_OSL if (const ShaderGroup* sg = material->get_osl_surface()) { // TODO: get object area somehow. const float surface_area = 0.0f; m_shading_context.execute_osl_emission(*sg, shading_point, surface_area); } #endif edf->evaluate_inputs(input_evaluator, shading_point); // Sample the EDF. sampling_context.split_in_place(2, 1); Vector3d emission_direction; Spectrum edf_value; double edf_prob; edf->sample( sampling_context, input_evaluator.data(), light_sample.m_geometric_normal, Basis3d(light_sample.m_shading_normal), sampling_context.next_vector2<2>(), emission_direction, edf_value, edf_prob); // Compute the initial particle weight. Spectrum initial_flux = edf_value; initial_flux *= static_cast<float>( dot(emission_direction, light_sample.m_shading_normal) / (light_sample.m_probability * edf_prob)); // Make a shading point that will be used to avoid self-intersections with the light sample. ShadingPoint parent_shading_point; light_sample.make_shading_point( parent_shading_point, emission_direction, m_intersector); // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay light_ray( light_sample.m_point, emission_direction, sampling_context.next_double2(), m_ray_dtime, ShadingRay::LightRay); // Build the path tracer. PathVisitor path_visitor( m_params, m_scene, m_frame, m_shading_context, samples, initial_flux); PathTracerType path_tracer( path_visitor, m_params.m_rr_min_path_length, m_params.m_max_path_length, m_params.m_max_iterations, edf->get_light_near_start()); // don't illuminate points closer than the light near start value // Handle the light vertex separately. Spectrum light_particle_flux = edf_value; // todo: only works for diffuse EDF? What we need is the light exitance light_particle_flux /= static_cast<float>(light_sample.m_probability); path_visitor.visit_area_light_vertex( light_sample, light_particle_flux, light_ray.m_time); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray, &parent_shading_point); // Update path statistics. ++m_path_count; m_path_length.insert(path_length); // Return the number of samples generated when tracing this light path. return path_visitor.get_sample_count(); }
void compute_ibl_environment_sampling( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const ShadingPoint& shading_point, const Vector3d& outgoing, const BSDF& bsdf, const void* bsdf_data, const int env_sampling_modes, const size_t bsdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing)); const Vector3d& geometric_normal = shading_point.get_geometric_normal(); const Basis3d& shading_basis = shading_point.get_shading_basis(); radiance.set(0.0f); // todo: if we had a way to know that a BSDF is purely specular, we could // immediately return black here since there will be no contribution from // such a BSDF. sampling_context.split_in_place(2, env_sample_count); for (size_t i = 0; i < env_sample_count; ++i) { // Generate a uniform sample in [0,1)^2. const Vector2d s = sampling_context.next_vector2<2>(); // Sample the environment. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Vector3d incoming; Spectrum env_value; double env_prob; environment_edf.sample( input_evaluator, s, incoming, env_value, env_prob); // Cull samples behind the shading surface. assert(is_normalized(incoming)); const double cos_in = dot(incoming, shading_basis.get_normal()); if (cos_in < 0.0) continue; // Discard occluded samples. const double transmission = shading_context.get_tracer().trace( shading_point, incoming, ShadingRay::ShadowRay); if (transmission == 0.0) continue; // Evaluate the BSDF. Spectrum bsdf_value; const double bsdf_prob = bsdf.evaluate( bsdf_data, false, // not adjoint true, // multiply by |cos(incoming, normal)| geometric_normal, shading_basis, outgoing, incoming, env_sampling_modes, bsdf_value); if (bsdf_prob == 0.0) continue; // Compute MIS weight. const double mis_weight = mis_power2( env_sample_count * env_prob, bsdf_sample_count * bsdf_prob); // Add the contribution of this sample to the illumination. env_value *= static_cast<float>(transmission / env_prob * mis_weight); env_value *= bsdf_value; radiance += env_value; } if (env_sample_count > 1) radiance /= static_cast<float>(env_sample_count); }
bool DirectLightingIntegrator::compute_incoming_radiance( SamplingContext& sampling_context, Vector3d& incoming, double& incoming_prob, Spectrum& radiance) const { if (!m_light_sampler.has_lights_or_emitting_triangles()) return false; sampling_context.split_in_place(3, 1); const Vector3d s = sampling_context.next_vector2<3>(); LightSample sample; m_light_sampler.sample(m_time, s, sample); if (sample.m_triangle) { const Material* material = sample.m_triangle->m_material; const Material::RenderData& material_data = material->get_render_data(); const EDF* edf = material_data.m_edf; // No contribution if we are computing indirect lighting but this light does not cast indirect light. if (m_indirect && !(edf->get_flags() & EDF::CastIndirectLight)) return false; // Compute the incoming direction in world space. incoming = sample.m_point - m_point; // No contribution if the shading point is behind the light. double cos_on_light = dot(-incoming, sample.m_shading_normal); if (cos_on_light <= 0.0) return false; // Compute the transmission factor between the light sample and the shading point. const double transmission = m_shading_context.get_tracer().trace_between( m_shading_point, sample.m_point, VisibilityFlags::ShadowRay); // Discard occluded samples. if (transmission == 0.0) return false; // Don't use this sample if we're closer than the light near start value. const double square_distance = square_norm(incoming); if (square_distance < square(edf->get_light_near_start())) return false; // Normalize the incoming direction. const double rcp_square_distance = 1.0 / square_distance; const double rcp_distance = sqrt(rcp_square_distance); incoming *= rcp_distance; cos_on_light *= rcp_distance; // Build a shading point on the light source. ShadingPoint light_shading_point; sample.make_shading_point( light_shading_point, sample.m_shading_normal, m_shading_context.get_intersector()); #ifdef APPLESEED_WITH_OSL if (material_data.m_shader_group) { m_shading_context.execute_osl_emission( *material_data.m_shader_group, light_shading_point); } #endif // Evaluate the EDF inputs. InputEvaluator edf_input_evaluator(m_shading_context.get_texture_cache()); edf->evaluate_inputs(edf_input_evaluator, light_shading_point); // Evaluate the EDF. edf->evaluate( edf_input_evaluator.data(), sample.m_geometric_normal, Basis3d(sample.m_shading_normal), -incoming, radiance); // Compute probability with respect to solid angle of incoming direction. const double g = cos_on_light * rcp_square_distance; incoming_prob = sample.m_probability / g; // Compute and return the incoming radiance. radiance *= static_cast<float>(transmission * g / sample.m_probability); } else { const Light* light = sample.m_light; // No contribution if we are computing indirect lighting but this light does not cast indirect light. if (m_indirect && !(light->get_flags() & Light::CastIndirectLight)) return false; // Evaluate the light. InputEvaluator input_evaluator(m_shading_context.get_texture_cache()); Vector3d emission_position, emission_direction; light->evaluate( input_evaluator, sample.m_light_transform, m_point, emission_position, emission_direction, radiance); // Compute the transmission factor between the light sample and the shading point. const double transmission = m_shading_context.get_tracer().trace_between( m_shading_point, emission_position, VisibilityFlags::ShadowRay); // Discard occluded samples. if (transmission == 0.0) return false; // Compute the incoming direction in world space. incoming = -emission_direction; incoming_prob = BSDF::DiracDelta; // Compute and return the incoming radiance. const double attenuation = light->compute_distance_attenuation(m_point, emission_position); radiance *= static_cast<float>(transmission * attenuation / sample.m_probability); } return true; }
void DiagnosticSurfaceShader::evaluate( SamplingContext& sampling_context, const PixelContext& pixel_context, const ShadingContext& shading_context, const ShadingPoint& shading_point, ShadingResult& shading_result) const { switch (m_shading_mode) { case Color: { shading_result.set_main_to_opaque_pink_linear_rgba(); const Material* material = shading_point.get_material(); if (material) { const Material::RenderData& material_data = material->get_render_data(); #ifdef APPLESEED_WITH_OSL // Execute the OSL shader if there is one. if (material_data.m_shader_group) { shading_context.execute_osl_shading( *material_data.m_shader_group, shading_point); } #endif if (material_data.m_bsdf) { InputEvaluator input_evaluator(shading_context.get_texture_cache()); material_data.m_bsdf->evaluate_inputs( shading_context, input_evaluator, shading_point); const Vector3d direction = -normalize(shading_point.get_ray().m_dir); material_data.m_bsdf->evaluate( input_evaluator.data(), false, false, shading_point.get_geometric_normal(), shading_point.get_shading_basis(), direction, direction, ScatteringMode::All, shading_result.m_main.m_color); shading_result.m_color_space = ColorSpaceSpectral; } } } break; case Coverage: shading_result.set_main_to_linear_rgb(Color3f(1.0f)); break; case Barycentric: shading_result.set_main_to_linear_rgb( vector2_to_color(shading_point.get_bary())); break; case UV: shading_result.set_main_to_linear_rgb( uvs_to_color(shading_point.get_uv(0))); break; case Tangent: case Bitangent: case ShadingNormal: { #ifdef APPLESEED_WITH_OSL const Material* material = shading_point.get_material(); if (material) { const Material::RenderData& material_data = material->get_render_data(); // Execute the OSL shader if there is one. if (material_data.m_shader_group) { sampling_context.split_in_place(2, 1); shading_context.execute_osl_bump( *material_data.m_shader_group, shading_point, sampling_context.next_vector2<2>()); } } #endif const Vector3d v = m_shading_mode == ShadingNormal ? shading_point.get_shading_basis().get_normal() : m_shading_mode == Tangent ? shading_point.get_shading_basis().get_tangent_u() : shading_point.get_shading_basis().get_tangent_v(); shading_result.set_main_to_linear_rgb(vector3_to_color(v)); } break; case GeometricNormal: shading_result.set_main_to_linear_rgb( vector3_to_color(shading_point.get_geometric_normal())); break; case OriginalShadingNormal: shading_result.set_main_to_linear_rgb( vector3_to_color(shading_point.get_original_shading_normal())); break; case WorldSpacePosition: { const Vector3d& p = shading_point.get_point(); shading_result.set_main_to_linear_rgb( Color3f(Color3d(p.x, p.y, p.z))); } break; case Sides: shading_result.set_main_to_linear_rgb( shading_point.get_side() == ObjectInstance::FrontSide ? Color3f(0.0f, 0.0f, 1.0f) : Color3f(1.0f, 0.0f, 0.0f)); break; case Depth: shading_result.set_main_to_linear_rgb( Color3f(static_cast<float>(shading_point.get_distance()))); break; case ScreenSpaceWireframe: { // Initialize the shading result to the background color. shading_result.set_main_to_linear_rgba(Color4f(0.0f, 0.0f, 0.8f, 0.5f)); if (shading_point.is_triangle_primitive()) { // Film space thickness of the wires. const double SquareWireThickness = square(0.00025); // Retrieve the time, the scene and the camera. const double time = shading_point.get_time().m_absolute; const Scene& scene = shading_point.get_scene(); const Camera& camera = *scene.get_camera(); // Compute the film space coordinates of the intersection point. Vector2d point_ndc; camera.project_point(time, shading_point.get_point(), point_ndc); // Loop over the triangle edges. for (size_t i = 0; i < 3; ++i) { // Retrieve the end points of this edge. const size_t j = (i + 1) % 3; const Vector3d vi = shading_point.get_vertex(i); const Vector3d vj = shading_point.get_vertex(j); // Compute the film space coordinates of the edge's end points. Vector2d vi_ndc, vj_ndc; if (!camera.project_segment(time, vi, vj, vi_ndc, vj_ndc)) continue; // Compute the film space distance from the intersection point to the edge. const double d = square_distance_point_segment(point_ndc, vi_ndc, vj_ndc); // Shade with the wire's color if the hit point is close enough to the edge. if (d < SquareWireThickness) { shading_result.set_main_to_linear_rgba(Color4f(1.0f)); break; } } } else { assert(shading_point.is_curve_primitive()); // todo: implement. } } break; case WorldSpaceWireframe: { // Initialize the shading result to the background color. shading_result.set_main_to_linear_rgba(Color4f(0.0f, 0.0f, 0.8f, 0.5f)); if (shading_point.is_triangle_primitive()) { // World space thickness of the wires. const double SquareWireThickness = square(0.0015); // Retrieve the world space intersection point. const Vector3d& point = shading_point.get_point(); // Loop over the triangle edges. for (size_t i = 0; i < 3; ++i) { // Retrieve the end points of this edge. const size_t j = (i + 1) % 3; const Vector3d& vi = shading_point.get_vertex(i); const Vector3d& vj = shading_point.get_vertex(j); // Compute the world space distance from the intersection point to the edge. const double d = square_distance_point_segment(point, vi, vj); // Shade with the wire's color if the hit point is close enough to the edge. if (d < SquareWireThickness) { shading_result.set_main_to_linear_rgba(Color4f(1.0f)); break; } } } else { assert(shading_point.is_curve_primitive()); // todo: implement. } } break; case AmbientOcclusion: { // Compute the occlusion. const double occlusion = compute_ambient_occlusion( sampling_context, sample_hemisphere_uniform<double>, shading_context.get_intersector(), shading_point, m_ao_max_distance, m_ao_samples); // Return a gray scale value proportional to the accessibility. const float accessibility = static_cast<float>(1.0 - occlusion); shading_result.set_main_to_linear_rgb(Color3f(accessibility)); } break; case AssemblyInstances: shading_result.set_main_to_linear_rgb( integer_to_color(shading_point.get_assembly_instance().get_uid())); break; case ObjectInstances: shading_result.set_main_to_linear_rgb( integer_to_color(shading_point.get_object_instance().get_uid())); break; case Regions: { const uint32 h = mix_uint32( static_cast<uint32>(shading_point.get_object_instance().get_uid()), static_cast<uint32>(shading_point.get_region_index())); shading_result.set_main_to_linear_rgb(integer_to_color(h)); } break; case Primitives: { const uint32 h = mix_uint32( static_cast<uint32>(shading_point.get_object_instance().get_uid()), static_cast<uint32>(shading_point.get_region_index()), static_cast<uint32>(shading_point.get_primitive_index())); shading_result.set_main_to_linear_rgb(integer_to_color(h)); } break; case Materials: { const Material* material = shading_point.get_material(); if (material) shading_result.set_main_to_linear_rgb(integer_to_color(material->get_uid())); else shading_result.set_main_to_opaque_pink_linear_rgba(); } break; case RaySpread: { const ShadingRay& ray = shading_point.get_ray(); if (!ray.m_has_differentials) break; const Material* material = shading_point.get_material(); if (material) { const Material::RenderData& material_data = material->get_render_data(); #ifdef APPLESEED_WITH_OSL // Execute the OSL shader if there is one. if (material_data.m_shader_group) { shading_context.execute_osl_shading( *material_data.m_shader_group, shading_point); } #endif if (material_data.m_bsdf) { const Dual3d outgoing( -ray.m_dir, ray.m_dir - ray.m_rx.m_dir, ray.m_dir - ray.m_ry.m_dir); InputEvaluator input_evaluator(shading_context.get_texture_cache()); material_data.m_bsdf->evaluate_inputs( shading_context, input_evaluator, shading_point); const void* bsdf_data = input_evaluator.data(); BSDFSample sample(shading_point, outgoing); material_data.m_bsdf->sample( sampling_context, bsdf_data, false, false, sample); if (!sample.m_incoming.has_derivatives()) break; // The 3.0 factor is chosen so that ray spread from Lambertian BRDFs is approximately 1. const double spread = max( norm(sample.m_incoming.get_dx()), norm(sample.m_incoming.get_dy())) * 3.0; shading_result.set_main_to_linear_rgb( Color3f(static_cast<float>(spread))); } } } break; case FacingRatio: { const Vector3d& normal = shading_point.get_shading_normal(); const Vector3d& view = shading_point.get_ray().m_dir; const double facing = abs(dot(normal, view)); shading_result.set_main_to_linear_rgb( Color3f(static_cast<float>(facing))); } break; default: assert(false); shading_result.set_main_to_transparent_black_linear_rgba(); break; } }
void DirectLightingIntegrator::add_non_physical_light_sample_contribution( SamplingContext& sampling_context, const LightSample& sample, const Dual3d& outgoing, DirectShadingComponents& radiance, LightPathStream* light_path_stream) const { const Light* light = sample.m_light; // No contribution if we are computing indirect lighting but this light does not cast indirect light. if (m_indirect && !(light->get_flags() & Light::CastIndirectLight)) return; // Generate a uniform sample in [0,1)^2. SamplingContext child_sampling_context = sampling_context.split(2, 1); const Vector2d s = child_sampling_context.next2<Vector2d>(); // Evaluate the light. Vector3d emission_position, emission_direction; Spectrum light_value(Spectrum::Illuminance); float probability; light->sample( m_shading_context, sample.m_light_transform, m_material_sampler.get_point(), s, emission_position, emission_direction, light_value, probability); // Compute the incoming direction in world space. const Vector3d incoming = -emission_direction; // Compute the transmission factor between the light sample and the shading point. Spectrum transmission; m_material_sampler.trace_between( m_shading_context, emission_position, transmission); // Discard occluded samples. if (is_zero(transmission)) return; // Evaluate the BSDF (or volume). DirectShadingComponents material_value; const float material_probability = m_material_sampler.evaluate( Vector3f(outgoing.get_value()), Vector3f(incoming), m_light_sampling_modes, material_value); assert(material_probability >= 0.0f); if (material_probability == 0.0f) return; // Add the contribution of this sample to the illumination. const float attenuation = light->compute_distance_attenuation( m_material_sampler.get_point(), emission_position); light_value *= transmission; light_value *= attenuation / (sample.m_probability * probability); madd(radiance, material_value, light_value); // Record light path event. if (light_path_stream) { light_path_stream->sampled_non_physical_light( *light, emission_position, material_value.m_beauty, light_value); } }
void DirectLightingIntegrator::add_emitting_shape_sample_contribution( SamplingContext& sampling_context, const LightSample& sample, const MISHeuristic mis_heuristic, const Dual3d& outgoing, DirectShadingComponents& radiance, LightPathStream* light_path_stream) const { const Material* material = sample.m_shape->get_material(); const Material::RenderData& material_data = material->get_render_data(); const EDF* edf = material_data.m_edf; // No contribution if we are computing indirect lighting but this light does not cast indirect light. if (m_indirect && !(edf->get_flags() & EDF::CastIndirectLight)) return; // Compute the incoming direction in world space. Vector3d incoming = sample.m_point - m_material_sampler.get_point(); // No contribution if the shading point is behind the light. double cos_on = dot(-incoming, sample.m_shading_normal); if (cos_on <= 0.0) return; // Compute the square distance between the light sample and the shading point. const double square_distance = square_norm(incoming); // Don't use this sample if we're closer than the light near start value. if (square_distance < square(edf->get_light_near_start())) return; const double rcp_sample_square_distance = 1.0 / square_distance; const double rcp_sample_distance = sqrt(rcp_sample_square_distance); // Normalize the incoming direction. cos_on *= rcp_sample_distance; incoming *= rcp_sample_distance; // Probabilistically skip light samples with low maximum contribution. float contribution_prob = 1.0f; if (m_low_light_threshold > 0.0f) { // Compute the approximate maximum contribution of this light sample. const float max_contribution = static_cast<float>( cos_on * rcp_sample_square_distance * sample.m_shape->get_max_flux()); // Use Russian Roulette to skip this sample if its maximum contribution is low. if (max_contribution < m_low_light_threshold) { // Generate a uniform sample in [0,1). SamplingContext child_sampling_context = sampling_context.split(1, 1); const float s = child_sampling_context.next2<float>(); // Compute the probability of taking the sample's contribution into account. contribution_prob = max_contribution / m_low_light_threshold; // Russian Roulette. if (!pass_rr(contribution_prob, s)) return; } } // Compute the transmission factor between the light sample and the shading point. Spectrum transmission; m_material_sampler.trace_between( m_shading_context, sample.m_point, transmission); // Discard occluded samples. if (is_zero(transmission)) return; // Evaluate the BSDF (or volume). DirectShadingComponents material_value; const float material_probability = m_material_sampler.evaluate( Vector3f(outgoing.get_value()), Vector3f(incoming), m_light_sampling_modes, material_value); assert(material_probability >= 0.0f); if (material_probability == 0.0f) return; // Build a shading point on the light source. ShadingPoint light_shading_point; sample.make_shading_point( light_shading_point, sample.m_shading_normal, m_shading_context.get_intersector()); if (material_data.m_shader_group) { m_shading_context.execute_osl_emission( *material_data.m_shader_group, light_shading_point); } // Evaluate the EDF. Spectrum edf_value(Spectrum::Illuminance); edf->evaluate( edf->evaluate_inputs(m_shading_context, light_shading_point), Vector3f(sample.m_geometric_normal), Basis3f(Vector3f(sample.m_shading_normal)), -Vector3f(incoming), edf_value); const float g = static_cast<float>(cos_on * rcp_sample_square_distance); // Apply MIS weighting. const float mis_weight = mis( mis_heuristic, m_light_sample_count * sample.m_probability, m_material_sample_count * material_probability * g); // Add the contribution of this sample to the illumination. edf_value *= transmission; edf_value *= (mis_weight * g) / (sample.m_probability * contribution_prob); madd(radiance, material_value, edf_value); // Record light path event. if (light_path_stream) { light_path_stream->sampled_emitting_shape( *sample.m_shape, sample.m_point, material_value.m_beauty, edf_value); } }
size_t generate_non_physical_light_sample( SamplingContext& sampling_context, const LightSample& light_sample, SampleVector& samples) { // Sample the light. InputEvaluator input_evaluator(m_texture_cache); sampling_context.split_in_place(2, 1); Vector3d emission_position, emission_direction; Spectrum light_value; float light_prob; light_sample.m_light->sample( input_evaluator, light_sample.m_light_transform, sampling_context.next2<Vector2d>(), emission_position, emission_direction, light_value, light_prob); // Compute the initial particle weight. Spectrum initial_flux = light_value; initial_flux /= light_sample.m_probability * light_prob; // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay::Time time = ShadingRay::Time::create_with_normalized_time( sampling_context.next2<float>(), m_shutter_open_time, m_shutter_close_time); const ShadingRay light_ray( emission_position, emission_direction, time, VisibilityFlags::LightRay, 0); // Build the path tracer. PathVisitor path_visitor( m_params, m_scene, m_frame, m_shading_context, sampling_context, samples, initial_flux); PathTracerType path_tracer( path_visitor, m_params.m_rr_min_path_length, m_params.m_max_path_length, m_params.m_max_iterations); // Handle the light vertex separately. Spectrum light_particle_flux = light_value; light_particle_flux /= light_sample.m_probability; path_visitor.visit_non_physical_light_vertex( emission_position, light_particle_flux, light_ray.m_time); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray); // Update path statistics. ++m_path_count; m_path_length.insert(path_length); // Return the number of samples generated when tracing this light path. return path_visitor.get_sample_count(); }
void compute_ibl_environment_sampling( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const BSSRDF& bssrdf, const void* bssrdf_data, const ShadingPoint& incoming_point, const ShadingPoint& outgoing_point, const Dual3d& outgoing, const size_t bssrdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing.get_value())); const Basis3d& shading_basis = incoming_point.get_shading_basis(); radiance.set(0.0f); sampling_context.split_in_place(2, env_sample_count); for (size_t i = 0; i < env_sample_count; ++i) { // Generate a uniform sample in [0,1)^2. const Vector2d s = sampling_context.next_vector2<2>(); // Sample the environment. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Vector3d incoming; Spectrum env_value; double env_prob; environment_edf.sample( shading_context, input_evaluator, s, incoming, env_value, env_prob); // Cull samples behind the shading surface. assert(is_normalized(incoming)); const double cos_in = dot(incoming, shading_basis.get_normal()); if (cos_in <= 0.0) continue; // Discard occluded samples. const double transmission = shading_context.get_tracer().trace( incoming_point, incoming, VisibilityFlags::ShadowRay); if (transmission == 0.0) continue; // Evaluate the BSSRDF. Spectrum bssrdf_value; bssrdf.evaluate( bssrdf_data, outgoing_point, outgoing.get_value(), incoming_point, incoming, bssrdf_value); // Compute MIS weight. const double bssrdf_prob = cos_in * RcpPi; const double mis_weight = mis_power2( env_sample_count * env_prob, bssrdf_sample_count * bssrdf_prob); // Add the contribution of this sample to the illumination. env_value *= static_cast<float>(transmission * cos_in / env_prob * mis_weight); env_value *= bssrdf_value; radiance += env_value; } if (env_sample_count > 1) radiance /= static_cast<float>(env_sample_count); }
void compute_ibl_bssrdf_sampling( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const BSSRDF& bssrdf, const void* bssrdf_data, const ShadingPoint& incoming_point, const ShadingPoint& outgoing_point, const Dual3d& outgoing, const size_t bssrdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing.get_value())); radiance.set(0.0f); sampling_context.split_in_place(2, bssrdf_sample_count); for (size_t i = 0; i < bssrdf_sample_count; ++i) { // Generate a uniform sample in [0,1)^2. const Vector2d s = sampling_context.next_vector2<2>(); // Sample the BSSRDF (hemisphere cosine). Vector3d incoming = sample_hemisphere_cosine(s); const double cos_in = incoming.y; const double bssrdf_prob = cos_in * RcpPi; incoming = incoming_point.get_shading_basis().transform_to_parent(incoming); if (incoming_point.get_side() == ObjectInstance::BackSide) incoming = -incoming; assert(is_normalized(incoming)); // Discard occluded samples. const double transmission = shading_context.get_tracer().trace( incoming_point, incoming, VisibilityFlags::ShadowRay); if (transmission == 0.0) continue; // Evaluate the BSSRDF. Spectrum bssrdf_value; bssrdf.evaluate( bssrdf_data, outgoing_point, outgoing.get_value(), incoming_point, incoming, bssrdf_value); // Evaluate the environment's EDF. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Spectrum env_value; double env_prob; environment_edf.evaluate( shading_context, input_evaluator, incoming, env_value, env_prob); // Compute MIS weight. const double mis_weight = mis_power2( bssrdf_sample_count * bssrdf_prob, env_sample_count * env_prob); // Add the contribution of this sample to the illumination. env_value *= static_cast<float>(transmission * cos_in / bssrdf_prob * mis_weight); env_value *= bssrdf_value; radiance += env_value; } if (bssrdf_sample_count > 1) radiance /= static_cast<float>(bssrdf_sample_count); }
size_t SubsurfaceSampler::sample( SamplingContext& sampling_context, const ShadingPoint& outgoing_point, const BSSRDF& bssrdf, const void* bssrdf_data, SubsurfaceSample samples[], const size_t max_sample_count) { assert(max_sample_count > 0); // Sample the diffusion profile. BSSRDFSample bssrdf_sample(sampling_context); if (!bssrdf.sample(bssrdf_data, bssrdf_sample)) return 0; // Reject points too far away. // This introduces negligible bias in comparison to the other approximations. const Vector2d& point(bssrdf_sample.get_point()); const double radius2 = square_norm(point); const double rmax2 = bssrdf_sample.get_rmax2(); if (radius2 > rmax2) return 0; // Evaluate the PDF of the diffusion profile. const double radius = sqrt(radius2); const double bssrdf_sample_pdf = bssrdf.evaluate_pdf(bssrdf_data, bssrdf_sample.get_channel(), radius); // Pick a sampling basis. sampling_context.split_in_place(1, 1); Axis sampling_axis; Basis3d sampling_basis; double sampling_basis_pdf; pick_sampling_basis( outgoing_point.get_shading_basis(), sampling_context.next_double2(), sampling_axis, sampling_basis, sampling_basis_pdf); // Compute height of sample point on (positive) hemisphere of radius Rmax. assert(rmax2 >= radius2); const double h = sqrt(rmax2 - radius2); // Compute sphere entry and exit points. Vector3d entry_point, exit_point; entry_point = exit_point = outgoing_point.get_point(); entry_point += sampling_basis.transform_to_parent(Vector3d(point[0], +h, point[1])); exit_point += sampling_basis.transform_to_parent(Vector3d(point[0], -h, point[1])); assert(feq(norm(exit_point - entry_point), 2.0 * h, 1.0e-9)); // Build a probe ray inscribed inside the sphere of radius Rmax. ShadingRay probe_ray( entry_point, -sampling_basis.get_normal(), 0.0, 2.0 * h, outgoing_point.get_time(), VisibilityFlags::ProbeRay, outgoing_point.get_ray().m_depth + 1); const Material* material = outgoing_point.get_material(); ShadingPoint shading_points[2]; size_t shading_point_index = 0; ShadingPoint* parent_shading_point = 0; size_t sample_count = 0; // Trace the ray and return all intersections (or up to max_sample_count of them) found inside the sphere. while (true) { // Continue tracing the ray. shading_points[shading_point_index].clear(); if (!m_shading_context.get_intersector().trace( probe_ray, shading_points[shading_point_index], parent_shading_point)) break; // Only consider points lying on surfaces with the same material as the outgoing point. if (shading_points[shading_point_index].get_material() == material) { // Execute the OSL shader if we have one. Needed for bump mapping. #ifdef APPLESEED_WITH_OSL if (material->has_osl_surface()) { sampling_context.split_in_place(1, 1); m_shading_context.execute_osl_bump( *material->get_osl_surface(), shading_points[shading_point_index], sampling_context.next_double2()); } #endif SubsurfaceSample& sample = samples[sample_count++]; sample.m_point = shading_points[shading_point_index]; // Compute sample probability. sample.m_probability = bssrdf_sample_pdf * sampling_basis_pdf * abs(dot(sampling_basis.get_normal(), sample.m_point.get_geometric_normal())); // todo: or shading normal? // Weight sample probability with multiple importance sampling. sample.m_probability /= compute_mis_weight( bssrdf, bssrdf_data, bssrdf_sample.get_channel(), sampling_basis, sampling_axis, sample.m_probability, outgoing_point.get_point(), sample.m_point.get_point(), sample.m_point.get_geometric_normal()); // todo: or shading normal? // Return the relative index of refraction. sample.m_eta = bssrdf_sample.get_eta(); if (sample_count == max_sample_count) break; } // Move the ray's origin past the hit surface. probe_ray.m_org = shading_points[shading_point_index].get_point(); probe_ray.m_tmax = norm(exit_point - probe_ray.m_org); // Swap the current and parent shading points. parent_shading_point = &shading_points[shading_point_index]; shading_point_index = 1 - shading_point_index; } return sample_count; }
size_t generate_environment_sample( SamplingContext& sampling_context, const EnvironmentEDF* env_edf, SampleVector& samples) { // Sample the environment. sampling_context.split_in_place(2, 1); InputEvaluator input_evaluator(m_texture_cache); Vector3d outgoing; Spectrum env_edf_value; double env_edf_prob; env_edf->sample( input_evaluator, sampling_context.next_vector2<2>(), outgoing, // points toward the environment env_edf_value, env_edf_prob); // Compute the center of the tangent disk. const Vector3d disk_center = m_safe_scene_radius * outgoing; // Uniformly sample the tangent disk. sampling_context.split_in_place(2, 1); const Vector2d disk_point = m_safe_scene_radius * sample_disk_uniform(sampling_context.next_vector2<2>()); // Compute the origin of the light ray. const Basis3d basis(-outgoing); const Vector3d ray_origin = disk_center + disk_point[0] * basis.get_tangent_u() + disk_point[1] * basis.get_tangent_v(); // Compute the initial particle weight. Spectrum initial_flux = env_edf_value; initial_flux /= static_cast<float>(m_disk_point_prob * env_edf_prob); // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay light_ray( ray_origin, -outgoing, sampling_context.next_double2(), m_ray_dtime, ShadingRay::LightRay); // Build the path tracer. PathVisitor path_visitor( m_params, m_scene, m_frame, m_shading_context, samples, initial_flux); PathTracerType path_tracer( path_visitor, m_params.m_rr_min_path_length, m_params.m_max_path_length, m_params.m_max_iterations); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray); // Update path statistics. ++m_path_count; m_path_length.insert(path_length); // Return the number of samples generated when tracing this light path. return path_visitor.get_sample_count(); }
void trace_emitting_triangle_photon( SamplingContext& sampling_context, LightSample& light_sample) { // Make sure the geometric normal of the light sample is in the same hemisphere as the shading normal. light_sample.m_geometric_normal = flip_to_same_hemisphere( light_sample.m_geometric_normal, light_sample.m_shading_normal); const EDF* edf = light_sample.m_triangle->m_edf; // Evaluate the EDF inputs. InputEvaluator input_evaluator(m_texture_cache); edf->evaluate_inputs(input_evaluator, light_sample.m_bary); // Sample the EDF. SamplingContext child_sampling_context = sampling_context.split(2, 1); Vector3d emission_direction; Spectrum edf_value; double edf_prob; edf->sample( input_evaluator.data(), light_sample.m_geometric_normal, Basis3d(light_sample.m_shading_normal), child_sampling_context.next_vector2<2>(), emission_direction, edf_value, edf_prob); // Compute the initial particle weight. Spectrum initial_flux = edf_value; initial_flux *= static_cast<float>( dot(emission_direction, light_sample.m_shading_normal) / (light_sample.m_probability * edf_prob * m_params.m_light_photon_count)); // Make a shading point that will be used to avoid self-intersections with the light sample. ShadingPoint parent_shading_point; light_sample.make_shading_point( parent_shading_point, emission_direction, m_intersector); // Build the photon ray. child_sampling_context.split_in_place(1, 1); const ShadingRay ray( light_sample.m_point, emission_direction, child_sampling_context.next_double2(), ShadingRay::LightRay); // Build the path tracer. const bool cast_indirect_light = (edf->get_flags() & EDF::CastIndirectLight) != 0; PathVisitor path_visitor( initial_flux, m_params.m_dl_mode == SPPMParameters::SPPM, // store direct lighting photons? cast_indirect_light, m_params.m_enable_caustics, m_local_photons); PathTracer<PathVisitor, true> path_tracer( // true = adjoint path_visitor, m_params.m_photon_tracing_rr_min_path_length, m_params.m_photon_tracing_max_path_length, m_params.m_max_iterations, edf->get_light_near_start()); // don't illuminate points closer than the light near start value // Trace the photon path. path_tracer.trace( child_sampling_context, m_shading_context, ray, &parent_shading_point); }
void DirectLightingIntegrator::compute_outgoing_radiance_light_sampling_low_variance( SamplingContext& sampling_context, const MISHeuristic mis_heuristic, const Dual3d& outgoing, Spectrum& radiance, SpectrumStack& aovs) const { radiance.set(0.0f); aovs.set(0.0f); // todo: if we had a way to know that a BSDF is purely specular, we could // immediately return black here since there will be no contribution from // such a BSDF. // Sample emitting triangles. if (m_light_sampler.get_emitting_triangle_count() > 0) { sampling_context.split_in_place(3, m_light_sample_count); for (size_t i = 0; i < m_light_sample_count; ++i) { const Vector3d s = sampling_context.next_vector2<3>(); LightSample sample; m_light_sampler.sample_emitting_triangles(m_time, s, sample); add_emitting_triangle_sample_contribution( sample, mis_heuristic, outgoing, radiance, aovs); } if (m_light_sample_count > 1) { const float rcp_light_sample_count = 1.0f / m_light_sample_count; radiance *= rcp_light_sample_count; aovs *= rcp_light_sample_count; } } // Sample non-physical light sources. const size_t light_count = m_light_sampler.get_non_physical_light_count(); if (light_count > 0) { sampling_context.split_in_place(2, light_count); for (size_t i = 0; i < light_count; ++i) { const Vector2d s = sampling_context.next_vector2<2>(); LightSample sample; m_light_sampler.sample_non_physical_light(m_time, s, i, sample); add_non_physical_light_sample_contribution( sample, outgoing, radiance, aovs); } } }
size_t generate_emitting_triangle_sample( SamplingContext& sampling_context, LightSample& light_sample, SampleVector& samples) { // Make sure the geometric normal of the light sample is in the same hemisphere as the shading normal. light_sample.m_geometric_normal = flip_to_same_hemisphere( light_sample.m_geometric_normal, light_sample.m_shading_normal); const Material* material = light_sample.m_triangle->m_material; const Material::RenderData& material_data = material->get_render_data(); // Build a shading point on the light source. ShadingPoint light_shading_point; light_sample.make_shading_point( light_shading_point, light_sample.m_shading_normal, m_shading_context.get_intersector()); if (material_data.m_shader_group) { m_shading_context.execute_osl_emission( *material_data.m_shader_group, light_shading_point); } // Evaluate the EDF inputs. InputEvaluator input_evaluator(m_texture_cache); material_data.m_edf->evaluate_inputs(input_evaluator, light_shading_point); // Sample the EDF. sampling_context.split_in_place(2, 1); Vector3f emission_direction; Spectrum edf_value; float edf_prob; material_data.m_edf->sample( sampling_context, input_evaluator.data(), Vector3f(light_sample.m_geometric_normal), Basis3f(Vector3f(light_sample.m_shading_normal)), sampling_context.next2<Vector2f>(), emission_direction, edf_value, edf_prob); // Compute the initial particle weight. Spectrum initial_flux = edf_value; initial_flux *= dot(emission_direction, Vector3f(light_sample.m_shading_normal)) / (light_sample.m_probability * edf_prob); // Make a shading point that will be used to avoid self-intersections with the light sample. ShadingPoint parent_shading_point; light_sample.make_shading_point( parent_shading_point, Vector3d(emission_direction), m_intersector); // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay::Time time = ShadingRay::Time::create_with_normalized_time( sampling_context.next2<float>(), m_shutter_open_time, m_shutter_close_time); const ShadingRay light_ray( light_sample.m_point, Vector3d(emission_direction), time, VisibilityFlags::LightRay, 0); // Build the path tracer. PathVisitor path_visitor( m_params, m_scene, m_frame, m_shading_context, sampling_context, samples, initial_flux); PathTracerType path_tracer( path_visitor, m_params.m_rr_min_path_length, m_params.m_max_path_length, m_params.m_max_iterations, material_data.m_edf->get_light_near_start()); // don't illuminate points closer than the light near start value // Handle the light vertex separately. Spectrum light_particle_flux = edf_value; // todo: only works for diffuse EDF? What we need is the light exitance light_particle_flux /= light_sample.m_probability; path_visitor.visit_area_light_vertex( light_sample, light_particle_flux, light_ray.m_time); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray, &parent_shading_point); // Update path statistics. ++m_path_count; m_path_length.insert(path_length); // Return the number of samples generated when tracing this light path. return path_visitor.get_sample_count(); }
FORCE_INLINE virtual Mode sample( SamplingContext& sampling_context, const void* data, const bool adjoint, const bool cosine_mult, const Vector3d& geometric_normal, const Basis3d& shading_basis, const Vector3d& outgoing, Vector3d& incoming, Spectrum& value, double& probability) const { const InputValues* values = static_cast<const InputValues*>(data); // Compute the incoming direction by sampling the MDF. sampling_context.split_in_place(2, 1); const Vector2d s = sampling_context.next_vector2<2>(); const Vector3d m = m_mdf->sample(s, values->m_ax, values->m_ay); const Vector3d ht = shading_basis.transform_to_parent(m); if (!refract( outgoing, ht, values->m_from_ior / values->m_to_ior, incoming)) { // Ignore TIR. return Absorption; } // If incoming and outgoing are on the same hemisphere // this is not a refraction. const Vector3d& n = shading_basis.get_normal(); if (dot(incoming, n) * dot(outgoing, n) >= 0.0) return Absorption; const double G = m_mdf->G( shading_basis.transform_to_local(incoming), shading_basis.transform_to_local(outgoing), m, values->m_ax, values->m_ay); if (G == 0.0) return Absorption; const double D = m_mdf->D(m, values->m_ax, values->m_ay); const double cos_oh = dot(outgoing, ht); const double cos_ih = dot(incoming, ht); const double cos_in = dot(incoming, n); const double cos_on = dot(outgoing, n); // [1] equation 21. double v = abs((cos_ih * cos_oh) / (cos_in * cos_on)); v *= square(values->m_to_ior) * D * G; const double denom = values->m_to_ior * cos_ih + values->m_from_ior * cos_oh; v /= square(denom); value.set(static_cast<float>(v)); const double ht_norm = norm(values->m_from_ior * outgoing + values->m_to_ior * incoming); const double dwh_dwo = refraction_jacobian( incoming, values->m_to_ior, ht, ht_norm); probability = m_mdf->pdf(m, values->m_ax, values->m_ay) * dwh_dwo; return Glossy; }
size_t generate_environment_sample( SamplingContext& sampling_context, const EnvironmentEDF* env_edf, SampleVector& samples) { // Sample the environment. sampling_context.split_in_place(2, 1); InputEvaluator input_evaluator(m_texture_cache); Vector3f outgoing; Spectrum env_edf_value; float env_edf_prob; env_edf->sample( m_shading_context, input_evaluator, sampling_context.next2<Vector2f>(), outgoing, // points toward the environment env_edf_value, env_edf_prob); // Uniformly sample the tangent disk. sampling_context.split_in_place(2, 1); const Vector2d p = m_scene_radius * sample_disk_uniform(sampling_context.next2<Vector2d>()); // Compute the origin of the light ray. const Basis3d basis(-Vector3d(outgoing)); const Vector3d ray_origin = m_scene_center - m_safe_scene_diameter * basis.get_normal() // a safe radius would have been sufficient + p[0] * basis.get_tangent_u() + + p[1] * basis.get_tangent_v(); // Compute the initial particle weight. Spectrum initial_flux = env_edf_value; initial_flux /= m_disk_point_prob * env_edf_prob; // Build the light ray. sampling_context.split_in_place(1, 1); const ShadingRay::Time time = ShadingRay::Time::create_with_normalized_time( sampling_context.next2<float>(), m_shutter_open_time, m_shutter_close_time); const ShadingRay light_ray( ray_origin, -Vector3d(outgoing), time, VisibilityFlags::LightRay, 0); // Build the path tracer. PathVisitor path_visitor( m_params, m_scene, m_frame, m_shading_context, sampling_context, samples, initial_flux); PathTracerType path_tracer( path_visitor, m_params.m_rr_min_path_length, m_params.m_max_path_length, m_params.m_max_iterations); // Trace the light path. const size_t path_length = path_tracer.trace( sampling_context, m_shading_context, light_ray); // Update path statistics. ++m_path_count; m_path_length.insert(path_length); // Return the number of samples generated when tracing this light path. return path_visitor.get_sample_count(); }
void DirectLightingIntegrator::compute_outgoing_radiance_light_sampling_low_variance( SamplingContext& sampling_context, const MISHeuristic mis_heuristic, const Dual3d& outgoing, DirectShadingComponents& radiance, LightPathStream* light_path_stream) const { radiance.set(0.0f); // No light source in the scene. if (!m_light_sampler.has_lights()) return; // Check if PDF of the sampler is Dirac delta and therefore cannot contribute to the light sampling. if (!m_material_sampler.contributes_to_light_sampling()) return; if (m_light_sample_count > 0) { // Add contributions from all non-physical light sources that aren't part of the lightset. for (size_t i = 0, e = m_light_sampler.get_non_physical_light_count(); i < e; ++i) { // Sample the light. LightSample sample; m_light_sampler.sample_non_physical_light(m_time, i, sample); // Add the contribution of the chosen light. add_non_physical_light_sample_contribution( sampling_context, sample, outgoing, radiance, light_path_stream); } } // Add contributions from the light set. if (m_light_sampler.has_lightset()) { DirectShadingComponents lightset_radiance; sampling_context.split_in_place(3, m_light_sample_count); for (size_t i = 0, e = m_light_sample_count; i < e; ++i) { // Sample the light set. LightSample sample; m_light_sampler.sample_lightset( m_time, sampling_context.next2<Vector3f>(), m_material_sampler.get_shading_point(), sample); // Add the contribution of the chosen light. if (sample.m_shape) { add_emitting_shape_sample_contribution( sampling_context, sample, mis_heuristic, outgoing, lightset_radiance, light_path_stream); } else { add_non_physical_light_sample_contribution( sampling_context, sample, outgoing, lightset_radiance, light_path_stream); } } if (m_light_sample_count > 1) lightset_radiance /= static_cast<float>(m_light_sample_count); radiance += lightset_radiance; } }