#pragma once #include "rhi.h" #include "scene/scene_types.h" // BlackHoleParams, SceneObject #include #include namespace Donut { // The black-hole view, written ONCE against the RHI. Each frame it ray-traces // the geodesic shader into an off-screen target (progressive resolution: a // small target while the camera moves, a larger one once it settles) and then // upscales that target onto the swapchain. The Application owns the camera and // the HDRI cubemap and passes them in. struct GeodesicView { glm::vec3 position, right, up, forward; float tan_half_fov = 1.0f; float aspect = 16.0f / 9.0f; bool moving = false; float time = 0.0f; }; class BlackHoleRenderer { public: static constexpr int GEO_LO_W = 480, GEO_LO_H = 270; static constexpr int GEO_HI_W = 960, GEO_HI_H = 540; auto init(RHI::Device& device) -> bool; // Off-screen geodesic pass (opens/closes its own render pass). Call before // the swapchain pass; `cubemap` is the shared HDRI environment. `objects` // are the Scene's spheres — ray-traced as lensed spheres around the hole // (1 scene unit = 1 Schwarzschild radius). Grid units == r_s. auto render_geodesic(RHI::CommandList& cmd, const GeodesicView& view, const BlackHoleParams& params, const std::vector& objects, RHI::Texture* cubemap) -> void; // Upscales the geodesic target onto the swapchain (call inside the open // swapchain render pass, before ImGui). auto blit(RHI::CommandList& cmd, int fb_width, int fb_height) -> void; private: RHI::Device* m_device = nullptr; Ref m_geo_pipeline; Ref m_geo_lo; Ref m_geo_hi; Ref m_quad_vb; Ref m_cam_ubo; Ref m_disk_ubo; Ref m_obj_ubo; Ref m_sim_ubo; Ref m_present_pipeline; RHI::RenderTarget* m_last_target = nullptr; // target chosen this frame }; }