diff --git a/src/base/geometry.cpp b/src/base/geometry.cpp index b215b4ec..2ef0cd85 100644 --- a/src/base/geometry.cpp +++ b/src/base/geometry.cpp @@ -108,7 +108,6 @@ void Geometry::_process_shape( InstancedTransform inst_xform{t_node, instance_id}; if (!is_static) { _dynamic_transforms.emplace_back(inst_xform); } auto object_to_world = inst_xform.matrix(init_time); - _accel.emplace_back(*mesh.resource, object_to_world, visible); auto vertices = shape->mesh().vertices; for (auto &v : vertices) { _world_max = max(_world_max, make_float3(object_to_world * make_float4(v.position(), 1.f))); @@ -117,13 +116,22 @@ void Geometry::_process_shape( // create instance auto surface_tag = 0u; - auto light_tag = 0u; - auto medium_tag = 0u; auto properties = mesh.vertex_properties; if (surface != nullptr && !surface->is_null()) { surface_tag = _pipeline.register_surface(command_buffer, surface); properties |= Shape::property_flag_has_surface; + if (_pipeline.surfaces().impl(surface_tag)->maybe_non_opaque()) { + properties |= Shape::property_flag_maybe_non_opaque; + _any_non_opaque = true; + } } + + // emplace instance here since we need to know the opaque property + _accel.emplace_back(*mesh.resource, object_to_world, visible, false); + //(properties & Shape::property_flag_maybe_non_opaque) == 0u); + + auto light_tag = 0u; + auto medium_tag = 0u; if (light != nullptr && !light->is_null()) { light_tag = _pipeline.register_light(command_buffer, light); properties |= Shape::property_flag_has_light; @@ -179,12 +187,88 @@ bool Geometry::update(CommandBuffer &command_buffer, float time) noexcept { } Var Geometry::trace_closest(const Var &ray) const noexcept { - auto hit = _accel->trace_closest(ray); - return Var{hit.inst, hit.prim, hit.bary}; + if (!_any_non_opaque) { + // happy path + auto hit = _accel->intersect(ray, {}); + return Var{hit.inst, hit.prim, hit.bary}; + } + auto rq_hit = + _accel->traverse(ray, {}) + .on_surface_candidate([&](compute::SurfaceCandidate &c) noexcept { + auto hit = c.hit(); + auto bary = make_float3(1.f - hit.bary.x - hit.bary.y, hit.bary); + auto it = interaction(hit.inst, hit.prim, bary, -ray->direction()); + $if(it->shape().maybe_non_opaque() & it->shape().has_surface()) { + PolymorphicCall call; + _pipeline.surfaces().dispatch(it->shape().surface_tag(), [&](auto surface) noexcept { + $if(surface->maybe_non_opaque()) { + // TODO: pass the correct time + surface->closure(call, *it, _pipeline.spectrum()->sample(.5f), -ray->direction(), 1.f, 0.f); + }; + }); + auto u1 = xxhash32(as(ray->origin())); + auto u2 = xxhash32(as(ray->direction())); + auto u = xxhash32(make_uint2(u1, u2)) * 0x1p-32f; + call.execute([&](const Surface::Closure *closure) noexcept { + // apply opacity map + auto alpha_skip = def(false); + if (auto o = closure->opacity()) { + auto opacity = saturate(*o); + alpha_skip = u >= opacity; + } + $if(!alpha_skip) { + c.commit(); + }; + }); + } + $else { + c.commit(); + }; + }) + .trace(); + return Var{rq_hit.inst, rq_hit.prim, rq_hit.bary}; } Var Geometry::trace_any(const Var &ray) const noexcept { - return _accel->trace_any(ray); + if (!_any_non_opaque) { + // happy path + return _accel->intersect_any(ray, {}); + } + auto rq_hit = + _accel->traverse_any(ray, {}) + .on_surface_candidate([&](compute::SurfaceCandidate &c) noexcept { + auto hit = c.hit(); + auto bary = make_float3(1.f - hit.bary.x - hit.bary.y, hit.bary); + auto it = interaction(hit.inst, hit.prim, bary, -ray->direction()); + $if(it->shape().maybe_non_opaque() & it->shape().has_surface()) { + PolymorphicCall call; + _pipeline.surfaces().dispatch(it->shape().surface_tag(), [&](auto surface) noexcept { + $if(surface->maybe_non_opaque()) { + // TODO: pass the correct time + surface->closure(call, *it, _pipeline.spectrum()->sample(.5f), -ray->direction(), 1.f, 0.f); + }; + }); + auto u1 = xxhash32(as(ray->origin())); + auto u2 = xxhash32(as(ray->direction())); + auto u = xxhash32(make_uint2(u1, u2)) * 0x1p-32f; + call.execute([&](const Surface::Closure *closure) noexcept { + // apply opacity map + auto alpha_skip = def(false); + if (auto o = closure->opacity()) { + auto opacity = saturate(*o); + alpha_skip = u >= opacity; + } + $if(!alpha_skip) { + c.commit(); + }; + }); + } + $else { + c.commit(); + }; + }) + .trace(); + return !rq_hit->miss(); } luisa::shared_ptr Geometry::interaction(Expr inst_id, Expr prim_id, diff --git a/src/base/geometry.h b/src/base/geometry.h index 352925e6..ea434928 100644 --- a/src/base/geometry.h +++ b/src/base/geometry.h @@ -69,8 +69,8 @@ class Geometry { Buffer _instance_buffer; float3 _world_min; float3 _world_max; - - uint _triangle_count; // for debug + uint _triangle_count;// for debug + bool _any_non_opaque{false}; private: void _process_shape( diff --git a/src/base/pipeline.cpp b/src/base/pipeline.cpp index 2a32bdaf..e39c764c 100644 --- a/src/base/pipeline.cpp +++ b/src/base/pipeline.cpp @@ -101,7 +101,7 @@ luisa::unique_ptr Pipeline::create(Device &device, Stream &stream, con bool Pipeline::update(CommandBuffer &command_buffer, float time) noexcept { // TODO: support deformable meshes auto updated = _geometry->update(command_buffer, time); - if (_any_dynamic_transforms) { + if (_any_dynamic_transform) { updated = true; for (auto i = 0u; i < _transforms.size(); ++i) { _transform_matrices[i] = _transforms[i]->matrix(time); @@ -151,7 +151,7 @@ void Pipeline::register_transform(const Transform *transform) noexcept { "Transform matrix buffer overflows."); _transform_to_id.emplace(transform, transform_id); _transforms.emplace_back(transform); - _any_dynamic_transforms |= !transform->is_static(); + _any_dynamic_transform |= !transform->is_static(); _transform_matrices[transform_id] = transform->matrix(_initial_time); } } diff --git a/src/base/pipeline.h b/src/base/pipeline.h index fd8d4bdc..c1459b3c 100644 --- a/src/base/pipeline.h +++ b/src/base/pipeline.h @@ -96,7 +96,8 @@ class Pipeline { // other things luisa::unique_ptr _printer; float _initial_time{}; - bool _any_dynamic_transforms{false}; + bool _any_dynamic_transform{false}; + bool _any_non_opaque_surface{false}; public: // for internal use only; use Pipeline::create() instead @@ -207,6 +208,7 @@ class Pipeline { [[nodiscard]] auto spectrum() const noexcept { return _spectrum.get(); } [[nodiscard]] auto geometry() const noexcept { return _geometry.get(); } [[nodiscard]] auto has_lighting() const noexcept { return !_lights.empty() || _environment != nullptr; } + [[nodiscard]] auto has_non_opaque_surfaces() const noexcept { return _any_non_opaque_surface; } [[nodiscard]] const Texture::Instance *build_texture(CommandBuffer &command_buffer, const Texture *texture) noexcept; [[nodiscard]] const Filter::Instance *build_filter(CommandBuffer &command_buffer, const Filter *filter) noexcept; [[nodiscard]] const PhaseFunction::Instance *build_phasefunction(CommandBuffer &command_buffer, const PhaseFunction *phasefunction) noexcept; diff --git a/src/base/shape.h b/src/base/shape.h index 9454c954..80ec473a 100644 --- a/src/base/shape.h +++ b/src/base/shape.h @@ -36,6 +36,7 @@ class Shape : public SceneNode { static constexpr auto property_flag_has_surface = 1u << 2u; static constexpr auto property_flag_has_light = 1u << 3u; static constexpr auto property_flag_has_medium = 1u << 4u; + static constexpr auto property_flag_maybe_non_opaque = 1u << 5u; private: const Surface *_surface; @@ -153,7 +154,8 @@ class Shape::Handle { private: Handle(Expr buffer_base, Expr flags, Expr surface_tag, Expr light_tag, Expr medium_tag, - Expr triangle_count, Expr shadow_terminator, Expr intersection_offset) noexcept + Expr triangle_count, + Expr shadow_terminator, Expr intersection_offset) noexcept : _buffer_base{buffer_base}, _properties{flags}, _surface_tag{surface_tag}, _light_tag{light_tag}, _medium_tag{medium_tag}, _triangle_count{triangle_count}, @@ -184,6 +186,7 @@ class Shape::Handle { [[nodiscard]] auto has_light() const noexcept { return test_property_flag(luisa::render::Shape::property_flag_has_light); } [[nodiscard]] auto has_surface() const noexcept { return test_property_flag(luisa::render::Shape::property_flag_has_surface); } [[nodiscard]] auto has_medium() const noexcept { return test_property_flag(luisa::render::Shape::property_flag_has_medium); } + [[nodiscard]] auto maybe_non_opaque() const noexcept { return test_property_flag(luisa::render::Shape::property_flag_maybe_non_opaque); } [[nodiscard]] auto shadow_terminator_factor() const noexcept { return _shadow_terminator; } [[nodiscard]] auto intersection_offset_factor() const noexcept { return _intersection_offset; } }; diff --git a/src/base/surface.h b/src/base/surface.h index df49f913..7922fa71 100644 --- a/src/base/surface.h +++ b/src/base/surface.h @@ -128,6 +128,8 @@ class Surface : public SceneNode { [[nodiscard]] auto node() const noexcept { return static_cast(_surface); } [[nodiscard]] auto &pipeline() const noexcept { return _pipeline; } + [[nodiscard]] virtual bool maybe_non_opaque() const noexcept { return false; } + void closure(PolymorphicCall &call, const Interaction &it, const SampledWavelengths &swl, Expr wo, Expr eta_i, Expr time) const noexcept; @@ -252,6 +254,12 @@ class OpacitySurfaceWrapper : public BaseSurface { typename Closure::Context ctx{.opacity = o}; closure->bind(std::move(ctx)); } + + [[nodiscard]] bool maybe_non_opaque() const noexcept override { + if (BaseInstance::maybe_non_opaque()) { return true; } + return _opacity != nullptr && + _opacity->node()->evaluate_static().value_or(make_float4(0.f)).x < 1.f; + } }; private: diff --git a/src/integrators/mega_path.cpp b/src/integrators/mega_path.cpp index 5af11355..18b4d98d 100644 --- a/src/integrators/mega_path.cpp +++ b/src/integrators/mega_path.cpp @@ -117,42 +117,28 @@ class MegakernelPathTracingInstance final : public ProgressiveIntegrator::Instan surface->closure(call, *it, swl, wo, 1.f, time); }); call.execute([&](const Surface::Closure *closure) noexcept { - // apply opacity map - auto alpha_skip = def(false); - if (auto o = closure->opacity()) { - auto opacity = saturate(*o); - alpha_skip = u_lobe >= opacity; - u_lobe = ite(alpha_skip, (u_lobe - opacity) / (1.f - opacity), u_lobe / opacity); + if (auto dispersive = closure->is_dispersive()) { + $if(*dispersive) { swl.terminate_secondary(); }; } - - $if(alpha_skip) { - ray = it->spawn_ray(ray->direction()); - pdf_bsdf = 1e16f; - } - $else { - if (auto dispersive = closure->is_dispersive()) { - $if(*dispersive) { swl.terminate_secondary(); }; - } - // direct lighting - $if(light_sample.eval.pdf > 0.0f & !occluded) { - auto wi = light_sample.shadow_ray->direction(); - auto eval = closure->evaluate(wo, wi); - auto w = balance_heuristic(light_sample.eval.pdf, eval.pdf) / - light_sample.eval.pdf; - Li += w * beta * eval.f * light_sample.eval.L; - }; - // sample material - auto surface_sample = closure->sample(wo, u_lobe, u_bsdf); - ray = it->spawn_ray(surface_sample.wi); - pdf_bsdf = surface_sample.eval.pdf; - auto w = ite(surface_sample.eval.pdf > 0.f, 1.f / surface_sample.eval.pdf, 0.f); - beta *= w * surface_sample.eval.f; - // apply eta scale - auto eta = closure->eta().value_or(1.f); - $switch(surface_sample.event) { - $case(Surface::event_enter) { eta_scale = sqr(eta); }; - $case(Surface::event_exit) { eta_scale = sqr(1.f / eta); }; - }; + // direct lighting + $if(light_sample.eval.pdf > 0.0f & !occluded) { + auto wi = light_sample.shadow_ray->direction(); + auto eval = closure->evaluate(wo, wi); + auto w = balance_heuristic(light_sample.eval.pdf, eval.pdf) / + light_sample.eval.pdf; + Li += w * beta * eval.f * light_sample.eval.L; + }; + // sample material + auto surface_sample = closure->sample(wo, u_lobe, u_bsdf); + ray = it->spawn_ray(surface_sample.wi); + pdf_bsdf = surface_sample.eval.pdf; + auto w = ite(surface_sample.eval.pdf > 0.f, 1.f / surface_sample.eval.pdf, 0.f); + beta *= w * surface_sample.eval.f; + // apply eta scale + auto eta = closure->eta().value_or(1.f); + $switch(surface_sample.event) { + $case(Surface::event_enter) { eta_scale = sqr(eta); }; + $case(Surface::event_exit) { eta_scale = sqr(1.f / eta); }; }; }); }; diff --git a/src/integrators/wave_path.cpp b/src/integrators/wave_path.cpp index a363b202..1627177a 100644 --- a/src/integrators/wave_path.cpp +++ b/src/integrators/wave_path.cpp @@ -402,49 +402,35 @@ void WavefrontPathTracingInstance::_render_one_camera( }); call.execute([&](const Surface::Closure *closure) noexcept { - // apply opacity map - auto alpha_skip = def(false); - if (auto o = closure->opacity()) { - auto opacity = saturate(*o); - alpha_skip = u_lobe >= opacity; - u_lobe = ite(alpha_skip, (u_lobe - opacity) / (1.f - opacity), u_lobe / opacity); - } - - $if(alpha_skip) { - ray = it->spawn_ray(ray->direction()); - path_states.write_pdf_bsdf(path_id, 1e16f); - } - $else { - if (auto dispersive = closure->is_dispersive()) { - $if(*dispersive) { - swl.terminate_secondary(); - path_states.terminate_secondary_wavelengths(path_id, u_wl); - }; - } - // direct lighting - auto light_wi_and_pdf = light_samples.read_wi_and_pdf(queue_id); - auto pdf_light = light_wi_and_pdf.w; - $if(light_wi_and_pdf.w > 0.f) { - auto eval = closure->evaluate(wo, light_wi_and_pdf.xyz()); - auto mis_weight = balance_heuristic(pdf_light, eval.pdf); - // update Li - auto Ld = light_samples.read_emission(queue_id); - auto Li = path_states.read_radiance(path_id); - Li += mis_weight / pdf_light * beta * eval.f * Ld; - path_states.write_radiance(path_id, Li); - }; - // sample material - auto surface_sample = closure->sample(wo, u_lobe, u_bsdf); - path_states.write_pdf_bsdf(path_id, surface_sample.eval.pdf); - ray = it->spawn_ray(surface_sample.wi); - auto w = ite(surface_sample.eval.pdf > 0.0f, 1.f / surface_sample.eval.pdf, 0.f); - beta *= w * surface_sample.eval.f; - // eta scale - auto eta = closure->eta().value_or(1.f); - $switch(surface_sample.event) { - $case(Surface::event_enter) { eta_scale = sqr(eta); }; - $case(Surface::event_exit) { eta_scale = 1.f / sqr(eta); }; + if (auto dispersive = closure->is_dispersive()) { + $if(*dispersive) { + swl.terminate_secondary(); + path_states.terminate_secondary_wavelengths(path_id, u_wl); }; + } + // direct lighting + auto light_wi_and_pdf = light_samples.read_wi_and_pdf(queue_id); + auto pdf_light = light_wi_and_pdf.w; + $if(light_wi_and_pdf.w > 0.f) { + auto eval = closure->evaluate(wo, light_wi_and_pdf.xyz()); + auto mis_weight = balance_heuristic(pdf_light, eval.pdf); + // update Li + auto Ld = light_samples.read_emission(queue_id); + auto Li = path_states.read_radiance(path_id); + Li += mis_weight / pdf_light * beta * eval.f * Ld; + path_states.write_radiance(path_id, Li); + }; + // sample material + auto surface_sample = closure->sample(wo, u_lobe, u_bsdf); + path_states.write_pdf_bsdf(path_id, surface_sample.eval.pdf); + ray = it->spawn_ray(surface_sample.wi); + auto w = ite(surface_sample.eval.pdf > 0.0f, 1.f / surface_sample.eval.pdf, 0.f); + beta *= w * surface_sample.eval.f; + // eta scale + auto eta = closure->eta().value_or(1.f); + $switch(surface_sample.event) { + $case(Surface::event_enter) { eta_scale = sqr(eta); }; + $case(Surface::event_exit) { eta_scale = 1.f / sqr(eta); }; }; }); diff --git a/src/surfaces/layered.cpp b/src/surfaces/layered.cpp index 21e64650..177b2212 100644 --- a/src/surfaces/layered.cpp +++ b/src/surfaces/layered.cpp @@ -176,6 +176,10 @@ class LayeredSurfaceInstance : public Surface::Instance { } [[nodiscard]] luisa::unique_ptr create_closure(const SampledWavelengths &swl, Expr time) const noexcept override; void populate_closure(Surface::Closure *closure, const Interaction &it, Expr wo, Expr eta_i) const noexcept override; + + [[nodiscard]] bool maybe_non_opaque() const noexcept override { + return _top->maybe_non_opaque() || _bottom->maybe_non_opaque(); + } }; luisa::unique_ptr LayeredSurface::_build(Pipeline &pipeline, CommandBuffer &command_buffer) const noexcept { diff --git a/src/surfaces/mix.cpp b/src/surfaces/mix.cpp index 3684d7ba..88566e9e 100644 --- a/src/surfaces/mix.cpp +++ b/src/surfaces/mix.cpp @@ -55,6 +55,10 @@ class MixSurfaceInstance : public Surface::Instance { } [[nodiscard]] luisa::unique_ptr create_closure(const SampledWavelengths &swl, Expr time) const noexcept override; void populate_closure(Surface::Closure *closure, const Interaction &it, Expr wo, Expr eta_i) const noexcept override; + + [[nodiscard]] bool maybe_non_opaque() const noexcept override { + return _a->maybe_non_opaque() || _b->maybe_non_opaque(); + } }; luisa::unique_ptr MixSurface::_build(