void PathVertex::compute_emitted_radiance( const ShadingContext& shading_context, TextureCache& texture_cache, Spectrum& radiance) const { assert(m_edf); // No radiance if we're too close to the light. if (m_shading_point->get_distance() < m_edf->get_light_near_start()) { radiance.set(0.0f); return; } if (const ShaderGroup* sg = get_material()->get_render_data().m_shader_group) shading_context.execute_osl_emission(*sg, *m_shading_point); // Evaluate the EDF inputs. InputEvaluator input_evaluator(texture_cache); m_edf->evaluate_inputs(input_evaluator, *m_shading_point); // Compute the emitted radiance. m_edf->evaluate( input_evaluator.data(), Vector3f(m_shading_point->get_geometric_normal()), Basis3f(m_shading_point->get_shading_basis()), Vector3f(m_outgoing.get_value()), radiance); }
void OSLShaderGroupExec::choose_bsdf_closure_shading_basis( const ShadingPoint& shading_point, const Vector2f& s) const { CompositeSurfaceClosure c( Basis3f(shading_point.get_shading_basis()), shading_point.get_osl_shader_globals().Ci, m_arena); float pdfs[CompositeSurfaceClosure::MaxClosureEntries]; const size_t num_closures = c.compute_pdfs(ScatteringMode::All, pdfs); if (num_closures == 0) return; const size_t index = c.choose_closure(s[1], num_closures, pdfs); shading_point.set_shading_basis( Basis3d(c.get_closure_shading_basis(index))); }
void OSLShaderGroupExec::execute_bump( const ShaderGroup& shader_group, const ShadingPoint& shading_point, const Vector2f& s) const { // Choose between BSSRDF and BSDF. if (shader_group.has_subsurface() && s[0] < 0.5f) { do_execute( shader_group, shading_point, VisibilityFlags::SubsurfaceRay); CompositeSubsurfaceClosure c( Basis3f(shading_point.get_shading_basis()), shading_point.get_osl_shader_globals().Ci, m_arena); // Pick a shading basis from one of the BSSRDF closures. if (c.get_closure_count() > 0) { const size_t index = c.choose_closure(s[1]); shading_point.set_shading_basis( Basis3d(c.get_closure_shading_basis(index))); } } else { do_execute( shader_group, shading_point, VisibilityFlags::CameraRay); choose_bsdf_closure_shading_basis(shading_point, s); } }
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); } }
void DirectLightingIntegrator::take_single_material_sample( SamplingContext& sampling_context, const MISHeuristic mis_heuristic, const Dual3d& outgoing, DirectShadingComponents& radiance) const { assert(m_light_sampler.has_hittable_lights()); // Sample material. Dual3f incoming; DirectShadingComponents sample_value; float sample_probability; if (!m_material_sampler.sample( sampling_context, outgoing, incoming, sample_value, sample_probability)) return; // Trace a ray in the direction of the reflection. Spectrum weight; const ShadingPoint& light_shading_point = m_material_sampler.trace_full( m_shading_context, incoming.get_value(), weight); // todo: wouldn't it be more efficient to look the environment up at this point? if (!light_shading_point.hit_surface()) return; // Retrieve the material at the intersection point. const Material* material = light_shading_point.get_material(); if (material == nullptr) return; const Material::RenderData& material_data = material->get_render_data(); // Retrieve the EDF at the intersection point. const EDF* edf = material_data.m_edf; if (edf == nullptr) return; // 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; // Cull the samples on the back side of the lights' shading surface. const float cos_on = dot(-incoming.get_value(), Vector3f(light_shading_point.get_shading_normal())); if (cos_on <= 0.0f) return; if (material_data.m_shader_group) { m_shading_context.execute_osl_emission( *material_data.m_shader_group, light_shading_point); } // Evaluate emitted radiance. Spectrum edf_value(Spectrum::Illuminance); float edf_prob; edf->evaluate( edf->evaluate_inputs(m_shading_context, light_shading_point), Vector3f(light_shading_point.get_geometric_normal()), Basis3f(light_shading_point.get_shading_basis()), -incoming.get_value(), edf_value, edf_prob); if (edf_prob == 0.0f) return; // Compute the square distance between the light sample and the shading point. const double square_distance = square(light_shading_point.get_distance()); // 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; if (sample_probability != BSDF::DiracDelta) { if (mis_heuristic != MISNone && square_distance > 0.0) { // Transform material_prob to surface area measure (Veach: 8.2.2.2 eq. 8.10). const float material_prob_area = sample_probability * cos_on / static_cast<float>(square_distance); // Compute the probability density wrt. surface area measure of the light sample. const float light_prob_area = m_light_sampler.evaluate_pdf( light_shading_point, m_material_sampler.get_shading_point()); // Apply the weighting function. weight *= mis( mis_heuristic, m_material_sample_count * material_prob_area, m_light_sample_count * light_prob_area); } edf_value *= weight / sample_probability; } else { edf_value *= weight; } // Add the contribution of this sample to the illumination. madd(radiance, sample_value, edf_value); }