mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-09 01:20:32 +01:00
Take the ambient occlusion into the lighting and add a probe of the cab
This commit is contained in:
205
rendering/common/cabprobe.cpp
Normal file
205
rendering/common/cabprobe.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
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/cabprobe.h"
|
||||
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// a change of the light of the cab lamp larger than this share of it (the lamp switched, dimmed, the daylight it fades in) has the
|
||||
// probe drawn anew at once; smaller ones wait for the next update
|
||||
float const LAMP_CHANGED{0.05f};
|
||||
|
||||
float max_component(glm::vec3 const &Color)
|
||||
{
|
||||
return std::max(Color.r, std::max(Color.g, Color.b));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int cab_probe::size_of(int const Level)
|
||||
{
|
||||
switch (Level)
|
||||
{
|
||||
case 1:
|
||||
return 64;
|
||||
case 2:
|
||||
return 128;
|
||||
case 3:
|
||||
return 256;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cab_probe::allocate(int const Size)
|
||||
{
|
||||
release();
|
||||
if (Size <= 0)
|
||||
return;
|
||||
if (false == gpu_allocate(Size))
|
||||
{
|
||||
ErrorLog("cab probe framebuffer setup failed, no cab probe");
|
||||
release();
|
||||
Global.gfx_cabprobe = 0;
|
||||
m_level = 0;
|
||||
return;
|
||||
}
|
||||
m_size = Size;
|
||||
WriteLog("cab probe: " + std::to_string(Size) + " x " + std::to_string(Size) + " cube");
|
||||
}
|
||||
|
||||
void cab_probe::release()
|
||||
{
|
||||
gpu_release();
|
||||
m_size = 0;
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
bool cab_probe::update_wanted(TDynamicObject const *Vehicle, TModel3d const *Cab, glm::vec3 const &Center, glm::vec3 const &Extent, gfx::local_lights::light const *Lamp, unsigned int const Framestamp)
|
||||
{
|
||||
auto const level{std::clamp(Global.gfx_cabprobe, 0, 3)};
|
||||
if (level != m_level)
|
||||
{
|
||||
m_level = level;
|
||||
auto const size{size_of(level)};
|
||||
if (size != m_size)
|
||||
allocate(size);
|
||||
m_valid = false;
|
||||
}
|
||||
if ((m_size == 0) || (Vehicle == nullptr) || (Cab == nullptr) || (Extent == glm::vec3{0.f}))
|
||||
{
|
||||
// with no cab drawn nothing reads the probe; once there is one again it may be another
|
||||
m_valid = false;
|
||||
return false;
|
||||
}
|
||||
auto const lamp{Lamp != nullptr ? Lamp->color : glm::vec3{0.f}};
|
||||
auto const lampchange{glm::length(lamp - m_lamp)};
|
||||
auto const lampchanged{(lampchange > 0.f) && ((max_component(m_lamp) <= 0.f) || (max_component(lamp) <= 0.f) || (lampchange > LAMP_CHANGED * glm::length(m_lamp)))};
|
||||
if ((Vehicle != m_vehicle) || (Cab != m_cab) || (Center != m_center) || (Extent != m_extent) || lampchanged)
|
||||
{
|
||||
m_valid = false;
|
||||
}
|
||||
// the moving parts of the cab and the lights of the desk are followed every few frames
|
||||
if (m_valid && (Framestamp - m_drawnframe < static_cast<unsigned int>(std::max(Global.gfx_cabprobe_interval, 1))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_vehicle = Vehicle;
|
||||
m_cab = Cab;
|
||||
m_center = Center;
|
||||
m_extent = Extent;
|
||||
m_lamp = lamp;
|
||||
m_drawnframe = Framestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
void cab_probe::setup_drawing(gl::scene_ubs &Scene, gfx::local_lights::light const *Lamp, glm::vec3 const &Lamplocal, bool const Shadows, glm::vec4 const &Shadowweights) const
|
||||
{
|
||||
if (Lamp == nullptr)
|
||||
{
|
||||
Scene.cab_probe_lamp = glm::vec4{0.f};
|
||||
Scene.cab_probe_lamp_color = glm::vec4{0.f};
|
||||
Scene.cab_probe_lamp_shadow = glm::vec4{0.f};
|
||||
return;
|
||||
}
|
||||
// as the light buffer has it (Write_Local_Light()): the shading takes the light to end at 1 / sqrt(quadratic)
|
||||
Scene.cab_probe_lamp = glm::vec4{Lamplocal - m_center, 1.f / sq(std::max(Lamp->range, 0.01f))};
|
||||
Scene.cab_probe_lamp_color = glm::vec4{Lamp->color, Shadows ? 1.f : 0.f};
|
||||
Scene.cab_probe_lamp_shadow = Shadowweights;
|
||||
}
|
||||
|
||||
void cab_probe::begin()
|
||||
{
|
||||
m_cpustart = std::chrono::steady_clock::now();
|
||||
gpu_begin();
|
||||
}
|
||||
|
||||
void cab_probe::end(std::uint32_t const Drawcalls)
|
||||
{
|
||||
gpu_end();
|
||||
m_valid = true;
|
||||
m_drawcalls = Drawcalls;
|
||||
++m_updatecount;
|
||||
auto const elapsed{std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - m_cpustart).count()};
|
||||
m_cputime = (m_cputime == 0.0 ? elapsed : m_cputime * 0.9 + elapsed * 0.1);
|
||||
}
|
||||
|
||||
glm::dmat4 cab_probe::model_to_probe() const
|
||||
{
|
||||
return glm::translate(glm::dmat4{1.0}, -glm::dvec3{m_center});
|
||||
}
|
||||
|
||||
void cab_probe::setup_scene(gl::scene_ubs &Scene, TDynamicObject const *Vehicle, glm::dvec3 const &Camera, glm::mat3 const &View) const
|
||||
{
|
||||
if (false == ready(Vehicle))
|
||||
{
|
||||
clear_scene(Scene);
|
||||
return;
|
||||
}
|
||||
// view space to the space of the probe: the space of the cab model, with the origin in the middle of its box. the vehicle is where
|
||||
// it is now, the probe stays right as it moves
|
||||
auto const toworld{glm::dmat4{glm::transpose(glm::dmat3{View})}};
|
||||
auto const toprobe{model_to_probe() * glm::inverse(Vehicle->mMatrix) * glm::translate(glm::dmat4{1.0}, Camera - Vehicle->GetPosition()) * toworld};
|
||||
Scene.cab_probe_view = glm::mat4{toprobe};
|
||||
Scene.cab_probe_box = glm::vec4{m_extent, 1.f};
|
||||
auto const levels{static_cast<float>(std::floor(std::log2(static_cast<float>(std::max(m_size, 1)))))};
|
||||
Scene.cab_probe_params = glm::vec4{Global.gfx_cabprobe_bounce, Global.gfx_cabprobe_reflections ? 1.f : 0.f, levels, 0.f};
|
||||
}
|
||||
|
||||
void cab_probe::clear_scene(gl::scene_ubs &Scene)
|
||||
{
|
||||
Scene.cab_probe_view = glm::mat4{1.f};
|
||||
Scene.cab_probe_box = glm::vec4{0.f};
|
||||
Scene.cab_probe_params = glm::vec4{0.f};
|
||||
}
|
||||
|
||||
int cab_probe::sh_level() const
|
||||
{
|
||||
auto level{0};
|
||||
while ((m_size >> level) > static_cast<int>(gl::CAB_PROBE_SH_SIZE))
|
||||
++level;
|
||||
return level;
|
||||
}
|
||||
|
||||
int cab_probe::sh_size() const
|
||||
{
|
||||
return std::max(m_size >> sh_level(), 1);
|
||||
}
|
||||
|
||||
std::string cab_probe::info() const
|
||||
{
|
||||
if (m_size == 0)
|
||||
return "cab probe: off";
|
||||
return "cab probe: " + std::to_string(m_size) + " px, " + (m_valid ? "ready" : "none") + ", draw calls " + std::to_string(m_drawcalls) + ", cpu " + to_string(m_cputime, 2) + " ms, updates " +
|
||||
std::to_string(m_updatecount);
|
||||
}
|
||||
|
||||
void cab_probe::draw_debug_ui()
|
||||
{
|
||||
char const *levels[]{"off", "low (64)", "medium (128)", "high (256)"};
|
||||
ImGui::Combo("cab probe (gfx.cabprobe)", &Global.gfx_cabprobe, levels, IM_ARRAYSIZE(levels));
|
||||
if (Global.gfx_cabprobe <= 0)
|
||||
return;
|
||||
ImGui::SliderInt("frames between its updates (gfx.cabprobe.interval)", &Global.gfx_cabprobe_interval, 1, 60);
|
||||
ImGui::SliderFloat("light bounced around the cab (gfx.cabprobe.bounce)", &Global.gfx_cabprobe_bounce, 0.f, 4.f);
|
||||
ImGui::Checkbox("the cab reflects itself (gfx.cabprobe.reflections)", &Global.gfx_cabprobe_reflections);
|
||||
ImGui::TextUnformatted(info().c_str());
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
116
rendering/common/cabprobe.h
Normal file
116
rendering/common/cabprobe.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "gl/ubo.h"
|
||||
#include "rendering/common/locallights.h"
|
||||
|
||||
class TDynamicObject;
|
||||
class TModel3d;
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
// the probe of the occupied cab (Global.gfx_cabprobe, the stage A4 of claude/renderers-plan.md), the part shared by the renderers;
|
||||
// the backend (opengl46_cab_probe, vk14::cab_probe_maps) keeps the maps and draws to them through the gpu_* methods.
|
||||
// - two cube maps drawn from the middle of the box of the cab model (mdKabina), in the space of the model, so they stay right while
|
||||
// the vehicle moves and turns: the light of the cab lamp the cab reflects (with the shadow map of the lamp) and the light of what
|
||||
// glows in it, and the albedo of the cab. both are cleared to nothing and premultiplied by the share of the direction the cab
|
||||
// fills, which is in their alpha (the glasses aren't drawn: through them the cab shows nothing), and have mipmaps;
|
||||
// - the six faces are drawn at once (a geometry shader with opengl, multiview with vulkan) by the programs of the probe
|
||||
// (cabprobe.vert, cabprobe.frag), every few frames (Global.gfx_cabprobe_interval: the moving parts of the cab, the lights of the
|
||||
// desk) and at once when the cab, its box, the level or the cab lamp change; the light reflected is then made into spherical
|
||||
// harmonics (cabprobe_sh.comp, gl::cab_probe_data);
|
||||
// - the shading of the parts of the cab (gl::model_ubs::cab_interior) reads them through gl::scene_ubs::cab_probe_*: the reflected
|
||||
// light lights the cab again, in place of the share of the light of the lamp the ambient light kept (Global.gfx_lights_cab_bounce),
|
||||
// and the cab reflects itself where the screen space reflections don't find it, the box standing for its walls; what the cab
|
||||
// shows there is the light of the lamp it reflects and its albedo lit by the ambient light of the reflecting surface.
|
||||
// the ambient light and the sun are left out of the probe: the cab gets them as before
|
||||
class cab_probe
|
||||
{
|
||||
public:
|
||||
cab_probe() = default;
|
||||
virtual ~cab_probe() = default;
|
||||
cab_probe(const cab_probe &) = delete;
|
||||
cab_probe &operator=(const cab_probe &) = delete;
|
||||
|
||||
// size of a face of the maps of the level (Global.gfx_cabprobe), 0 for none
|
||||
static int size_of(int const Level);
|
||||
// whether the probe is to be drawn in this frame. Vehicle: the occupied vehicle whose cab is drawn (nullptr: none), Cab its cab
|
||||
// model, Center and Extent the box of the model (model space); Lamp the cab lamp when it's a light of the frame (nullptr: none).
|
||||
// (re)makes the maps when the level changed
|
||||
bool update_wanted(TDynamicObject const *Vehicle, TModel3d const *Cab, glm::vec3 const &Center, glm::vec3 const &Extent, gfx::local_lights::light const *Lamp, unsigned int const Framestamp);
|
||||
// the fields of the block of the scene the drawing of the probe reads: the cab lamp (Lamp, at Lamplocal in the space of the cab
|
||||
// model, nullptr: none) with its shadow map when Shadows (Shadowweights: the headlight_weights of the map)
|
||||
void setup_drawing(gl::scene_ubs &Scene, gfx::local_lights::light const *Lamp, glm::vec3 const &Lamplocal, bool const Shadows, glm::vec4 const &Shadowweights) const;
|
||||
// the drawing to the probe: the target, the viewport, the cleared faces, the state of the faces. the cab is to be drawn with the
|
||||
// programs of the probe and a modelview from the space of the cab model to the space of the probe (model_to_probe())
|
||||
void begin();
|
||||
// ends the drawing: the mipmaps and the spherical harmonics of the light
|
||||
void end(std::uint32_t const Drawcalls);
|
||||
// the space of the cab model to the space of the probe: the axes of the model, the origin in the middle of the box
|
||||
glm::dmat4 model_to_probe() const;
|
||||
// whether the probe holds the cab of Vehicle
|
||||
bool ready(TDynamicObject const *Vehicle) const
|
||||
{
|
||||
return m_valid && (Vehicle != nullptr) && (Vehicle == m_vehicle);
|
||||
}
|
||||
// the fields of the block of the scene the colour pass of the main view reads the probe with: the cab of Vehicle drawn, seen from
|
||||
// Camera (world space) with the rotation of the view View. cleared when the probe doesn't hold the cab
|
||||
void setup_scene(gl::scene_ubs &Scene, TDynamicObject const *Vehicle, glm::dvec3 const &Camera, glm::mat3 const &View) const;
|
||||
// the fields cleared, for the passes other than the colour pass of the main view
|
||||
static void clear_scene(gl::scene_ubs &Scene);
|
||||
// size of a face of the maps, 0 when there are none
|
||||
int size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
// the level of the mipmaps the spherical harmonics are taken from, and its size
|
||||
int sh_level() const;
|
||||
int sh_size() const;
|
||||
// line for the statistics of the debug panel
|
||||
std::string info() const;
|
||||
// controls and counters for the debug window
|
||||
void draw_debug_ui();
|
||||
|
||||
protected:
|
||||
// the maps of Size x Size faces with their mipmaps; false when they can't be made
|
||||
virtual bool gpu_allocate(int const Size) = 0;
|
||||
virtual void gpu_release() = 0;
|
||||
virtual void gpu_begin() = 0;
|
||||
virtual void gpu_end() = 0;
|
||||
|
||||
int m_size{0};
|
||||
|
||||
private:
|
||||
void allocate(int const Size);
|
||||
void release();
|
||||
|
||||
int m_level{-1};
|
||||
bool m_valid{false};
|
||||
TDynamicObject const *m_vehicle{nullptr};
|
||||
TModel3d const *m_cab{nullptr};
|
||||
glm::vec3 m_center{0.f};
|
||||
glm::vec3 m_extent{0.f};
|
||||
// the light of the cab lamp the probe was drawn with, 0 without the lamp
|
||||
glm::vec3 m_lamp{0.f};
|
||||
unsigned int m_drawnframe{0};
|
||||
// statistics
|
||||
std::uint32_t m_drawcalls{0};
|
||||
std::uint32_t m_updatecount{0};
|
||||
double m_cputime{0.0};
|
||||
std::chrono::steady_clock::time_point m_cpustart;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
@@ -502,6 +502,18 @@ gfx::local_lights::section_data const &gfx::local_lights::model_bounds(TModel3d
|
||||
return m_models.emplace(Model, data).first->second;
|
||||
}
|
||||
|
||||
bool gfx::local_lights::cab_bounds(TModel3d *Cab, gfx::geometrybank_manager const &Geometry, glm::vec3 &Center, glm::vec3 &Extent)
|
||||
{
|
||||
if (Cab == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto const &bounds{model_bounds(Cab, Geometry)};
|
||||
Center = bounds.center;
|
||||
Extent = bounds.extent;
|
||||
return (bounds.extent != glm::vec3{0.f});
|
||||
}
|
||||
|
||||
std::string gfx::local_lights::info() const
|
||||
{
|
||||
return "local lights: " + std::to_string(m_lights.size()) + " (scenery " + std::to_string(m_scenerycount) + ", vehicles " + std::to_string(m_vehiclecount) + ", compartments " +
|
||||
|
||||
@@ -85,6 +85,8 @@ class local_lights
|
||||
{
|
||||
return m_cablamplocal;
|
||||
}
|
||||
// the box of the cab model Cab (model space): its middle and half its size; false when the model has no geometry
|
||||
bool cab_bounds(TModel3d *Cab, gfx::geometrybank_manager const &Geometry, glm::vec3 &Center, glm::vec3 &Extent);
|
||||
// counts of the last pass which wrote the lights to the light buffer: placed, and left out for the lack of room
|
||||
void placed(std::uint32_t const Placed, std::uint32_t const Dropped)
|
||||
{
|
||||
|
||||
@@ -1792,10 +1792,14 @@ bool scene_walk::Render_cab(TDynamicObject const *Dynamic, float const Lightleve
|
||||
auto const luminance { Global.fLuminance * ( Dynamic->fShade > 0.0f ? Dynamic->fShade : 1.0f ) };
|
||||
if( Lightlevel > 0.f ) {
|
||||
// crude way to light the cabin, until we have something more complete in place. with the lamp of the cab among the
|
||||
// lights a share of it is kept for the light which bounces around the cab
|
||||
auto const share { m_cablamplit ? Global.gfx_lights_cab_bounce : 1.f };
|
||||
walk_ambient_add( ( Dynamic->InteriorLight * Lightlevel ) * std::clamp( 1.25f - (float)luminance, 0.f, 1.f ) * share );
|
||||
// lights a share of it is kept for the light which bounces around the cab, unless the probe of the cab gives that light
|
||||
auto const share { m_cablamplit ? ( m_cabprobelit ? 0.f : Global.gfx_lights_cab_bounce ) : 1.f };
|
||||
if( share > 0.f ) {
|
||||
walk_ambient_add( ( Dynamic->InteriorLight * Lightlevel ) * std::clamp( 1.25f - (float)luminance, 0.f, 1.f ) * share );
|
||||
}
|
||||
}
|
||||
// the parts of the cab read its probe
|
||||
model_ubs.cab_interior = 1.f;
|
||||
|
||||
// render
|
||||
if (true == Alpha)
|
||||
@@ -1809,6 +1813,7 @@ bool scene_walk::Render_cab(TDynamicObject const *Dynamic, float const Lightleve
|
||||
Render(Dynamic->mdKabina, Dynamic->Material(), 0.0);
|
||||
}
|
||||
// post-render restore
|
||||
model_ubs.cab_interior = 0.f;
|
||||
walk_ambient_restore();
|
||||
walk_sunlight();
|
||||
|
||||
|
||||
@@ -512,6 +512,9 @@ class scene_walk
|
||||
bool m_previewing{false};
|
||||
// the last lighting of the pass placed the cab lamp among the lights: the cab is lit by it, not by an addition to the ambient light
|
||||
bool m_cablamplit{false};
|
||||
// the colour pass reads the probe of the cab (gfx::cab_probe): the light of the cab lamp the cab reflects lights it again from the
|
||||
// probe, so the ambient light keeps no share of the lamp
|
||||
bool m_cabprobelit{false};
|
||||
// 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};
|
||||
|
||||
Reference in New Issue
Block a user