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; }
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; }