DirectLightingIntegrator::DirectLightingIntegrator( const ShadingContext& shading_context, const LightSampler& light_sampler, const Vector3d& point, const Vector3d& geometric_normal, const Basis3d& shading_basis, const double time, const Vector3d& outgoing, const BSDF& bsdf, const void* bsdf_data, const int bsdf_modes, const size_t bsdf_sample_count, const size_t light_sample_count, const ShadingPoint* parent_shading_point) : m_shading_context(shading_context) , m_light_sampler(light_sampler) , m_point(point) , m_geometric_normal(geometric_normal) , m_shading_basis(shading_basis) , m_time(time) , m_outgoing(outgoing) , m_bsdf(bsdf) , m_bsdf_data(bsdf_data) , m_bsdf_modes(bsdf_modes) , m_bsdf_sample_count(bsdf_sample_count) , m_light_sample_count(light_sample_count) , m_parent_shading_point(parent_shading_point) { assert(is_normalized(geometric_normal)); assert(is_normalized(outgoing)); }
void throttle_cb(const std_msgs::Float64::ConstPtr &req) { float throttle_normalized = req->data; // note: && are lazy, is_normalized() should be called only if reverse_throttle are true. if (reverse_throttle && !is_normalized(throttle_normalized, -1.0, 1.0)) return; else if (!is_normalized(throttle_normalized, 0.0, 1.0)) return; send_attitude_throttle(throttle_normalized); }
void attitude_twist_cb(const geometry_msgs::TwistStamped::ConstPtr &req, const mavros_msgs::Thrust::ConstPtr &thrust_msg) { Eigen::Vector3d ang_vel; tf::vectorMsgToEigen(req->twist.angular, ang_vel); if (is_normalized(thrust_msg->thrust)) send_attitude_ang_velocity(req->header.stamp, ang_vel, thrust_msg->thrust); }
DirectLightingIntegrator::DirectLightingIntegrator( const ShadingContext& shading_context, const LightSampler& light_sampler, const PathVertex& vertex, const int bsdf_sampling_modes, const int light_sampling_modes, const size_t bsdf_sample_count, const size_t light_sample_count, const bool indirect) : m_shading_context(shading_context) , m_light_sampler(light_sampler) , m_shading_point(*vertex.m_shading_point) , m_point(vertex.get_point()) , m_geometric_normal(vertex.get_geometric_normal()) , m_shading_basis(vertex.get_shading_basis()) , m_time(vertex.get_time()) , m_outgoing(vertex.m_outgoing) , m_bsdf(*vertex.m_bsdf) , m_bsdf_data(vertex.m_bsdf_data) , m_bsdf_sampling_modes(bsdf_sampling_modes) , m_light_sampling_modes(light_sampling_modes) , m_bsdf_sample_count(bsdf_sample_count) , m_light_sample_count(light_sample_count) , m_indirect(indirect) { assert(is_normalized(vertex.m_outgoing)); }
bool CodeInterval::update(const Interval& symbol) throw(not_normalized, invalid_symbol) { if(!is_normalized()) { throw not_normalized("Interval must be normalized before update."); } if(symbol.range < 3 || symbol.base + symbol.range < symbol.base) { throw invalid_symbol("Invalid symbol interval."); } // Calculate the new base uint64 h, l; std::tie(h, l) = mul128(symbol.base, range); std::tie(h, l) = add128(h, l, symbol.base); const uint64 t = h + (l > 0 ? 1 : 0); base += t; // Calculate the new range std::tie(h, l) = mul128(symbol.base + symbol.range, range); std::tie(h, l) = add128(h, l, symbol.base + symbol.range); std::tie(h, l) = add128(h, l, range); std::tie(h, l) = add128(h, l, 1); range = h - t - 1; // Return carry return base < t; }
void attitude_pose_cb(const geometry_msgs::PoseStamped::ConstPtr &pose_msg, const mavros_msgs::Thrust::ConstPtr &thrust_msg) { Eigen::Affine3d tr; tf::poseMsgToEigen(pose_msg->pose, tr); if (is_normalized(thrust_msg->thrust)) send_attitude_quaternion(pose_msg->header.stamp, tr, thrust_msg->thrust); }
DirectLightingIntegrator::DirectLightingIntegrator( const ShadingContext& shading_context, const LightSampler& light_sampler, const ShadingPoint& shading_point, const Vector3d& outgoing, const BSDF& bsdf, const void* bsdf_data, const int bsdf_sampling_modes, const int light_sampling_modes, const size_t bsdf_sample_count, const size_t light_sample_count, const bool indirect) : m_shading_context(shading_context) , m_light_sampler(light_sampler) , m_shading_point(shading_point) , m_point(shading_point.get_point()) , m_geometric_normal(shading_point.get_geometric_normal()) , m_shading_basis(shading_point.get_shading_basis()) , m_time(shading_point.get_time()) , m_outgoing(outgoing) , m_bsdf(bsdf) , m_bsdf_data(bsdf_data) , m_bsdf_sampling_modes(bsdf_sampling_modes) , m_light_sampling_modes(light_sampling_modes) , m_bsdf_sample_count(bsdf_sample_count) , m_light_sample_count(light_sample_count) , m_indirect(indirect) { assert(is_normalized(outgoing)); }
bool is_normalized ( path_array const& pathname ) { DecoderT<path_array::object_type> decoder(pathname); return is_normalized(decoder); }
Scanline::Scanline(const vector3& origin, const vector3& direction, const vector3& lateral_dir, float timestamp) : origin(origin), direction(direction), lateral_dir(lateral_dir), timestamp(timestamp) { elevational_dir = lateral_dir.cross(direction); // TODO: Should all vectors be normalized here? if (!is_orthogonal()) throw std::runtime_error("Invalid Scanline: Not orthogonal unit vectors"); if (!is_normalized()) throw std::runtime_error("Invalid Scanline: Not unit length vectors"); }
void compute_ibl( 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 bsdf_sampling_modes, const int env_sampling_modes, const size_t bsdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing)); // Compute IBL by sampling the BSDF. compute_ibl_bsdf_sampling( sampling_context, shading_context, environment_edf, shading_point, outgoing, bsdf, bsdf_data, bsdf_sampling_modes, bsdf_sample_count, env_sample_count, radiance); // Compute IBL by sampling the environment. Spectrum radiance_env_sampling; compute_ibl_environment_sampling( sampling_context, shading_context, environment_edf, shading_point, outgoing, bsdf, bsdf_data, env_sampling_modes, bsdf_sample_count, env_sample_count, radiance_env_sampling); radiance += radiance_env_sampling; }
void compute_ibl( 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())); // Compute IBL by sampling the BSSRDF. compute_ibl_bssrdf_sampling( sampling_context, shading_context, environment_edf, bssrdf, bssrdf_data, incoming_point, outgoing_point, outgoing, bssrdf_sample_count, env_sample_count, radiance); // Compute IBL by sampling the environment. Spectrum radiance_env_sampling; compute_ibl_environment_sampling( sampling_context, shading_context, environment_edf, bssrdf, bssrdf_data, incoming_point, outgoing_point, outgoing, bssrdf_sample_count, env_sample_count, radiance_env_sampling); radiance += radiance_env_sampling; }
Interval::uint64 CodeInterval::descale(Interval::uint64 value) const throw(not_normalized, range_error) { if(!is_normalized()) { throw not_normalized("Interval must be normalized before descale."); } if(!includes(value)) { throw range_error("Value not in interval."); } // value = (value - base) · 2⁶⁴ / (range + 1) if(range == max) { return value - base; } else { assert(value - base < range + 1); std::uint64_t q, r; std::tie(q, r) = div128(value - base, 0, range + 1); q += (r >= msb) ? 1 : 0; return q; } }
path_t &operator+=(const char *path) { if (!path || *path == '\0') return *this; char *ns = (char *)::malloc(length() + ::strlen(path) + 2); ::strcpy(ns, p.get()); ::strcat(ns, "/"); ::strcat(ns, path); if (is_normalized(ns)) reset(ns); else { reset(normalize(ns)); ::free((void*)ns); } return *this; }
bool quat_is_normalized_with_eps(const Quaternion<T>& q, const T eps) { return is_normalized(q, eps); }
bool quat_is_normalized(const Quaternion<T>& q) { return is_normalized(q); }
void compute_ibl_bsdf_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 bsdf_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); for (size_t i = 0; i < bsdf_sample_count; ++i) { // Sample the BSDF. // todo: rendering will be incorrect if the BSDF value returned by the sample() method // includes the contribution of a specular component since these are explicitly rejected // afterward. We need a mechanism to indicate that we want the contribution of some of // the components only. Vector3d incoming; Spectrum bsdf_value; double bsdf_prob; const BSDF::Mode bsdf_mode = bsdf.sample( sampling_context, bsdf_data, false, // not adjoint true, // multiply by |cos(incoming, normal)| geometric_normal, shading_basis, outgoing, incoming, bsdf_value, bsdf_prob); // Filter scattering modes. if (!(bsdf_sampling_modes & bsdf_mode)) return; // Discard occluded samples. const double transmission = shading_context.get_tracer().trace( shading_point, incoming, ShadingRay::ShadowRay); if (transmission == 0.0) continue; // Evaluate the environment's EDF. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Spectrum env_value; double env_prob; environment_edf.evaluate( input_evaluator, incoming, env_value, env_prob); // Apply all weights, including MIS weight. if (bsdf_mode == BSDF::Specular) env_value *= static_cast<float>(transmission); else { const double mis_weight = mis_power2( bsdf_sample_count * bsdf_prob, env_sample_count * env_prob); env_value *= static_cast<float>(transmission / bsdf_prob * mis_weight); } // Add the contribution of this sample to the illumination. env_value *= bsdf_value; radiance += env_value; } if (bsdf_sample_count > 1) radiance /= static_cast<float>(bsdf_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 Scanline::is_valid() const { return (is_orthogonal() && is_normalized()); }
void ShadingPoint::fetch_source_geometry() const { assert(hit()); assert(!(m_members & HasSourceGeometry)); // Retrieve the assembly. m_assembly = &m_assembly_instance->get_assembly(); // Retrieve the object instance. m_object_instance = m_assembly->object_instances().get_by_index(m_object_instance_index); assert(m_object_instance); // Retrieve the object. m_object = &m_object_instance->get_object(); // Retrieve the region kit of the object. assert(m_region_kit_cache); const RegionKit& region_kit = *m_region_kit_cache->access( m_object->get_uid(), m_object->get_region_kit()); // Retrieve the region. const IRegion* region = region_kit[m_region_index]; // Retrieve the tessellation of the region. assert(m_tess_cache); const StaticTriangleTess& tess = *m_tess_cache->access( region->get_uid(), region->get_static_triangle_tess()); const size_t motion_segment_count = tess.get_motion_segment_count(); // Retrieve the triangle. const Triangle& triangle = tess.m_primitives[m_triangle_index]; // Copy the index of the triangle attribute. m_triangle_pa = triangle.m_pa; // Copy the texture coordinates from UV set #0. if (triangle.has_vertex_attributes() && tess.get_uv_vertex_count() > 0) { m_v0_uv = tess.get_uv_vertex(triangle.m_a0); m_v1_uv = tess.get_uv_vertex(triangle.m_a1); m_v2_uv = tess.get_uv_vertex(triangle.m_a2); } else { // UV set #0 doesn't exist, or this triangle doesn't have vertex attributes. m_v0_uv = m_v1_uv = m_v2_uv = GVector2(0.0); } // Copy the object instance space triangle vertices. assert(triangle.m_v0 != Triangle::None); assert(triangle.m_v1 != Triangle::None); assert(triangle.m_v2 != Triangle::None); if (motion_segment_count > 0) { // Fetch triangle vertices from the previous pose. const size_t prev_index = truncate<size_t>(m_ray.m_time * motion_segment_count); GVector3 prev_v0, prev_v1, prev_v2; if (prev_index == 0) { prev_v0 = tess.m_vertices[triangle.m_v0]; prev_v1 = tess.m_vertices[triangle.m_v1]; prev_v2 = tess.m_vertices[triangle.m_v2]; } else { prev_v0 = tess.get_vertex_pose(triangle.m_v0, prev_index - 1); prev_v1 = tess.get_vertex_pose(triangle.m_v1, prev_index - 1); prev_v2 = tess.get_vertex_pose(triangle.m_v2, prev_index - 1); } // Fetch triangle vertices from the next pose. const GVector3 next_v0 = tess.get_vertex_pose(triangle.m_v0, prev_index); const GVector3 next_v1 = tess.get_vertex_pose(triangle.m_v1, prev_index); const GVector3 next_v2 = tess.get_vertex_pose(triangle.m_v2, prev_index); // Interpolate triangle vertices. const GScalar k = static_cast<GScalar>(m_ray.m_time * motion_segment_count - prev_index); m_v0 = (GScalar(1.0) - k) * prev_v0 + k * next_v0; m_v1 = (GScalar(1.0) - k) * prev_v1 + k * next_v1; m_v2 = (GScalar(1.0) - k) * prev_v2 + k * next_v2; } else { m_v0 = tess.m_vertices[triangle.m_v0]; m_v1 = tess.m_vertices[triangle.m_v1]; m_v2 = tess.m_vertices[triangle.m_v2]; } // Copy the object instance space triangle vertex normals. assert(triangle.m_n0 != Triangle::None); assert(triangle.m_n1 != Triangle::None); assert(triangle.m_n2 != Triangle::None); m_n0 = tess.m_vertex_normals[triangle.m_n0]; m_n1 = tess.m_vertex_normals[triangle.m_n1]; m_n2 = tess.m_vertex_normals[triangle.m_n2]; assert(is_normalized(m_n0)); assert(is_normalized(m_n1)); assert(is_normalized(m_n2)); }
void LightSampler::collect_emitting_triangles( const Assembly& assembly, const AssemblyInstance& assembly_instance, const TransformSequence& transform_sequence) { // Loop over the object instances of the assembly. const size_t object_instance_count = assembly.object_instances().size(); for (size_t object_instance_index = 0; object_instance_index < object_instance_count; ++object_instance_index) { // Retrieve the object instance. const ObjectInstance* object_instance = assembly.object_instances().get_by_index(object_instance_index); // Retrieve the materials of the object instance. const MaterialArray& front_materials = object_instance->get_front_materials(); const MaterialArray& back_materials = object_instance->get_back_materials(); // Skip object instances without light-emitting materials. if (!has_emitting_materials(front_materials) && !has_emitting_materials(back_materials)) continue; double object_area = 0.0; // Compute the object space to world space transformation. // todo: add support for moving light-emitters. const Transformd& object_instance_transform = object_instance->get_transform(); const Transformd& assembly_instance_transform = transform_sequence.get_earliest_transform(); const Transformd global_transform = assembly_instance_transform * object_instance_transform; // Retrieve the object. Object& object = object_instance->get_object(); // Retrieve the region kit of the object. Access<RegionKit> region_kit(&object.get_region_kit()); // Loop over the regions of the object. const size_t region_count = region_kit->size(); for (size_t region_index = 0; region_index < region_count; ++region_index) { // Retrieve the region. const IRegion* region = (*region_kit)[region_index]; // Retrieve the tessellation of the region. Access<StaticTriangleTess> tess(®ion->get_static_triangle_tess()); // Loop over the triangles of the region. const size_t triangle_count = tess->m_primitives.size(); for (size_t triangle_index = 0; triangle_index < triangle_count; ++triangle_index) { // Fetch the triangle. const Triangle& triangle = tess->m_primitives[triangle_index]; // Skip triangles without a material. if (triangle.m_pa == Triangle::None) continue; // Fetch the materials assigned to this triangle. const size_t pa_index = static_cast<size_t>(triangle.m_pa); const Material* front_material = pa_index < front_materials.size() ? front_materials[pa_index] : 0; const Material* back_material = pa_index < back_materials.size() ? back_materials[pa_index] : 0; // Skip triangles that don't emit light. if ((front_material == 0 || front_material->has_emission() == false) && (back_material == 0 || back_material->has_emission() == false)) continue; // Retrieve object instance space vertices of the triangle. const GVector3& v0_os = tess->m_vertices[triangle.m_v0]; const GVector3& v1_os = tess->m_vertices[triangle.m_v1]; const GVector3& v2_os = tess->m_vertices[triangle.m_v2]; // Transform triangle vertices to assembly space. const GVector3 v0_as = object_instance_transform.point_to_parent(v0_os); const GVector3 v1_as = object_instance_transform.point_to_parent(v1_os); const GVector3 v2_as = object_instance_transform.point_to_parent(v2_os); // Compute the support plane of the hit triangle in assembly space. const GTriangleType triangle_geometry(v0_as, v1_as, v2_as); TriangleSupportPlaneType triangle_support_plane; triangle_support_plane.initialize(TriangleType(triangle_geometry)); // Transform triangle vertices to world space. const Vector3d v0(assembly_instance_transform.point_to_parent(v0_as)); const Vector3d v1(assembly_instance_transform.point_to_parent(v1_as)); const Vector3d v2(assembly_instance_transform.point_to_parent(v2_as)); // Compute the geometric normal to the triangle and the area of the triangle. Vector3d geometric_normal = cross(v1 - v0, v2 - v0); const double geometric_normal_norm = norm(geometric_normal); if (geometric_normal_norm == 0.0) continue; const double rcp_geometric_normal_norm = 1.0 / geometric_normal_norm; const double rcp_area = 2.0 * rcp_geometric_normal_norm; const double area = 0.5 * geometric_normal_norm; geometric_normal *= rcp_geometric_normal_norm; assert(is_normalized(geometric_normal)); // Retrieve object instance space vertex normals. Vector3d n0_os, n1_os, n2_os; if (triangle.m_n0 != Triangle::None && triangle.m_n1 != Triangle::None && triangle.m_n2 != Triangle::None) { n0_os = Vector3d(tess->m_vertex_normals[triangle.m_n0]); n1_os = Vector3d(tess->m_vertex_normals[triangle.m_n1]); n2_os = Vector3d(tess->m_vertex_normals[triangle.m_n2]); } else n0_os = n1_os = n2_os = geometric_normal; // Transform vertex normals to world space. const Vector3d n0(normalize(global_transform.normal_to_parent(n0_os))); const Vector3d n1(normalize(global_transform.normal_to_parent(n1_os))); const Vector3d n2(normalize(global_transform.normal_to_parent(n2_os))); for (size_t side = 0; side < 2; ++side) { // Retrieve the material; skip sides without a material or without emission. const Material* material = side == 0 ? front_material : back_material; if (material == 0 || material->has_emission() == false) continue; // Retrieve the EDF and get the importance multiplier. double importance_multiplier = 1.0; if (const EDF* edf = material->get_uncached_edf()) importance_multiplier = edf->get_uncached_importance_multiplier(); // Accumulate the object area for OSL shaders. object_area += area; // Compute the probability density of this triangle. const double triangle_importance = m_params.m_importance_sampling ? area : 1.0; const double triangle_prob = triangle_importance * importance_multiplier; // Create a light-emitting triangle. EmittingTriangle emitting_triangle; emitting_triangle.m_assembly_instance = &assembly_instance; emitting_triangle.m_object_instance_index = object_instance_index; emitting_triangle.m_region_index = region_index; emitting_triangle.m_triangle_index = triangle_index; emitting_triangle.m_v0 = v0; emitting_triangle.m_v1 = v1; emitting_triangle.m_v2 = v2; emitting_triangle.m_n0 = side == 0 ? n0 : -n0; emitting_triangle.m_n1 = side == 0 ? n1 : -n1; emitting_triangle.m_n2 = side == 0 ? n2 : -n2; emitting_triangle.m_geometric_normal = side == 0 ? geometric_normal : -geometric_normal; emitting_triangle.m_triangle_support_plane = triangle_support_plane; emitting_triangle.m_rcp_area = rcp_area; emitting_triangle.m_triangle_prob = 0.0; // will be initialized once the emitting triangle CDF is built emitting_triangle.m_material = material; // Store the light-emitting triangle. const size_t emitting_triangle_index = m_emitting_triangles.size(); m_emitting_triangles.push_back(emitting_triangle); // Insert the light-emitting triangle into the CDF. m_emitting_triangles_cdf.insert(emitting_triangle_index, triangle_prob); } } } #ifdef APPLESEED_WITH_OSL store_object_area_in_shadergroups( &assembly_instance, object_instance, object_area, front_materials); store_object_area_in_shadergroups( &assembly_instance, object_instance, object_area, back_materials); #endif } }
const ShadingPoint& Tracer::do_trace( const Vector3d& origin, const Vector3d& direction, const ShadingRay::Time& ray_time, const VisibilityFlags::Type ray_flags, const ShadingRay::DepthType ray_depth, float& transmission, const ShadingPoint* parent_shading_point) { assert(is_normalized(direction)); transmission = 1.0f; const ShadingPoint* shading_point_ptr = parent_shading_point; size_t shading_point_index = 0; Vector3d point = origin; size_t iterations = 0; while (true) { // Put a hard limit on the number of iterations. if (++iterations >= m_max_iterations) { RENDERER_LOG_WARNING( "reached hard iteration limit (%s), breaking trace loop.", pretty_int(m_max_iterations).c_str()); break; } // Construct the visibility ray. const ShadingRay ray( point, direction, ray_time, ray_flags, ray_depth); // ray depth does not increase when passing through an alpha-mapped surface // Trace the ray. m_shading_points[shading_point_index].clear(); m_intersector.trace( ray, m_shading_points[shading_point_index], shading_point_ptr); // Update the pointers to the shading points. shading_point_ptr = &m_shading_points[shading_point_index]; shading_point_index = 1 - shading_point_index; // Stop if the ray escaped the scene. if (!shading_point_ptr->hit()) break; // Retrieve the material at the shading point. const Material* material = shading_point_ptr->get_material(); if (material == 0) break; Alpha alpha; evaluate_alpha(*material, *shading_point_ptr, alpha); // Stop at the first fully opaque occluder. if (alpha[0] >= 1.0f) break; // Update the transmission factor. transmission *= 1.0f - alpha[0]; // Stop once we hit full opacity. if (transmission < m_transmission_threshold) break; // Move past this partial occluder. point = shading_point_ptr->get_point(); } return *shading_point_ptr; }
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); }
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 LightSampler::collect_emitting_triangles( const Assembly& assembly, const AssemblyInstance& assembly_instance) { // Loop over the object instances of the assembly. const size_t object_instance_count = assembly.object_instances().size(); for (size_t object_instance_index = 0; object_instance_index < object_instance_count; ++object_instance_index) { // Retrieve the object instance. const ObjectInstance* object_instance = assembly.object_instances().get_by_index(object_instance_index); // Retrieve the materials of the object instance. const MaterialArray& front_materials = object_instance->get_front_materials(); const MaterialArray& back_materials = object_instance->get_back_materials(); // Skip object instances without light-emitting materials. if (!has_emitting_materials(front_materials) && !has_emitting_materials(back_materials)) continue; // Compute the object space to world space transformation. // todo: add support for moving light-emitters. const Transformd& object_instance_transform = object_instance->get_transform(); const Transformd assembly_instance_transform = assembly_instance.transform_sequence().empty() ? Transformd::identity() : assembly_instance.transform_sequence().earliest_transform(); const Transformd global_transform = assembly_instance_transform * object_instance_transform; // Retrieve the object. Object& object = object_instance->get_object(); // Retrieve the region kit of the object. Access<RegionKit> region_kit(&object.get_region_kit()); // Loop over the regions of the object. const size_t region_count = region_kit->size(); for (size_t region_index = 0; region_index < region_count; ++region_index) { // Retrieve the region. const IRegion* region = (*region_kit)[region_index]; // Retrieve the tessellation of the region. Access<StaticTriangleTess> tess(®ion->get_static_triangle_tess()); // Loop over the triangles of the region. const size_t triangle_count = tess->m_primitives.size(); for (size_t triangle_index = 0; triangle_index < triangle_count; ++triangle_index) { // Fetch the triangle. const Triangle& triangle = tess->m_primitives[triangle_index]; // Skip triangles without a material. if (triangle.m_pa == Triangle::None) continue; // Fetch the materials assigned to this triangle. const size_t pa_index = static_cast<size_t>(triangle.m_pa); const Material* front_material = pa_index < front_materials.size() ? front_materials[pa_index] : 0; const Material* back_material = pa_index < back_materials.size() ? back_materials[pa_index] : 0; // Skip triangles that don't emit light. if ((front_material == 0 || front_material->get_uncached_edf() == 0) && (back_material == 0 || back_material->get_uncached_edf() == 0)) continue; // Retrieve object instance space vertices of the triangle. const GVector3& v0_os = tess->m_vertices[triangle.m_v0]; const GVector3& v1_os = tess->m_vertices[triangle.m_v1]; const GVector3& v2_os = tess->m_vertices[triangle.m_v2]; // Transform triangle vertices to assembly space. const GVector3 v0_as = object_instance_transform.point_to_parent(v0_os); const GVector3 v1_as = object_instance_transform.point_to_parent(v1_os); const GVector3 v2_as = object_instance_transform.point_to_parent(v2_os); // Compute the support plane of the hit triangle in assembly space. const GTriangleType triangle_geometry(v0_as, v1_as, v2_as); TriangleSupportPlaneType triangle_support_plane; triangle_support_plane.initialize(TriangleType(triangle_geometry)); // Transform triangle vertices to world space. const Vector3d v0(assembly_instance_transform.point_to_parent(v0_as)); const Vector3d v1(assembly_instance_transform.point_to_parent(v1_as)); const Vector3d v2(assembly_instance_transform.point_to_parent(v2_as)); // Compute the geometric normal to the triangle and the area of the triangle. Vector3d geometric_normal = cross(v1 - v0, v2 - v0); const double geometric_normal_norm = norm(geometric_normal); if (geometric_normal_norm == 0.0) continue; const double rcp_geometric_normal_norm = 1.0 / geometric_normal_norm; const double rcp_area = 2.0 * rcp_geometric_normal_norm; const double area = 0.5 * geometric_normal_norm; geometric_normal *= rcp_geometric_normal_norm; assert(is_normalized(geometric_normal)); // Retrieve object instance space vertex normals. const GVector3& n0_os = tess->m_vertex_normals[triangle.m_n0]; const GVector3& n1_os = tess->m_vertex_normals[triangle.m_n1]; const GVector3& n2_os = tess->m_vertex_normals[triangle.m_n2]; // Transform vertex normals to world space. const Vector3d n0(normalize(global_transform.normal_to_parent(n0_os))); const Vector3d n1(normalize(global_transform.normal_to_parent(n1_os))); const Vector3d n2(normalize(global_transform.normal_to_parent(n2_os))); for (size_t side = 0; side < 2; ++side) { const Material* material = side == 0 ? front_material : back_material; const Vector3d side_geometric_normal = side == 0 ? geometric_normal : -geometric_normal; const Vector3d side_n0 = side == 0 ? n0 : -n0; const Vector3d side_n1 = side == 0 ? n1 : -n1; const Vector3d side_n2 = side == 0 ? n2 : -n2; // Skip sides without a material. if (material == 0) continue; const EDF* edf = material->get_uncached_edf(); // Skip sides without a light-emitting material. if (edf == 0) continue; // Create a light-emitting triangle. EmittingTriangle emitting_triangle; emitting_triangle.m_assembly_instance = &assembly_instance; emitting_triangle.m_object_instance_index = object_instance_index; emitting_triangle.m_region_index = region_index; emitting_triangle.m_triangle_index = triangle_index; emitting_triangle.m_v0 = v0; emitting_triangle.m_v1 = v1; emitting_triangle.m_v2 = v2; emitting_triangle.m_n0 = side_n0; emitting_triangle.m_n1 = side_n1; emitting_triangle.m_n2 = side_n2; emitting_triangle.m_geometric_normal = side_geometric_normal; emitting_triangle.m_triangle_support_plane = triangle_support_plane; emitting_triangle.m_rcp_area = rcp_area; emitting_triangle.m_edf = edf; // Store the light-emitting triangle. const size_t emitting_triangle_index = m_emitting_triangles.size(); m_emitting_triangles.push_back(emitting_triangle); // Insert the light-emitting triangle into the CDFs. m_emitter_cdf.insert(emitting_triangle_index + m_lights.size(), area); m_emitting_triangle_cdf.insert(emitting_triangle_index, area); // Keep track of the total area of the light-emitting triangles. m_total_emissive_area += area; } } } } }
void compute_ibl_bsdf_sampling( SamplingContext& sampling_context, const ShadingContext& shading_context, const EnvironmentEDF& environment_edf, const ShadingPoint& shading_point, const Dual3d& outgoing, const BSDF& bsdf, const void* bsdf_data, const int bsdf_sampling_modes, const size_t bsdf_sample_count, const size_t env_sample_count, Spectrum& radiance) { assert(is_normalized(outgoing.get_value())); radiance.set(0.0f); for (size_t i = 0; i < bsdf_sample_count; ++i) { // Sample the BSDF. // todo: rendering will be incorrect if the BSDF value returned by the sample() method // includes the contribution of a specular component since these are explicitly rejected // afterward. We need a mechanism to indicate that we want the contribution of some of // the components only. BSDFSample sample(shading_point, outgoing); bsdf.sample( sampling_context, bsdf_data, false, // not adjoint true, // multiply by |cos(incoming, normal)| sample); // Filter scattering modes. if (!(bsdf_sampling_modes & sample.m_mode)) continue; // Discard occluded samples. const float transmission = shading_context.get_tracer().trace( shading_point, Vector3d(sample.m_incoming.get_value()), VisibilityFlags::ShadowRay); if (transmission == 0.0f) continue; // Evaluate the environment's EDF. InputEvaluator input_evaluator(shading_context.get_texture_cache()); Spectrum env_value; float env_prob; environment_edf.evaluate( shading_context, input_evaluator, sample.m_incoming.get_value(), env_value, env_prob); // Apply all weights, including MIS weight. if (sample.m_mode == ScatteringMode::Specular) env_value *= transmission; else { const float mis_weight = mis_power2( bsdf_sample_count * sample.m_probability, env_sample_count * env_prob); env_value *= transmission / sample.m_probability * mis_weight; } // Add the contribution of this sample to the illumination. env_value *= sample.m_value; radiance += env_value; } if (bsdf_sample_count > 1) radiance /= static_cast<float>(bsdf_sample_count); }