17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-11 05:51:01 +01:00

Replace cubemap scenery reflections with SSR, add Vulkan ray traced reflections

This commit is contained in:
2026-10-25 13:54:49 +01:00
parent 89f11895fc
commit f20b1a3071
45 changed files with 2725 additions and 174 deletions

View File

@@ -638,9 +638,10 @@ void scene_walk::Render(cell_sequence::iterator First, cell_sequence::iterator L
}
}
if( Global.reflectiontune.fidelity >= 2 ) {
// opaque parts of vehicles
// opaque parts of vehicles, but the one the backend leaves out (the vehicle of the cab, seen from the inside)
for( auto *path : cell->m_paths ) {
for( auto *dynamic : path->Dynamics ) {
if( dynamic == m_shadowskipvehicle ) { continue; }
Render( dynamic );
}
}

View File

@@ -515,7 +515,8 @@ class scene_walk
// the shadow pass draws the kept scenery part of the map of a lamp: no vehicles, and not the model of the lamp itself
bool m_cubescenery{false};
TAnimModel const *m_cubeskipinstance{nullptr};
// the shadow pass leaves this vehicle out (the one the headlights of the map being drawn belong to)
// the shadow pass leaves this vehicle out (the one the headlights of the map being drawn belong to), and so does the walk of the
// reflection mode (the vehicle of the cab, for the ray traced reflections of the vulkan renderer)
TDynamicObject const *m_shadowskipvehicle{nullptr};
// what the picking passes drew, by the index of their colours
std::vector<TSubModel const *> m_pickcontrolsitems;

View File

@@ -0,0 +1,135 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "rendering/common/screenreflections.h"
#include "vehicle/DynObj.h"
#include "gl/ubo.h"
#include "utilities/Globals.h"
namespace
{
// the camera moved farther than this since the frame of the history (another camera, another vehicle, a jump): the history is
// no good for the reflections
constexpr double CAMERA_JUMP{50.0};
// the box around the vehicle of the cab is this much larger than the vehicle on each side, metres
constexpr double CAB_BOX_MARGIN{0.5};
} // namespace
void gfx::screen_reflections::begin_frame(glm::mat4 const &Projection, glm::dmat3 const &View, glm::dvec3 const &Camera, TDynamicObject const *Cab, glm::ivec2 const &Size, int const Levels)
{
++m_frame;
m_levels = std::max(Levels, 1);
m_current.projection = glm::dmat4{Projection};
m_current.view = View;
m_current.camera = Camera;
m_current.vehicle = Cab;
m_current.cab = glm::dmat4{1.0};
if (Cab != nullptr)
{
// as Render_cab() places it
m_current.cab = glm::translate(glm::dmat4{1.0}, Cab->GetPosition() - Camera) * Cab->mMatrix;
}
m_current.size = Size;
m_current.valid = true;
m_historyvalid = m_history.valid && (m_history.size == Size) && (glm::length(Camera - m_history.camera) < CAMERA_JUMP);
m_hascab = false;
if (false == m_historyvalid)
{
m_reprojection = glm::mat4{1.f};
m_reprojectioncab = glm::mat4{1.f};
m_cabbox = glm::mat4{1.f};
return;
}
// the view of this frame to the world around the camera, the world around the camera of the history, its view and projection
auto const toworld{glm::dmat4{glm::inverse(View)}};
m_reprojection = glm::mat4{m_history.projection * glm::dmat4{m_history.view} * glm::translate(glm::dmat4{1.0}, Camera - m_history.camera) * toworld};
// the cab goes with its vehicle: the view of this frame to the cab, to the world around the camera of the history
if ((Cab != nullptr) && (Cab == m_history.vehicle))
{
m_reprojectioncab = glm::mat4{m_history.projection * glm::dmat4{m_history.view} * m_history.cab * glm::inverse(m_current.cab) * toworld};
// the vehicle stands on the origin of its model, along z, up to its height
auto const &dimensions{Cab->MoverParameters->Dim};
glm::dvec3 const lower{-0.5 * dimensions.W - CAB_BOX_MARGIN, -CAB_BOX_MARGIN, -0.5 * dimensions.L - CAB_BOX_MARGIN};
glm::dvec3 const upper{0.5 * dimensions.W + CAB_BOX_MARGIN, dimensions.H + CAB_BOX_MARGIN, 0.5 * dimensions.L + CAB_BOX_MARGIN};
auto const centre{0.5 * (lower + upper)};
auto const halfsize{glm::max(0.5 * (upper - lower), glm::dvec3{0.1})};
auto const tobox{glm::scale(glm::dmat4{1.0}, 1.0 / halfsize) * glm::translate(glm::dmat4{1.0}, -centre)};
m_cabbox = glm::mat4{tobox * glm::inverse(m_current.cab) * toworld};
m_hascab = true;
}
else
{
m_reprojectioncab = m_reprojection;
m_cabbox = glm::mat4{1.f};
}
}
void gfx::screen_reflections::end_frame()
{
if (m_current.valid)
{
m_history = m_current;
}
}
void gfx::screen_reflections::setup(gl::scene_ubs &Scene, float const Steps, bool const Raytracing, float const Raytracingrange, bool const Temporal) const
{
clear(Scene);
auto const screen{m_historyvalid && (Steps > 0.f)};
if ((false == screen) && (false == Raytracing))
{
return;
}
Scene.ssr_reprojection = m_reprojection;
Scene.ssr_reprojection_cab = m_reprojectioncab;
Scene.ssr_cab_box = m_cabbox;
Scene.ssr_params = glm::vec4{screen ? Steps : 0.f, Global.gfx_ssr_range, Global.gfx_ssr_roughness, Temporal ? static_cast<float>(m_frame % 16) : 0.f};
Scene.ssr_params2 = glm::vec4{static_cast<float>(m_levels - 1), m_hascab ? 1.f : 0.f, Raytracing ? 1.f : 0.f, Raytracingrange};
}
void gfx::screen_reflections::clear(gl::scene_ubs &Scene)
{
Scene.ssr_reprojection = glm::mat4{1.f};
Scene.ssr_reprojection_cab = glm::mat4{1.f};
Scene.ssr_cab_box = glm::mat4{1.f};
Scene.ssr_params = glm::vec4{0.f};
Scene.ssr_params2 = glm::vec4{0.f};
}
float gfx::screen_reflections::steps(int const Level)
{
switch (Level)
{
case 1: return 16.f;
case 2: return 32.f;
case 3: return 64.f;
default: return 0.f;
}
}
void gfx::screen_reflections::draw_debug_ui(char const *Raytracing) const
{
char const *levels[]{"no", "low (16 steps)", "medium (32 steps)", "high (64 steps)"};
ImGui::Combo("screen space reflections (gfx.ssr)", &Global.gfx_ssr, levels, IM_ARRAYSIZE(levels));
ImGui::SliderFloat("longest ray, m (gfx.ssr.range)", &Global.gfx_ssr_range, 10.f, 2000.f, "%.0f", ImGuiSliderFlags_Logarithmic);
ImGui::SliderFloat("roughest surface reflecting the scene (gfx.ssr.roughness)", &Global.gfx_ssr_roughness, 0.f, 1.f, "%.2f");
if (Global.gfx_ssr > 0)
{
ImGui::Text("image of the last frame: %s", m_historyvalid ? "in use" : "none (first frame, camera jump or new size)");
}
if (Raytracing != nullptr)
{
ImGui::TextUnformatted(Raytracing);
}
}

View File

@@ -0,0 +1,85 @@
/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
// the screen space reflections of the main view (gfx.ssr), the part shared by the renderers: the matrices which take the points
// the rays hit in a frame to the place they had in the image of the frame kept for the reflections (the history: the finished
// image of the last frame the view was drawn in, with its mipmaps), and the parameters of the shaders (gl::scene_ubs::ssr_*).
//
// the rays go through the depth of the scene drawn before the colour (light_common.glsl, ssr.glsl). the world is taken to stand
// still; what lies in the box around the vehicle of the cab moves with the camera, and is taken to the history with the matrix of
// the cab. the other vehicles moving leave their reflections a frame behind
#pragma once
#include <glm/glm.hpp>
class TDynamicObject;
namespace gl
{
struct scene_ubs;
}
namespace gfx
{
class screen_reflections
{
public:
// the frame of the colour pass of the main view about to be drawn: Projection as it is drawn (with the offset of the temporal
// anti-aliasing), View the rotation of the camera (the axes of the world to those of the view), Camera its place, Cab the vehicle
// whose cab is drawn (or null), Size of the images of the pass, Levels the mipmaps of the history
void begin_frame(glm::mat4 const &Projection, glm::dmat3 const &View, glm::dvec3 const &Camera, TDynamicObject const *Cab, glm::ivec2 const &Size, int const Levels);
// the image of the frame begun last went to the history
void end_frame();
// the history is dropped (the renderer made its images anew, the view changed size)
void reset()
{
m_history.valid = false;
}
// the history holds an image the frame begun last can take the reflections from
bool history_valid() const
{
return m_historyvalid;
}
// the parameters of the shaders for the frame begun last: Steps of the rays (0: none), whether the ray tracing fills in what the
// screen doesn't hold (Raytracing, the longest traced ray Raytracingrange) and whether the noise of the steps changes from frame
// to frame (Temporal, with the temporal anti-aliasing). the rays of the screen are off when the history can't be used
void setup(gl::scene_ubs &Scene, float const Steps, bool const Raytracing, float const Raytracingrange, bool const Temporal) const;
// the parameters with the reflections off, for the other passes
static void clear(gl::scene_ubs &Scene);
// the steps of a ray at a level of gfx.ssr (1 low, 2 medium, 3 high), 0: off
static float steps(int const Level);
// the settings of the reflections in a debug window, with a line on the ray tracing (null: the renderer has none)
void draw_debug_ui(char const *Raytracing) const;
private:
struct frame
{
glm::dmat4 projection{1.0}; // as drawn
glm::dmat3 view{1.0};
glm::dvec3 camera{0.0};
glm::dmat4 cab{1.0}; // the cab to the world around the camera
TDynamicObject const *vehicle{nullptr};
glm::ivec2 size{0};
bool valid{false};
};
frame m_history; // of the image in the history
frame m_current; // of the frame begun last
glm::mat4 m_reprojection{1.f};
glm::mat4 m_reprojectioncab{1.f};
glm::mat4 m_cabbox{1.f};
bool m_hascab{false};
int m_levels{1};
unsigned int m_frame{0};
bool m_historyvalid{false};
};
} // namespace gfx