diff --git a/src/integrators/restir_di.cpp b/src/integrators/restir_di.cpp index b75547c5..ace4835b 100644 --- a/src/integrators/restir_di.cpp +++ b/src/integrators/restir_di.cpp @@ -96,9 +96,15 @@ class ReSTIRDirectLightingInstance final : public ProgressiveIntegrator::Instanc const SampledWavelengths &swl, Expr time) const noexcept { auto L = SampledSpectrum{swl.dimension(), 0.f}; auto pdf = def(0.f); - auto prob = light_sampler()->evaluate_selection(sample.tag, it.p(), swl, time); + auto prob = def(0.f); + $outline { + prob = light_sampler()->evaluate_selection(sample.tag, it.p(), swl, time); + }; auto sel = LightSampler::Selection{sample.tag, prob}; - auto light_sample = light_sampler()->sample_light(it, sel, sample.u_light_surface, swl, time); + auto light_sample = LightSampler::Sample::zero(swl.dimension()); + $outline { + light_sample = light_sampler()->sample_light(it, sel, sample.u_light_surface, swl, time); + }; auto surface_tag = it.shape().surface_tag(); PolymorphicCall call; pipeline().surfaces().dispatch(surface_tag, [&](auto surface) noexcept { @@ -118,9 +124,15 @@ class ReSTIRDirectLightingInstance final : public ProgressiveIntegrator::Instanc const SampledWavelengths &swl, Expr time) const noexcept { auto L = SampledSpectrum{swl.dimension(), 0.f}; auto pdf = def(0.f); - auto prob = light_sampler()->evaluate_selection(sample.tag, it.p(), swl, time); + auto prob = def(0.f); + $outline { + prob = light_sampler()->evaluate_selection(sample.tag, it.p(), swl, time); + }; auto sel = LightSampler::Selection{sample.tag, prob}; - auto light_sample = light_sampler()->sample_light(it, sel, sample.u_light_surface, swl, time); + auto light_sample = LightSampler::Sample::zero(swl.dimension()); + $outline { + light_sample = light_sampler()->sample_light(it, sel, sample.u_light_surface, swl, time); + }; auto occluded = pipeline().geometry()->intersect_any(light_sample.shadow_ray); auto surface_tag = it.shape().surface_tag(); PolymorphicCall call; @@ -232,48 +244,48 @@ class ReSTIRDirectLightingInstance final : public ProgressiveIntegrator::Instanc // miss $if(!it->valid() | !it->shape().has_surface()) { $break; }; // RIS over light samples - $outline { - $for(_, num_initial_sample) { - auto u_sel = sampler()->generate_1d(); - auto sel = light_sampler()->select(*it, u_sel, swl, time); - auto u_light_selection = sampler()->generate_2d(); - auto target_pdf = def(0.f), total_weight = def(0.f); - Reservoir candidate{ - ReservoirSample{sel.tag, u_light_selection}, - ReservoirWeight{1.f, total_weight, target_pdf}}; - auto [L, pdf] = _evaluate_without_occlusion(candidate.sample, *it, wo, swl, time); - candidate.weight.target_pdf = pipeline().spectrum()->cie_y(swl, L); - candidate.weight.total_weight = ite(pdf == 0.f, 0.f, candidate.weight.target_pdf / pdf); - reservoir.update(candidate, sampler()->generate_1d()); + $for(_, num_initial_sample) { + LightSampler::Selection sel; + auto u_sel = sampler()->generate_1d(); + $outline { + sel = light_sampler()->select(*it, u_sel, swl, time); }; + auto u_light_selection = sampler()->generate_2d(); + auto target_pdf = def(0.f), total_weight = def(0.f); + Reservoir candidate{ + ReservoirSample{sel.tag, u_light_selection}, + ReservoirWeight{1.f, total_weight, target_pdf}}; + auto [L, pdf] = _evaluate_without_occlusion(candidate.sample, *it, wo, swl, time); + candidate.weight.target_pdf = pipeline().spectrum()->cie_y(swl, L); + candidate.weight.total_weight = ite(pdf == 0.f, 0.f, candidate.weight.target_pdf / pdf); + reservoir.update(candidate, sampler()->generate_1d()); }; // visibility reuse $if(enable_visibility_reuse) { + auto light_sample = LightSampler::Sample::zero(swl.dimension()); $outline { - auto light_sample = light_sampler()->sample_light(*it, {reservoir.sample.tag, 1.f}, reservoir.sample.u_light_surface, swl, time); - auto occluded = pipeline().geometry()->intersect_any(light_sample.shadow_ray); - $if(light_sample.eval.pdf == 0.f | occluded) { - reservoir.weight.total_weight = 0.f; - }; + light_sample = light_sampler()->sample_light(*it, {reservoir.sample.tag, 1.f}, reservoir.sample.u_light_surface, swl, time); + }; + auto occluded = pipeline().geometry()->intersect_any(light_sample.shadow_ray); + $if(light_sample.eval.pdf == 0.f | occluded) { + reservoir.weight.total_weight = 0.f; }; }; // temporal reuse $if(enable_temporal_reuse & frame_index != 0u) { - $outline { - auto prev_frame_view_matrix = _prev_frame_view_matrix->read(0u); - auto p_view = make_float3(prev_frame_view_matrix * make_float4(it->p(), 1.f)); - auto [prev_frame_pixel_id, valid] = camera->project(p_view); - prev_frame_pixel_id = clamp(prev_frame_pixel_id, 0.f, make_float2(resolution) - 1.f); - $if(valid) { - auto prev_frame_reservoir = _temporal_reservoir_buffer->read(make_uint2(prev_frame_pixel_id)); - $if(!dsl::isnan(prev_frame_reservoir.weight.total_weight) & !dsl::isnan(prev_frame_reservoir.weight.target_pdf) & prev_frame_reservoir.weight.target_pdf > 0.f) { - auto [L, pdf] = _evaluate_without_occlusion(prev_frame_reservoir.sample, *it, wo, swl, time); - auto target_pdf = pipeline().spectrum()->cie_y(swl, L); - prev_frame_reservoir.weight.total_weight *= target_pdf / prev_frame_reservoir.weight.target_pdf; - prev_frame_reservoir.weight.target_pdf = target_pdf; - prev_frame_reservoir.weight.m = min(prev_frame_reservoir.weight.m, 20.f * reservoir.weight.m); - reservoir.update(prev_frame_reservoir, sampler()->generate_1d()); - }; + auto prev_frame_view_matrix = _prev_frame_view_matrix->read(0u); + auto p_view = make_float3(prev_frame_view_matrix * make_float4(it->p(), 1.f)); + auto [prev_frame_pixel_id, valid] = camera->project(p_view); + prev_frame_pixel_id = clamp(prev_frame_pixel_id, 0.f, make_float2(resolution) - 1.f); + $if(valid) { + auto prev_frame_reservoir = _temporal_reservoir_buffer->read(make_uint2(prev_frame_pixel_id)); + $if(!dsl::isnan(prev_frame_reservoir.weight.total_weight) & !dsl::isnan(prev_frame_reservoir.weight.target_pdf) & prev_frame_reservoir.weight.target_pdf > 0.f) { + auto [L, pdf] = _evaluate_without_occlusion(prev_frame_reservoir.sample, *it, wo, swl, time); + auto target_pdf = pipeline().spectrum()->cie_y(swl, L); + prev_frame_reservoir.weight.total_weight *= target_pdf / prev_frame_reservoir.weight.target_pdf; + prev_frame_reservoir.weight.target_pdf = target_pdf; + prev_frame_reservoir.weight.m = min(prev_frame_reservoir.weight.m, 20.f * reservoir.weight.m); + reservoir.update(prev_frame_reservoir, sampler()->generate_1d()); }; }; }; @@ -309,12 +321,13 @@ class ReSTIRDirectLightingInstance final : public ProgressiveIntegrator::Instanc }; // visibility reuse $if(enable_visibility_reuse) { + auto light_sample = LightSampler::Sample::zero(swl.dimension()); $outline { - auto light_sample = light_sampler()->sample_light(*it, {reservoir.sample.tag, 1.f}, reservoir.sample.u_light_surface, swl, time); - auto occluded = pipeline().geometry()->intersect_any(light_sample.shadow_ray); - $if(light_sample.eval.pdf == 0.f | occluded) { - reservoir.weight.total_weight = 0.f; - }; + light_sample = light_sampler()->sample_light(*it, {reservoir.sample.tag, 1.f}, reservoir.sample.u_light_surface, swl, time); + }; + auto occluded = pipeline().geometry()->intersect_any(light_sample.shadow_ray); + $if(light_sample.eval.pdf == 0.f | occluded) { + reservoir.weight.total_weight = 0.f; }; }; $break; @@ -341,59 +354,57 @@ class ReSTIRDirectLightingInstance final : public ProgressiveIntegrator::Instanc // miss $if(!it->valid() | !it->shape().has_surface()) { $break; }; // spatial reuse - $outline { - ArrayVar valid_neighbor_ray_array; - ArrayVar valid_neighbor_hit_array; - ArrayFloat<3u> valid_neighbor_m_array; - auto num_valid_neighbor = def(0u); - auto z = reservoir.weight.m; - auto depth_projector = inverse(camera->camera_to_world())[2]; - auto current_pixel_depth = dot(depth_projector.xyz(), it->p()) + depth_projector.w; - $for(_, num_neighbor_sample) { - auto u_radius = sampler()->generate_1d(), u_theta = sampler()->generate_1d(); - auto radius = neighbor_radius * sqrt(u_radius); - auto theta = 2.f * pi * u_theta; - auto offset = make_float2(radius * cos(theta), radius * sin(theta)); - auto neighbor_id = make_uint2(clamp(make_float2(pixel_id) + offset, make_float2(0.f), make_float2(resolution) - 1.f)); - auto neighbor_ray = _visibility_buffer->ray(neighbor_id); - auto neighbor_hit = _visibility_buffer->hit(neighbor_id); - auto neighbor_it = pipeline().geometry()->interaction(neighbor_ray, neighbor_hit); - $if(neighbor_it->valid() & neighbor_it->shape().has_surface()) { - auto neighbor_pixel_depth = dot(depth_projector.xyz(), neighbor_it->p()) + depth_projector.w; - $if(abs(neighbor_pixel_depth - current_pixel_depth) < 0.05f * abs(current_pixel_depth) & - dot(it->ng(), neighbor_it->ng()) > 0.91f) { - auto neighbor_reservoir = Reservoir::zero(); - neighbor_reservoir = _spatial_reservoir_buffer->read(neighbor_id); - $if(dsl::isnan(neighbor_reservoir.weight.total_weight) | dsl::isnan(neighbor_reservoir.weight.target_pdf)) { $continue; }; - auto [L, pdf] = _evaluate_without_occlusion(neighbor_reservoir.sample, *it, wo, swl, time); - $if(neighbor_reservoir.weight.target_pdf > 0.f) { - auto neighbor_target_pdf = pipeline().spectrum()->cie_y(swl, L); - neighbor_reservoir.weight.total_weight *= neighbor_target_pdf / neighbor_reservoir.weight.target_pdf; - neighbor_reservoir.weight.target_pdf = neighbor_target_pdf; - reservoir.update(neighbor_reservoir, sampler()->generate_1d()); - $if(unbiased) { - valid_neighbor_ray_array[num_valid_neighbor] = neighbor_ray; - valid_neighbor_hit_array[num_valid_neighbor] = neighbor_hit; - valid_neighbor_m_array[num_valid_neighbor] = neighbor_reservoir.weight.m; - num_valid_neighbor += 1u; - }; + ArrayVar valid_neighbor_ray_array; + ArrayVar valid_neighbor_hit_array; + ArrayFloat<3u> valid_neighbor_m_array; + auto num_valid_neighbor = def(0u); + auto z = reservoir.weight.m; + auto depth_projector = inverse(camera->camera_to_world())[2]; + auto current_pixel_depth = dot(depth_projector.xyz(), it->p()) + depth_projector.w; + $for(_, num_neighbor_sample) { + auto u_radius = sampler()->generate_1d(), u_theta = sampler()->generate_1d(); + auto radius = neighbor_radius * sqrt(u_radius); + auto theta = 2.f * pi * u_theta; + auto offset = make_float2(radius * cos(theta), radius * sin(theta)); + auto neighbor_id = make_uint2(clamp(make_float2(pixel_id) + offset, make_float2(0.f), make_float2(resolution) - 1.f)); + auto neighbor_ray = _visibility_buffer->ray(neighbor_id); + auto neighbor_hit = _visibility_buffer->hit(neighbor_id); + auto neighbor_it = pipeline().geometry()->interaction(neighbor_ray, neighbor_hit); + $if(neighbor_it->valid() & neighbor_it->shape().has_surface()) { + auto neighbor_pixel_depth = dot(depth_projector.xyz(), neighbor_it->p()) + depth_projector.w; + $if(abs(neighbor_pixel_depth - current_pixel_depth) < 0.05f * abs(current_pixel_depth) & + dot(it->ng(), neighbor_it->ng()) > 0.91f) { + auto neighbor_reservoir = Reservoir::zero(); + neighbor_reservoir = _spatial_reservoir_buffer->read(neighbor_id); + $if(dsl::isnan(neighbor_reservoir.weight.total_weight) | dsl::isnan(neighbor_reservoir.weight.target_pdf)) { $continue; }; + auto [L, pdf] = _evaluate_without_occlusion(neighbor_reservoir.sample, *it, wo, swl, time); + $if(neighbor_reservoir.weight.target_pdf > 0.f) { + auto neighbor_target_pdf = pipeline().spectrum()->cie_y(swl, L); + neighbor_reservoir.weight.total_weight *= neighbor_target_pdf / neighbor_reservoir.weight.target_pdf; + neighbor_reservoir.weight.target_pdf = neighbor_target_pdf; + reservoir.update(neighbor_reservoir, sampler()->generate_1d()); + $if(unbiased) { + valid_neighbor_ray_array[num_valid_neighbor] = neighbor_ray; + valid_neighbor_hit_array[num_valid_neighbor] = neighbor_hit; + valid_neighbor_m_array[num_valid_neighbor] = neighbor_reservoir.weight.m; + num_valid_neighbor += 1u; }; }; }; }; - $if(unbiased) { - $for(neighbor_index, num_valid_neighbor) { - auto neighbor_ray = valid_neighbor_ray_array[neighbor_index]; - auto neighbor_hit = valid_neighbor_hit_array[neighbor_index]; - auto neighbor_it = pipeline().geometry()->interaction(neighbor_ray, neighbor_hit); - auto [L, _] = _evaluate_without_occlusion(reservoir.sample, *neighbor_it, -neighbor_ray->direction(), swl, time); - $if(any(L > 0.f)) { - z += valid_neighbor_m_array[neighbor_index]; - }; + }; + $if(unbiased) { + $for(neighbor_index, num_valid_neighbor) { + auto neighbor_ray = valid_neighbor_ray_array[neighbor_index]; + auto neighbor_hit = valid_neighbor_hit_array[neighbor_index]; + auto neighbor_it = pipeline().geometry()->interaction(neighbor_ray, neighbor_hit); + auto [L, _] = _evaluate_without_occlusion(reservoir.sample, *neighbor_it, -neighbor_ray->direction(), swl, time); + $if(any(L > 0.f)) { + z += valid_neighbor_m_array[neighbor_index]; }; - reservoir.weight.total_weight *= reservoir.weight.m / z; - reservoir.weight.m = z; }; + reservoir.weight.total_weight *= reservoir.weight.m / z; + reservoir.weight.m = z; }; $break; }; diff --git a/src/lightsamplers/bvh.cpp b/src/lightsamplers/bvh.cpp index 5692a670..3c591ffc 100644 --- a/src/lightsamplers/bvh.cpp +++ b/src/lightsamplers/bvh.cpp @@ -7,223 +7,225 @@ #include namespace luisa::render { - struct BVHPrimitive { - float x_min{}; - float y_min{}; - float z_min{}; - float x_max{}; - float y_max{}; - float z_max{}; - float power{0.f}; - uint tag{0xffffffff}; - [[nodiscard]] float3 p_min() const noexcept { return make_float3(x_min, y_min, z_min); } - [[nodiscard]] float3 p_max() const noexcept { return make_float3(x_max, y_max, z_max); } - }; - class BVHNode { - public: - float3 p_min; - float3 p_max; - float power; - uint first_primitive_offset; - uint primitive_count; - private: - luisa::unique_ptr _left_child; - luisa::unique_ptr _right_child; - public: - BVHNode(float3 p_min, float3 p_max, float power, uint first_primitive_offset, uint primitive_count) noexcept - : p_min(p_min), p_max(p_max), power(power), first_primitive_offset(first_primitive_offset), primitive_count(primitive_count) {} - BVHNode(luisa::unique_ptr left_child, luisa::unique_ptr right_child) noexcept { - if (!left_child || !right_child) {LUISA_ERROR_WITH_LOCATION("Invalid BVH child node.");} - _left_child = std::move(left_child); - _right_child = std::move(right_child); - p_min = min(_left_child->p_min, _right_child->p_min); - p_max = max(_left_child->p_max, _right_child->p_max); - power = _left_child->power + _right_child->power; - first_primitive_offset = min(_left_child->first_primitive_offset, _right_child->first_primitive_offset); - primitive_count = _left_child->primitive_count + _right_child->primitive_count; +struct BVHPrimitive { + float x_min{}; + float y_min{}; + float z_min{}; + float x_max{}; + float y_max{}; + float z_max{}; + float power{0.f}; + uint tag{0xffffffff}; + [[nodiscard]] float3 p_min() const noexcept { return make_float3(x_min, y_min, z_min); } + [[nodiscard]] float3 p_max() const noexcept { return make_float3(x_max, y_max, z_max); } +}; +class BVHNode { +public: + float3 p_min; + float3 p_max; + float power; + uint first_primitive_offset; + uint primitive_count; + +private: + luisa::unique_ptr _left_child; + luisa::unique_ptr _right_child; + +public: + BVHNode(float3 p_min, float3 p_max, float power, uint first_primitive_offset, uint primitive_count) noexcept + : p_min(p_min), p_max(p_max), power(power), first_primitive_offset(first_primitive_offset), primitive_count(primitive_count) {} + BVHNode(luisa::unique_ptr left_child, luisa::unique_ptr right_child) noexcept { + if (!left_child || !right_child) { LUISA_ERROR_WITH_LOCATION("Invalid BVH child node."); } + _left_child = std::move(left_child); + _right_child = std::move(right_child); + p_min = min(_left_child->p_min, _right_child->p_min); + p_max = max(_left_child->p_max, _right_child->p_max); + power = _left_child->power + _right_child->power; + first_primitive_offset = min(_left_child->first_primitive_offset, _right_child->first_primitive_offset); + primitive_count = _left_child->primitive_count + _right_child->primitive_count; + } + [[nodiscard]] uint size() const noexcept { + return _left_child ? (_left_child->size() + _right_child->size() + 1u) : 1u; + } + [[nodiscard]] bool is_leaf() const noexcept { return !_left_child; } + [[nodiscard]] BVHNode *left_child() const noexcept { return _left_child.get(); } + [[nodiscard]] BVHNode *right_child() const noexcept { return _right_child.get(); } +}; +enum BVHSplitAxis { + X = 0u, + Y = 1u, + Z = 2u +}; +class BVHSplitBin { +private: + float3 _p_min; + float3 _p_max; + float _power; + luisa::list _primitives; + +public: + BVHSplitBin() noexcept { + _p_min = make_float3(std::numeric_limits::max()); + _p_max = make_float3(std::numeric_limits::lowest()); + _power = 0.f; + } + [[nodiscard]] auto p_min() const noexcept { return _p_min; } + [[nodiscard]] auto p_max() const noexcept { return _p_max; } + [[nodiscard]] auto power() const noexcept { return _power; } + [[nodiscard]] auto &primitives() noexcept { return _primitives; } + void update() noexcept { + _p_min = make_float3(std::numeric_limits::max()); + _p_max = make_float3(std::numeric_limits::lowest()); + _power = 0.f; + for (auto const &primitive : _primitives) { + _p_min = min(_p_min, primitive.p_min()); + _p_max = max(_p_max, primitive.p_max()); + _power += primitive.power; } - [[nodiscard]] uint size() const noexcept { - return _left_child ? (_left_child->size() + _right_child->size() + 1u) : 1u; + } +}; +[[nodiscard]] luisa::unique_ptr create_bvh(luisa::list &primitives, uint first_primitive_offset = 0u) noexcept { + auto constexpr MAX_LEAF_CAPACITY = 4u; + auto create_leaf_node = [&]() noexcept { + auto p_min = make_float3(std::numeric_limits::max()), p_max = make_float3(std::numeric_limits::lowest()); + auto power = 0.f; + for (auto const &primitive : primitives) { + p_min = min(p_min, primitive.p_min()); + p_max = max(p_max, primitive.p_max()); + power += primitive.power; } - [[nodiscard]] bool is_leaf() const noexcept { return !_left_child; } - [[nodiscard]] BVHNode *left_child() const noexcept { return _left_child.get(); } - [[nodiscard]] BVHNode *right_child() const noexcept { return _right_child.get(); } + return luisa::make_unique(p_min, p_max, power, first_primitive_offset, primitives.size()); }; - enum BVHSplitAxis { - X = 0u, - Y = 1u, - Z = 2u - }; - class BVHSplitBin { - private: - float3 _p_min; - float3 _p_max; - float _power; - luisa::list _primitives; - public: - BVHSplitBin() noexcept { - _p_min = make_float3(std::numeric_limits::max()); - _p_max = make_float3(std::numeric_limits::lowest()); - _power = 0.f; - } - [[nodiscard]] auto p_min() const noexcept { return _p_min; } - [[nodiscard]] auto p_max() const noexcept { return _p_max; } - [[nodiscard]] auto power() const noexcept { return _power; } - [[nodiscard]] auto &primitives() noexcept { return _primitives; } - void update() noexcept { - _p_min = make_float3(std::numeric_limits::max()); - _p_max = make_float3(std::numeric_limits::lowest()); - _power = 0.f; - for (auto const &primitive : _primitives) { - _p_min = min(_p_min, primitive.p_min()); - _p_max = max(_p_max, primitive.p_max()); - _power += primitive.power; - } + if (primitives.size() <= MAX_LEAF_CAPACITY) { + return create_leaf_node(); + } + auto constexpr NUM_BINS = 12u; + auto constexpr NUM_SPLITS = NUM_BINS - 1u; + std::array bins; + auto constexpr compute_split_axis = [](auto extent) noexcept { + if (extent.x > extent.y) { + return extent.x > extent.z ? BVHSplitAxis::X : BVHSplitAxis::Z; + } else { + return extent.y > extent.z ? BVHSplitAxis::Y : BVHSplitAxis::Z; } }; - [[nodiscard]] luisa::unique_ptr create_bvh(luisa::list &primitives, uint first_primitive_offset = 0u) noexcept { - auto constexpr MAX_LEAF_CAPACITY = 4u; - auto create_leaf_node = [&]() noexcept { - auto p_min = make_float3(std::numeric_limits::max()), p_max = make_float3(std::numeric_limits::lowest()); - auto power = 0.f; - for (auto const &primitive : primitives) { - p_min = min(p_min, primitive.p_min()); - p_max = max(p_max, primitive.p_max()); - power += primitive.power; - } - return luisa::make_unique(p_min, p_max, power, first_primitive_offset, primitives.size()); - }; - if (primitives.size() <= MAX_LEAF_CAPACITY) { - return create_leaf_node(); - } - auto constexpr NUM_BINS = 12u; - auto constexpr NUM_SPLITS = NUM_BINS - 1u; - std::array bins; - auto constexpr compute_split_axis = [](auto extent) noexcept { - if (extent.x > extent.y) { - return extent.x > extent.z ? BVHSplitAxis::X : BVHSplitAxis::Z; - } else { - return extent.y > extent.z ? BVHSplitAxis::Y : BVHSplitAxis::Z; - } - }; - auto range_min = make_float3(std::numeric_limits::max()); - auto range_max = make_float3(std::numeric_limits::lowest()); - for (auto const &primitive : primitives) { - range_min = min(range_min, primitive.p_min()); - range_max = max(range_max, primitive.p_max()); - } - auto range_extent = range_max - range_min; - auto split_axis = compute_split_axis(range_extent); - while (!primitives.empty()) { - auto const &primitive = primitives.front(); - auto centroid = (primitive.p_min() + primitive.p_max()) * .5f; - auto bin_index = static_cast(clamp((centroid[split_axis] - range_min[split_axis]) / range_extent[split_axis] * NUM_BINS, 0.f, NUM_BINS - 1.f)); - auto &bin = bins[bin_index]; - bin.primitives().splice(bin.primitives().end(), primitives, primitives.begin()); - } - for (auto &bin : bins) {bin.update();} - float split_costs[NUM_SPLITS]; - std::fill(split_costs, split_costs + NUM_SPLITS, 0.f); - auto split_min = make_float3(std::numeric_limits::max()); - auto split_max = make_float3(std::numeric_limits::lowest()); - auto split_power = 0.f; - for (auto i = 0u; i < NUM_SPLITS; i++) { - split_min = min(split_min, bins[i].p_min()); - split_max = max(split_max, bins[i].p_max()); - split_power += bins[i].power(); - auto split_extent = split_max - split_min; - auto split_area = 2.f * (split_extent.x * split_extent.y + split_extent.y * split_extent.z + split_extent.z * split_extent.x); - split_costs[i] += split_power * split_area; - } - split_min = make_float3(std::numeric_limits::max()); - split_max = make_float3(std::numeric_limits::lowest()); - split_power = 0.f; - for (auto i = NUM_SPLITS; i > 0u; i--) { - split_min = min(split_min, bins[i].p_min()); - split_max = max(split_max, bins[i].p_max()); - split_power += bins[i].power(); - auto split_extent = split_max - split_min; - auto split_area = 2.f * (split_extent.x * split_extent.y + split_extent.y * split_extent.z + split_extent.z * split_extent.x); - split_costs[i - 1u] += split_power * split_area; - } - auto min_split_cost = std::numeric_limits::max(); - auto best_split_index = 0u; - for (auto i = 0u; i < NUM_SPLITS; i++) { - if (split_costs[i] < min_split_cost) { - min_split_cost = split_costs[i]; - best_split_index = i; - } - } - luisa::list left_child_primitives, right_child_primitives; - for (auto i = 0u; i < NUM_BINS; i++) { - if (i <= best_split_index) { - left_child_primitives.splice(left_child_primitives.end(), bins[i].primitives()); - } else { - right_child_primitives.splice(right_child_primitives.end(), bins[i].primitives()); - } + auto range_min = make_float3(std::numeric_limits::max()); + auto range_max = make_float3(std::numeric_limits::lowest()); + for (auto const &primitive : primitives) { + range_min = min(range_min, primitive.p_min()); + range_max = max(range_max, primitive.p_max()); + } + auto range_extent = range_max - range_min; + auto split_axis = compute_split_axis(range_extent); + while (!primitives.empty()) { + auto const &primitive = primitives.front(); + auto centroid = (primitive.p_min() + primitive.p_max()) * .5f; + auto bin_index = static_cast(clamp((centroid[split_axis] - range_min[split_axis]) / range_extent[split_axis] * NUM_BINS, 0.f, NUM_BINS - 1.f)); + auto &bin = bins[bin_index]; + bin.primitives().splice(bin.primitives().end(), primitives, primitives.begin()); + } + for (auto &bin : bins) { bin.update(); } + float split_costs[NUM_SPLITS]; + std::fill(split_costs, split_costs + NUM_SPLITS, 0.f); + auto split_min = make_float3(std::numeric_limits::max()); + auto split_max = make_float3(std::numeric_limits::lowest()); + auto split_power = 0.f; + for (auto i = 0u; i < NUM_SPLITS; i++) { + split_min = min(split_min, bins[i].p_min()); + split_max = max(split_max, bins[i].p_max()); + split_power += bins[i].power(); + auto split_extent = split_max - split_min; + auto split_area = 2.f * (split_extent.x * split_extent.y + split_extent.y * split_extent.z + split_extent.z * split_extent.x); + split_costs[i] += split_power * split_area; + } + split_min = make_float3(std::numeric_limits::max()); + split_max = make_float3(std::numeric_limits::lowest()); + split_power = 0.f; + for (auto i = NUM_SPLITS; i > 0u; i--) { + split_min = min(split_min, bins[i].p_min()); + split_max = max(split_max, bins[i].p_max()); + split_power += bins[i].power(); + auto split_extent = split_max - split_min; + auto split_area = 2.f * (split_extent.x * split_extent.y + split_extent.y * split_extent.z + split_extent.z * split_extent.x); + split_costs[i - 1u] += split_power * split_area; + } + auto min_split_cost = std::numeric_limits::max(); + auto best_split_index = 0u; + for (auto i = 0u; i < NUM_SPLITS; i++) { + if (split_costs[i] < min_split_cost) { + min_split_cost = split_costs[i]; + best_split_index = i; } - if (left_child_primitives.empty() | right_child_primitives.empty()) { - primitives.splice(primitives.end(), left_child_primitives); - primitives.splice(primitives.end(), right_child_primitives); - return create_leaf_node(); + } + luisa::list left_child_primitives, right_child_primitives; + for (auto i = 0u; i < NUM_BINS; i++) { + if (i <= best_split_index) { + left_child_primitives.splice(left_child_primitives.end(), bins[i].primitives()); + } else { + right_child_primitives.splice(right_child_primitives.end(), bins[i].primitives()); } - auto left_child = create_bvh(left_child_primitives, first_primitive_offset); - auto right_child = create_bvh(right_child_primitives, first_primitive_offset + left_child_primitives.size()); + } + if (left_child_primitives.empty() | right_child_primitives.empty()) { primitives.splice(primitives.end(), left_child_primitives); primitives.splice(primitives.end(), right_child_primitives); - return luisa::make_unique(std::move(left_child), std::move(right_child)); + return create_leaf_node(); } - struct alignas(16u) QBVHNode { - uint quantized_p_min; - uint quantized_p_max; - float power; - uint parent; - uint right_child; - uint first_primitive_offset; - uint primitive_count; - uint lut_entry; - [[nodiscard]] static auto encode(const BVHNode &node, float3 world_min, float3 world_max) noexcept { - auto constexpr QUANTIZED_COORDINATE_BITS = 10u; - auto constexpr MAX_COORDINATE_VALUE = static_cast((1u << QUANTIZED_COORDINATE_BITS) - 1u); - auto encode_coordinates = [&](auto p) noexcept { - auto x = make_uint3(clamp((p - world_min) / (world_max - world_min + std::numeric_limits::epsilon()) * MAX_COORDINATE_VALUE, 0.f, MAX_COORDINATE_VALUE)); - return x.x | (x.y << QUANTIZED_COORDINATE_BITS) | (x.z << (2u * QUANTIZED_COORDINATE_BITS)); - }; - return QBVHNode { - .quantized_p_min = encode_coordinates(node.p_min), - .quantized_p_max = encode_coordinates(node.p_max), - .power = node.power, - .parent = 0xffffffffu, - .right_child = 0xffffffffu, - .first_primitive_offset = node.first_primitive_offset, - .primitive_count = node.primitive_count - }; + auto left_child = create_bvh(left_child_primitives, first_primitive_offset); + auto right_child = create_bvh(right_child_primitives, first_primitive_offset + left_child_primitives.size()); + primitives.splice(primitives.end(), left_child_primitives); + primitives.splice(primitives.end(), right_child_primitives); + return luisa::make_unique(std::move(left_child), std::move(right_child)); +} +struct alignas(16u) QBVHNode { + uint quantized_p_min; + uint quantized_p_max; + float power; + uint parent; + uint right_child; + uint first_primitive_offset; + uint primitive_count; + uint lut_entry; + [[nodiscard]] static auto encode(const BVHNode &node, float3 world_min, float3 world_max) noexcept { + auto constexpr QUANTIZED_COORDINATE_BITS = 10u; + auto constexpr MAX_COORDINATE_VALUE = static_cast((1u << QUANTIZED_COORDINATE_BITS) - 1u); + auto encode_coordinates = [&](auto p) noexcept { + auto x = make_uint3(clamp((p - world_min) / (world_max - world_min + std::numeric_limits::epsilon()) * MAX_COORDINATE_VALUE, 0.f, MAX_COORDINATE_VALUE)); + return x.x | (x.y << QUANTIZED_COORDINATE_BITS) | (x.z << (2u * QUANTIZED_COORDINATE_BITS)); + }; + return QBVHNode{ + .quantized_p_min = encode_coordinates(node.p_min), + .quantized_p_max = encode_coordinates(node.p_max), + .power = node.power, + .parent = 0xffffffffu, + .right_child = 0xffffffffu, + .first_primitive_offset = node.first_primitive_offset, + .primitive_count = node.primitive_count}; + } +}; +static_assert(sizeof(BVHPrimitive) == 32u, "Invalid."); +static_assert(sizeof(QBVHNode) == 32u, "Invalid."); +[[nodiscard]] luisa::vector encode_quantized_bvh(BVHNode *root, float3 world_min, float3 world_max) noexcept { + luisa::vector quantized_nodes; + quantized_nodes.reserve(root->size()); + luisa::stack> stack;// child node, parent node index + stack.push({root, 0xffffffffu}); + while (!stack.empty()) { + auto [node, parent_index] = stack.top(); + stack.pop(); + auto current_index = static_cast(quantized_nodes.size()); + quantized_nodes.push_back(QBVHNode::encode(*node, world_min, world_max)); + quantized_nodes[current_index].parent = parent_index; + if (current_index != parent_index + 1u) { + quantized_nodes[parent_index].right_child = current_index; } - }; - static_assert(sizeof(BVHPrimitive) == 32u, "Invalid."); - static_assert(sizeof(QBVHNode) == 32u, "Invalid."); - [[nodiscard]] luisa::vector encode_quantized_bvh(BVHNode *root, float3 world_min, float3 world_max) noexcept { - luisa::vector quantized_nodes; - quantized_nodes.reserve(root->size()); - luisa::stack> stack; // child node, parent node index - stack.push({root, 0xffffffffu}); - while (!stack.empty()) { - auto [node, parent_index] = stack.top(); - stack.pop(); - auto current_index = static_cast(quantized_nodes.size()); - quantized_nodes.push_back(QBVHNode::encode(*node, world_min, world_max)); - quantized_nodes[current_index].parent = parent_index; - if (current_index != parent_index + 1u) { - quantized_nodes[parent_index].right_child = current_index; - } - if (!node->is_leaf()) { - stack.push({node->right_child(), current_index}); - stack.push({node->left_child(), current_index}); - } + if (!node->is_leaf()) { + stack.push({node->right_child(), current_index}); + stack.push({node->left_child(), current_index}); } - return quantized_nodes; } -} // namespace luisa::render + return quantized_nodes; +} +}// namespace luisa::render LUISA_STRUCT(luisa::render::BVHPrimitive, x_min, y_min, z_min, x_max, y_max, z_max, power, tag){}; LUISA_STRUCT(luisa::render::QBVHNode, quantized_p_min, quantized_p_max, power, parent, right_child, first_primitive_offset, primitive_count, lut_entry){}; @@ -249,7 +251,6 @@ class BVHLightSampler final : public LightSampler { class BVHLightSamplerInstance final : public LightSampler::Instance { private: - luisa::shared_ptr> _clear_primitive_data; luisa::shared_ptr> _update_primitive_data; Buffer _world_bounds_buffer; Buffer _bvh_primitive_buffer; @@ -301,28 +302,10 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { _world_bounds_buffer = pipeline.device().create_buffer(2u); _bvh_primitive_buffer = pipeline.device().create_buffer(n); _quantized_bvh_node_buffer = pipeline.device().create_buffer(2u * n - 1u); - _clear_primitive_data = luisa::make_shared>(pipeline.device().compile<1>([&]() noexcept { - set_block_size(256u); - auto n = static_cast(pipeline.geometry()->light_instances().size()); - auto i = dispatch_id().x; - Var primitive; - primitive.x_min = std::numeric_limits::max(); - primitive.y_min = std::numeric_limits::max(); - primitive.z_min = std::numeric_limits::max(); - primitive.x_max = std::numeric_limits::lowest(); - primitive.y_max = std::numeric_limits::lowest(); - primitive.z_max = std::numeric_limits::lowest(); - primitive.power = 0.f; - primitive.tag = 0xffffffffu; - $while(i < n) { - _bvh_primitive_buffer->write(i, primitive); - i += dispatch_size().x; - }; - })); _update_primitive_data = luisa::make_shared>(pipeline.device().compile<1>([&](Float time) noexcept { set_block_size(256u); auto n = static_cast(pipeline.geometry()->light_instances().size()); - auto tag = def(0u); + auto tag = dispatch_id().x; $while(tag < n) { auto handle = pipeline.buffer(_light_handle_buffer_id).read(tag); auto light_inst = pipeline.geometry()->instance(handle.instance_id); @@ -332,8 +315,7 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { auto power = def(0.f); auto p_min = def(make_float3(std::numeric_limits::max())); auto p_max = def(make_float3(std::numeric_limits::lowest())); - auto primitive_id = dispatch_id().x; - $while(primitive_id < light_inst.triangle_count()) { + $for(primitive_id, 0u, light_inst.triangle_count()) { auto triangle = pipeline.geometry()->triangle(light_inst, primitive_id); auto v_buffer = light_inst.vertex_buffer_id(); auto v0 = pipeline.buffer(v_buffer).read(triangle.i0); @@ -357,16 +339,18 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { emission_luminance += closure->evaluate_luminance(Interaction(gravity_center_uv)) / 3.f; }); power += surface_area * emission_luminance; - primitive_id += dispatch_size().x; }; - _bvh_primitive_buffer->atomic(tag).x_min.fetch_min(p_min.x); - _bvh_primitive_buffer->atomic(tag).y_min.fetch_min(p_min.y); - _bvh_primitive_buffer->atomic(tag).z_min.fetch_min(p_min.z); - _bvh_primitive_buffer->atomic(tag).x_max.fetch_max(p_max.x); - _bvh_primitive_buffer->atomic(tag).y_max.fetch_max(p_max.y); - _bvh_primitive_buffer->atomic(tag).z_max.fetch_max(p_max.z); - _bvh_primitive_buffer->atomic(tag).power.fetch_add(power); - tag += 1u; + Var primitive; + primitive.x_min = p_min.x; + primitive.y_min = p_min.y; + primitive.z_min = p_min.z; + primitive.x_max = p_max.x; + primitive.y_max = p_max.y; + primitive.z_max = p_max.z; + primitive.power = power; + primitive.tag = tag; + _bvh_primitive_buffer->write(tag, primitive); + tag += dispatch_size().x; }; })); } @@ -382,20 +366,14 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { void update(CommandBuffer &command_buffer, float time) noexcept override { if (!pipeline().lights().empty()) { - command_buffer << synchronize(); auto n = static_cast(pipeline().geometry()->light_instances().size()); - LUISA_INFO("Reconstructing BVH of {} lights...", n); - Clock clk; luisa::vector primitive_buffer(n); - command_buffer << (*_clear_primitive_data)().dispatch(1024u) - << (*_update_primitive_data)(time).dispatch(1024u) + command_buffer << (*_update_primitive_data)(time).dispatch(1024u) << _bvh_primitive_buffer.copy_to(primitive_buffer.data()) << commit() << synchronize(); luisa::list primitive_list; - for (auto i = 0u; i < n; i++) { - auto primitive = primitive_buffer[i]; - primitive.tag = i; + for (auto const &primitive : primitive_buffer) { primitive_list.push_back(primitive); } auto bvh = create_bvh(primitive_list); @@ -403,6 +381,9 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { std::array world_bounds; world_bounds[0] = bvh->p_min; world_bounds[1] = bvh->p_max; + command_buffer << _world_bounds_buffer.copy_from(world_bounds.data()) + << _bvh_primitive_buffer.copy_from(primitive_buffer.data()) + << commit(); auto quantized_nodes = encode_quantized_bvh(bvh.get(), world_bounds[0], world_bounds[1]); if (quantized_nodes.size() < 2u * n - 1u) { quantized_nodes.resize(2u * n - 1u); @@ -418,11 +399,8 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { } } command_buffer << _quantized_bvh_node_buffer.copy_from(quantized_nodes.data()) - << _world_bounds_buffer.copy_from(world_bounds.data()) - << _bvh_primitive_buffer.copy_from(primitive_buffer.data()) - << commit(); - command_buffer << synchronize(); - LUISA_INFO("BVH updated in {} ms.", clk.toc()); + << commit() + << synchronize(); } } @@ -432,7 +410,8 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { auto prob = def(0.f); $if(tag == LightSampler::selection_environment) { prob = _env_prob; - } $else { + } + $else { if (pipeline().lights().empty()) [[unlikely]] {// no lights LUISA_WARNING_WITH_LOCATION("No lights in scene."); prob = 0.f; @@ -466,7 +445,8 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { current = parent; current_node = parent_node; }; - } $else { + } + $else { prob = 0.f; }; } @@ -522,7 +502,8 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { current_node = left_child; prob *= left_prob; uu /= left_prob; - } $else { + } + $else { current = current_node.right_child; current_node = right_child; prob *= 1.f - left_prob; @@ -542,7 +523,8 @@ class BVHLightSamplerInstance final : public LightSampler::Instance { w1 = w; selected_primitive_id = primitive_id; uu /= p; - } $else { + } + $else { uu = (uu - p) / (1.f - p); }; }; @@ -623,6 +605,6 @@ unique_ptr BVHLightSampler::build(Pipeline &pipeline, Co this, pipeline, command_buffer); } -} // namespace luisa::render +}// namespace luisa::render LUISA_RENDER_MAKE_SCENE_NODE_PLUGIN(luisa::render::BVHLightSampler) diff --git a/src/lightsamplers/power.cpp b/src/lightsamplers/power.cpp index 22408898..3b6bd175 100644 --- a/src/lightsamplers/power.cpp +++ b/src/lightsamplers/power.cpp @@ -27,8 +27,7 @@ class PowerLightSampler final : public LightSampler { class PowerLightSamplerInstance final : public LightSampler::Instance { private: - luisa::shared_ptr> _clear_light_power; - luisa::shared_ptr> _compute_light_power; + luisa::shared_ptr> _compute_light_power; Buffer _alias_table_buffer; Buffer _pdf_buffer; uint _light_handle_buffer_id{0u}; @@ -39,42 +38,34 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { PowerLightSamplerInstance(const PowerLightSampler *sampler, Pipeline &pipeline, CommandBuffer &command_buffer) noexcept : LightSampler::Instance{pipeline, sampler} { if (!pipeline.lights().empty()) { - auto num_inst = static_cast(pipeline.geometry()->instances().size()); - auto num_light_inst = static_cast(pipeline.geometry()->light_instances().size()); - auto [light_handle_buffer_view, light_handle_buffer_id] = pipeline.bindless_arena_buffer(num_light_inst); + auto m = static_cast(pipeline.geometry()->instances().size()); + auto n = static_cast(pipeline.geometry()->light_instances().size()); + auto [light_handle_buffer_view, light_handle_buffer_id] = pipeline.bindless_arena_buffer(n); _light_handle_buffer_id = light_handle_buffer_id; - auto [tag_lut_buffer_view, tag_lut_buffer_id] = pipeline.bindless_arena_buffer(num_inst); + auto [tag_lut_buffer_view, tag_lut_buffer_id] = pipeline.bindless_arena_buffer(m); _tag_lut_buffer_id = tag_lut_buffer_id; - _alias_table_buffer = pipeline.device().create_buffer(num_light_inst); - _pdf_buffer = pipeline.device().create_buffer(num_light_inst); + _alias_table_buffer = pipeline.device().create_buffer(n); + _pdf_buffer = pipeline.device().create_buffer(n); command_buffer << light_handle_buffer_view.copy_from(pipeline.geometry()->light_instances().data()) << commit(); - luisa::vector tag_lut(num_inst); - for (auto i = 0u; i < num_light_inst; i++) { + luisa::vector tag_lut(m); + for (auto i = 0u; i < n; i++) { auto const &handle = pipeline.geometry()->light_instances()[i]; tag_lut[handle.instance_id] = i; } command_buffer << tag_lut_buffer_view.copy_from(tag_lut.data()) << commit(); - _clear_light_power = luisa::make_shared>(pipeline.device().compile<1>([&](UInt num_light_inst) noexcept { + _compute_light_power = luisa::make_shared>(pipeline.device().compile<1>([&](Float time) noexcept { set_block_size(256u); - auto i = dispatch_id().x; - $while(i < num_light_inst) { - _pdf_buffer->write(i, 0.f); - i += dispatch_size().x; - }; - })); - _compute_light_power = luisa::make_shared>(pipeline.device().compile<1>([&](UInt num_light_inst, Float time) noexcept { - set_block_size(256u); - auto instance_id = def(0u); - $while(instance_id < num_light_inst) { + auto instance_id = dispatch_id().x; + auto n = static_cast(pipeline.geometry()->light_instances().size()); + $while(instance_id < n) { auto power = def(0.f); auto handle = pipeline.buffer(_light_handle_buffer_id).read(instance_id); auto light_inst = pipeline.geometry()->instance(handle.instance_id); auto object_to_world = pipeline.geometry()->instance_to_world(handle.instance_id); auto m = make_float3x3(object_to_world); - auto primitive_id = dispatch_id().x; - $while(primitive_id < light_inst.triangle_count()) { + auto v_buffer = light_inst.vertex_buffer_id(); + $for(primitive_id, 0u, light_inst.triangle_count()) { auto triangle = pipeline.geometry()->triangle(light_inst, primitive_id); - auto v_buffer = light_inst.vertex_buffer_id(); auto v0 = pipeline.buffer(v_buffer).read(triangle.i0); auto v1 = pipeline.buffer(v_buffer).read(triangle.i1); auto v2 = pipeline.buffer(v_buffer).read(triangle.i2); @@ -91,10 +82,9 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { emission_luminance += closure->evaluate_luminance(Interaction(gravity_center_uv)) / 3.f; }); power += emission_luminance * surface_area; - primitive_id += dispatch_size().x; }; - _pdf_buffer->atomic(instance_id).fetch_add(power); - instance_id += 1u; + _pdf_buffer->write(instance_id, power); + instance_id += dispatch_size().x; }; })); command_buffer << synchronize(); @@ -106,17 +96,17 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { void update(CommandBuffer &command_buffer, float time) noexcept override { if (!pipeline().lights().empty()) { - auto num_light_inst = static_cast(pipeline().geometry()->light_instances().size()); - luisa::vector power_table(num_light_inst); - command_buffer << (*_clear_light_power)(num_light_inst).dispatch(1024u) - << (*_compute_light_power)(num_light_inst, time).dispatch(1024u) + auto n = static_cast(pipeline().geometry()->light_instances().size()); + luisa::vector power_table(n); + command_buffer << (*_compute_light_power)(time).dispatch(1024u) << _pdf_buffer.copy_to(power_table.data()) << commit(); command_buffer << synchronize(); auto [alias_table, pdf] = create_alias_table(power_table); command_buffer << _alias_table_buffer.copy_from(alias_table.data()) << _pdf_buffer.copy_from(pdf.data()) - << commit(); + << commit() + << synchronize(); } } @@ -126,7 +116,8 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { auto prob = def(0.f); $if(tag == LightSampler::selection_environment) { prob = _env_prob; - } $else { + } + $else { if (pipeline().lights().empty()) [[unlikely]] {// no lights LUISA_WARNING_WITH_LOCATION("No lights in scene."); prob = 0.f; @@ -134,7 +125,8 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { auto n = static_cast(pipeline().geometry()->light_instances().size()); $if(tag >= n) { prob = 0.f; - } $else { + } + $else { prob = (1.f - _env_prob) * _pdf_buffer->read(tag); }; } @@ -143,8 +135,8 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { } [[nodiscard]] Light::Evaluation evaluate_hit( - const Interaction &it, Expr p_from, - const SampledWavelengths &swl, Expr time) const noexcept override { + const Interaction &it, Expr p_from, + const SampledWavelengths &swl, Expr time) const noexcept override { auto eval = Light::Evaluation::zero(swl.dimension()); if (pipeline().lights().empty()) [[unlikely]] {// no lights LUISA_WARNING_WITH_LOCATION("No lights in scene."); @@ -160,7 +152,7 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { } [[nodiscard]] Light::Evaluation evaluate_miss( - Expr wi, const SampledWavelengths &swl, Expr time) const noexcept override { + Expr wi, const SampledWavelengths &swl, Expr time) const noexcept override { if (_env_prob == 0.f) [[unlikely]] {// no environment LUISA_WARNING_WITH_LOCATION("No environment in scene"); return {.L = SampledSpectrum{swl.dimension()}, .pdf = 0.f}; @@ -171,8 +163,8 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { } [[nodiscard]] LightSampler::Selection select( - const Interaction &it_from, Expr u, - const SampledWavelengths &swl, Expr time) const noexcept override { + const Interaction &it_from, Expr u, + const SampledWavelengths &swl, Expr time) const noexcept override { LUISA_ASSERT(pipeline().has_lighting(), "No lights in scene."); auto n = static_cast(pipeline().geometry()->light_instances().size()); if (_env_prob == 1.f) { return {.tag = LightSampler::selection_environment, .prob = 1.f}; } @@ -185,8 +177,8 @@ class PowerLightSamplerInstance final : public LightSampler::Instance { } [[nodiscard]] LightSampler::Selection select( - Expr u, - const SampledWavelengths &swl, Expr time) const noexcept override { + Expr u, + const SampledWavelengths &swl, Expr time) const noexcept override { LUISA_ASSERT(pipeline().has_lighting(), "No lights in scene."); auto n = static_cast(pipeline().geometry()->light_instances().size()); if (_env_prob == 1.f) { return {.tag = LightSampler::selection_environment, .prob = 1.f}; } @@ -263,6 +255,6 @@ unique_ptr PowerLightSampler::build( this, pipeline, command_buffer); } -} // namespace luisa::render +}// namespace luisa::render LUISA_RENDER_MAKE_SCENE_NODE_PLUGIN(luisa::render::PowerLightSampler) \ No newline at end of file