From e8a90507f065a0cd05614e1abd03361dd1773cc6 Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Wed, 28 Oct 2026 12:19:43 +0100 Subject: [PATCH] Light the cab with the sun coming in through its windows --- docs/opengl46-renderer.md | 21 +++++++- gl/glsl_common.cpp | 1 + gl/ubo.h | 25 ++++++++-- rendering/common/cabprobe.cpp | 65 +++++++++++++++++++++++-- rendering/common/cabprobe.h | 22 ++++++++- rendering/common/scenewalk.cpp | 5 ++ rendering/opengl46/opengl46cabprobe.cpp | 13 ++++- rendering/opengl46/opengl46cabprobe.h | 6 ++- rendering/opengl46/opengl46renderer.cpp | 6 ++- rendering/vulkan14/vulkan14ao.cpp | 2 +- rendering/vulkan14/vulkan14bindings.h | 4 +- rendering/vulkan14/vulkan14cabprobe.cpp | 54 +++++++++++++++++++- rendering/vulkan14/vulkan14cabprobe.h | 17 +++++++ rendering/vulkan14/vulkan14frame.cpp | 2 +- rendering/vulkan14/vulkan14scene.cpp | 4 ++ rendering/vulkan14/vulkan14shaders.cpp | 1 + shaders/ogl46/cabprobe.frag | 3 ++ shaders/ogl46/cabprobe.glsl | 29 ++++++++++- shaders/ogl46/cabprobe_alpha.frag | 3 ++ shaders/ogl46/common.glsl | 10 +++- shaders/ogl46/light_common.glsl | 10 +++- shaders/vk14/cabprobe.frag | 3 ++ shaders/vk14/cabprobe.glsl | 29 ++++++++++- shaders/vk14/cabprobe_alpha.frag | 3 ++ shaders/vk14/common.glsl | 10 +++- shaders/vk14/light_common.glsl | 10 +++- utilities/Globals.cpp | 6 +++ utilities/Globals.h | 7 +++ 28 files changed, 342 insertions(+), 29 deletions(-) diff --git a/docs/opengl46-renderer.md b/docs/opengl46-renderer.md index bcffd20e..4a2f72fd 100644 --- a/docs/opengl46-renderer.md +++ b/docs/opengl46-renderer.md @@ -149,6 +149,7 @@ is the cheaper setting. | `gfx.postfx.ao.radius` | `1.5` | 0.1..10: reach of the occluders, metres | | `gfx.postfx.ao.strength` | `1.0` | 0..4: power the occlusion is raised to; 1 = as computed, 0 = none | | `gfx.postfx.ao.bounce` | `0.3` | 0..1: albedo of the surfaces assumed for the light that comes back after more bounces (brightens the occlusion a little); over the image only, in the light of the surfaces their own albedo is taken | +| `gfx.postfx.ao.cab` | `0.5` | 0..1: share of the occlusion the parts of the cab and the low poly interiors take (with `lighting`): walls and things are near all around in them and the whole occlusion darkens them too much; the light which bounces inside them comes from the probe of the cab (`gfx.cabprobe.sun`) or `gfx.interior.bounce`. 1: all of it, as before | | `gfx.postfx.ao.lighting yes/no` | `yes` | with `gtao`: the occlusion darkens the ambient light and the reflection of the surroundings in the shading (needs the depth of the scene before its colour); `no`: it darkens the finished image | | `gfx.postfx.ao.quality slices steps` | `3 4` | directions searched around each pixel (1..8) and samples on each side of a direction (1..16) | @@ -257,8 +258,22 @@ parts of the cab (as the shadow map of the lamp). The parts of the cab read them (the walls), the cab shows the light of the lamp it reflects and its albedo lit by the ambient light, through the windows the sky is seen as before. -The ambient light and the sun are left out of the probe, the cab gets them as before. `gfx.cabprobe no` is the old way (the -share of the lamp in the ambient light, the sky in the reflections). Cost: one draw of the opaque cab (about the draw calls of +The sun (the moon at night) lights the probe too, where it comes in through the windows (`gfx.cabprobe.sun`): the light the cab +lights itself with again is then also the sunlight bouncing off the desk, the floor and the walls where it falls, and the cab +reflects the sunlit spots. Where the windows are the probe drawn last tells: a small level of its albedo (32 pixels a face at most, +the mask) is copied after each drawing; from a point of the cab the ray towards the sun is taken to the box of the cab and the +mask, seen from the middle of the box, says whether the cab is there or a window (what is in the way inside the cab, the desk or +a seat, holds the sun back only where it hides that point from the middle: a coarse shadow, enough for the light that bounces). +The sun is held back by the scenery around the cab (`gfx.skyvisibility`, or the environment flags with `gfx.tunnelflags yes`) +and by the shadows of the clouds, as outside. A cab drawn for the first time (another vehicle, another cab) has no mask yet, +so its probe is drawn without the sun and again with it in the next frame. The cab gets the sun itself from the shading as +before (the shadow map); the ambient light is left out of the probe, the cab gets it as before. `gfx.cabprobe no` is the old way +(the share of the lamp in the ambient light, the sky in the reflections, `gfx.interior.bounce` for the sun). + +**The low poly interiors** of the vehicles (and the cab when it has no probe) get the sun which comes in through their windows +and bounces around them as a share of the light of the sun outside (`gfx.interior.bounce`), held back by the scenery and the +clouds like the sun; the interior isn't known well enough for more. Both kinds of interiors take only a share of the ambient +occlusion of the view (`gfx.postfx.ao.cab`). Cost: one draw of the opaque cab (about the draw calls of the cab in the colour pass) every `interval` frames, 2 cube maps of the size of the level with their mipmaps and the depth (64: 0.5 MB, 128: 2 MB, 256: 8 MB), two texture reads in the reflections of the cab. @@ -268,6 +283,8 @@ the reflections of the cab. | `gfx.cabprobe.interval` | `4` | 1..120: frames between the updates of the probe | | `gfx.cabprobe.bounce` | `1.0` | 0..4: share of the light of the lamp the cab reflects which lights it again | | `gfx.cabprobe.reflections yes/no` | `yes` | the cab reflects itself where the screen space reflections find nothing; `no`: the sky, as before | +| `gfx.cabprobe.sun` | `1.0` | 0..4: the sun lights the probe where it comes in through the windows, so its light bounces around the cab; 0: no sun in the probe, as before | +| `gfx.interior.bounce` | `0.1` | 0..1: share of the light of the sun outside the low poly interiors (and the cab without its probe) get as the light which comes in through their windows and bounces around them; 0: none, as before | ### Tunnels, bridges and roofs: the occlusion of the sky diff --git a/gl/glsl_common.cpp b/gl/glsl_common.cpp index 3c3f0b5e..0ccc30d9 100644 --- a/gl/glsl_common.cpp +++ b/gl/glsl_common.cpp @@ -100,6 +100,7 @@ void gl::glsl_common_setup() "#define SCENE_AO_TEX " + std::to_string(SCENE_AO_TEX) + "\n" + "#define CAB_PROBE_TEX " + std::to_string(CAB_PROBE_TEX) + "\n" + "#define CAB_PROBE_ALBEDO_TEX " + std::to_string(CAB_PROBE_ALBEDO_TEX) + "\n" + + "#define CAB_PROBE_MASK_TEX " + std::to_string(CAB_PROBE_MASK_TEX) + "\n" + "#define SKY_OCCLUSION_HEIGHT_TEX " + std::to_string(SKY_OCCLUSION_HEIGHT_TEX) + "\n" + "#define SKY_OCCLUSION_TEX " + std::to_string(SKY_OCCLUSION_TEX) + "\n" + "#define CAB_PROBE_SSBO_BINDING " + std::to_string(CAB_PROBE_SSBO_BINDING) + "\n" + diff --git a/gl/ubo.h b/gl/ubo.h index 418c399a..af4142f6 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -97,6 +97,9 @@ namespace gl // texel with textureGather. bound directly, like the ones above const size_t SKY_OCCLUSION_HEIGHT_TEX = MAX_TEXTURES + HELPER_TEXTURES + 17; const size_t SKY_OCCLUSION_TEX = MAX_TEXTURES + HELPER_TEXTURES + 18; + // the probe of the cab drawn last, a small level of its albedo, which the drawing of the probe reads to tell where the windows of + // the cab are (gfx::cab_probe, gl::scene_ubs::cab_probe_sun). bound directly, like the ones above + const size_t CAB_PROBE_MASK_TEX = MAX_TEXTURES + HELPER_TEXTURES + 19; struct scene_ubs { @@ -138,14 +141,16 @@ namespace gl // screen doesn't hold (the vulkan renderer, gfx.raytracing), w: the longest traced ray, metres glm::vec4 ssr_params2; // the reflection of the surroundings the screen space reflections and the ray tracing don't find, for every pass: x: albedo - // of the ground below the horizon of the environment map (gfx.ambient.groundalbedo), yzw unused + // of the ground below the horizon of the environment map (gfx.ambient.groundalbedo), y used by the vulkan renderer (the + // foliage traced), z: the share of the light of the sun the low poly interiors (and the cab without its probe) get as the light + // which comes in through their windows and bounces around them (gfx.interior.bounce), w unused glm::vec4 env_params; // the water on the windscreens (mat_rain_windscreen): xyz the velocity of the air against the vehicle of the camera (the // share of the wind the particles of the rain drift with, less the velocity of the vehicle), world space, m/s; w unused glm::vec4 rain_wind; // the ambient occlusion in the light of the surfaces (gfx.postfx.ao.lighting), set only for the colour pass of the main view, // zero elsewhere: x 1 when SCENE_AO_TEX holds the occlusion of the depth in SCENE_DEPTH_TEX, y the power it's raised to - // (gfx.postfx.ao.strength), zw unused + // (gfx.postfx.ao.strength), z the share of it the cab and the low poly interiors take (gfx.postfx.ao.cab), w unused glm::vec4 ao_params; // the probe of the occupied cab (gfx::cab_probe), set only for the colour pass of the main view, zero elsewhere: the view // space of the pass to the space of the probe (the axes of the cab model, metres, the origin in the middle of its box) @@ -169,9 +174,18 @@ namespace gl glm::vec4 sky_occlusion_params; // xy the middle of the maps (the main camera they follow) less the camera of the pass, world x and z, metres; zw unused glm::vec4 sky_occlusion_center; + // the drawing of the probe of the cab (cabprobe.glsl), set for it only: the sun (the moon) coming in through the windows. xyz + // the direction towards it in the space of the probe, w 1 when it lights the probe (CAB_PROBE_MASK_TEX holds the cab); rgb its + // light, held back by the scenery around the cab (gfx::sky_occlusion) and scaled by gfx.cabprobe.sun, a unused; xyz half the + // size of the box of the cab, w unused; xyz the middle of the box less the camera of the main view, world space (the shadows + // of the clouds), w unused + glm::vec4 cab_probe_sun; + glm::vec4 cab_probe_sun_color; + glm::vec4 cab_probe_sun_box; + glm::vec4 cab_probe_sun_world; }; - static_assert(sizeof(scene_ubs) == 912 + 80 * MAX_HEADLIGHT_SHADOWS, "bad size of ubs"); + static_assert(sizeof(scene_ubs) == 976 + 80 * MAX_HEADLIGHT_SHADOWS, "bad size of ubs"); const size_t MAX_PARAMS = 3; @@ -187,7 +201,8 @@ namespace gl float fog_density; float alpha_mult; float shadow_tone; - // 1 for the parts of the occupied cab, which the probe of the cab lights and reflects (gl::scene_ubs::cab_probe_box) + // 1 for the parts of the occupied cab, which the probe of the cab lights and reflects (gl::scene_ubs::cab_probe_box), 2 for the + // low poly interiors of the vehicles (gl::scene_ubs::env_params) float cab_interior{0.f}; UBS_PAD(8); @@ -421,6 +436,8 @@ namespace gl const GLuint CAB_PROBE_SSBO_BINDING = 17; // the face of the level of the probe the spherical harmonics are taken from, at most, and the work group of cabprobe_sh.comp const size_t CAB_PROBE_SH_SIZE = 16; + // the face of the level of the probe CAB_PROBE_MASK_TEX is a copy of, at most + const size_t CAB_PROBE_MASK_SIZE = 32; const size_t CAB_PROBE_SH_GROUP_SIZE = 64; static_assert(sizeof(sky_data) == 144, "bad size of sky_data"); diff --git a/rendering/common/cabprobe.cpp b/rendering/common/cabprobe.cpp index 5c3dd749..ec89a3b8 100644 --- a/rendering/common/cabprobe.cpp +++ b/rendering/common/cabprobe.cpp @@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "rendering/common/cabprobe.h" +#include "rendering/common/skyocclusion.h" #include "utilities/Globals.h" #include "utilities/Logs.h" #include "utilities/utilities.h" @@ -69,6 +70,7 @@ void cab_probe::release() gpu_release(); m_size = 0; m_valid = false; + m_masked = 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) @@ -81,22 +83,31 @@ bool cab_probe::update_wanted(TDynamicObject const *Vehicle, TModel3d const *Cab if (size != m_size) allocate(size); m_valid = false; + m_masked = 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; + m_masked = 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) + if ((Vehicle != m_vehicle) || (Cab != m_cab) || (Center != m_center) || (Extent != m_extent)) + { + // the mask is of another cab + m_valid = false; + m_masked = false; + } + if (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(std::max(Global.gfx_cabprobe_interval, 1)))) + // the moving parts of the cab, the lights of the desk and the sun are followed every few frames; a probe drawn without its mask + // is drawn again at once + if (m_valid && (false == m_settle) && (Framestamp - m_drawnframe < static_cast(std::max(Global.gfx_cabprobe_interval, 1)))) { return false; } @@ -124,6 +135,37 @@ void cab_probe::setup_drawing(gl::scene_ubs &Scene, gfx::local_lights::light con Scene.cab_probe_lamp_shadow = Shadowweights; } +void cab_probe::setup_sun(gl::scene_ubs &Scene, TDynamicObject const *Vehicle, glm::vec3 const &Tosun, glm::vec3 const &Sunlight, glm::dvec3 const &Camera) const +{ + Scene.cab_probe_sun = glm::vec4{0.f}; + Scene.cab_probe_sun_color = glm::vec4{0.f}; + Scene.cab_probe_sun_box = glm::vec4{m_extent, 0.f}; + Scene.cab_probe_sun_world = glm::vec4{0.f}; + if ((Vehicle == nullptr) || (false == m_masked) || (Global.gfx_cabprobe_sun <= 0.f) || (glm::length(Tosun) <= 0.f)) + return; + auto const rotation{glm::dmat3{Vehicle->mMatrix}}; + auto const middle{Vehicle->GetPosition() + rotation * glm::dvec3{m_center}}; + // the scenery around the cab holds the sun back as it does in the shading (the vehicle's light level of the environment flags with + // Global.gfx_tunnelflags, as walk_sunlight() takes it in Render_cab()) + auto share{1.f}; + if (Global.gfx_tunnelflags) + { + auto const shade{vehicle_shade(Vehicle)}; + share = (shade > 0.f ? shade : 1.f); + } + else if (auto const *occlusion{sky_occlusion::current()}) + { + share = std::clamp(occlusion->sample(middle).y, 0.f, 1.f); + } + auto const light{Sunlight * (share * Global.gfx_cabprobe_sun)}; + if (max_component(light) <= 0.f) + return; + auto const tosun{glm::normalize(glm::vec3{glm::inverse(rotation) * glm::dvec3{Tosun}})}; + Scene.cab_probe_sun = glm::vec4{tosun, 1.f}; + Scene.cab_probe_sun_color = glm::vec4{light, 0.f}; + Scene.cab_probe_sun_world = glm::vec4{glm::vec3{middle - Camera}, 0.f}; +} + void cab_probe::begin() { m_cpustart = std::chrono::steady_clock::now(); @@ -133,6 +175,9 @@ void cab_probe::begin() void cab_probe::end(std::uint32_t const Drawcalls) { gpu_end(); + // the mask was copied from this drawing: a drawing without it is made again with it + m_settle = (false == m_masked); + m_masked = true; m_valid = true; m_drawcalls = Drawcalls; ++m_updatecount; @@ -182,6 +227,19 @@ int cab_probe::sh_size() const return std::max(m_size >> sh_level(), 1); } +int cab_probe::mask_level() const +{ + auto level{0}; + while ((m_size >> level) > static_cast(gl::CAB_PROBE_MASK_SIZE)) + ++level; + return level; +} + +int cab_probe::mask_size() const +{ + return std::max(m_size >> mask_level(), 1); +} + std::string cab_probe::info() const { if (m_size == 0) @@ -199,6 +257,7 @@ void cab_probe::draw_debug_ui() 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::SliderFloat("sun through the windows (gfx.cabprobe.sun)", &Global.gfx_cabprobe_sun, 0.f, 4.f); ImGui::TextUnformatted(info().c_str()); } diff --git a/rendering/common/cabprobe.h b/rendering/common/cabprobe.h index 40bc5548..3c9eb882 100644 --- a/rendering/common/cabprobe.h +++ b/rendering/common/cabprobe.h @@ -36,7 +36,12 @@ namespace gfx // 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 +// the ambient light is left out of the probe, the cab gets it as before. the sun (the moon) lights the probe where it comes in +// through the windows (Global.gfx_cabprobe_sun), so the light which bounces around the cab is also the light of the sun where it +// falls in the cab; the cab gets the sun itself from the shading, as before. where the windows are the probe drawn last tells: +// a small level of its albedo is copied after each drawing (the mask, CAB_PROBE_MASK_TEX), and the ray towards the sun is taken +// from a point of the cab to the box of the cab and looked up in it. the first drawing of a cab has no mask yet, so it's drawn +// without the sun and again in the next frame class cab_probe { public: @@ -54,6 +59,10 @@ class cab_probe // 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 fields of the block of the scene the drawing of the probe takes the sun with: Vehicle the vehicle whose cab is drawn, Tosun the + // direction towards the sun (the moon), world space, Sunlight its light as the shading gets it (the colour of the light buffer times + // its intensity), Camera the camera of the main view (world space). no sun until the mask holds the cab + void setup_sun(gl::scene_ubs &Scene, TDynamicObject const *Vehicle, glm::vec3 const &Tosun, glm::vec3 const &Sunlight, glm::dvec3 const &Camera) 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(); @@ -79,6 +88,14 @@ class cab_probe // the level of the mipmaps the spherical harmonics are taken from, and its size int sh_level() const; int sh_size() const; + // the level of the mipmaps of the albedo the mask is a copy of, and its size + int mask_level() const; + int mask_size() const; + // whether the mask holds the cab being drawn (the drawing of the probe reads it then) + bool masked() const + { + return m_masked; + } // line for the statistics of the debug panel std::string info() const; // controls and counters for the debug window @@ -99,6 +116,9 @@ class cab_probe int m_level{-1}; bool m_valid{false}; + // the mask holds the cab drawn; the probe was drawn without it and is drawn again in the next frame + bool m_masked{false}; + bool m_settle{false}; TDynamicObject const *m_vehicle{nullptr}; TModel3d const *m_cab{nullptr}; glm::vec3 m_center{0.f}; diff --git a/rendering/common/scenewalk.cpp b/rendering/common/scenewalk.cpp index c2caa3c5..1f1e2ff4 100644 --- a/rendering/common/scenewalk.cpp +++ b/rendering/common/scenewalk.cpp @@ -174,12 +174,15 @@ bool scene_walk::Render_lowpoly( TDynamicObject *Dynamic, float const Squaredist 1.f ) - luminance, 0.f, 1.f ) ); + // the sun coming in through the windows bounces around the interior + model_ubs.cab_interior = 2.f; Render( Dynamic->mdLowPolyInt, Dynamic->Material(), Squaredistance ); // HACK: if the model has low poly interior, we presume the load is placed inside and also affected by reduced light level if( Dynamic->mdLoad ) { // renderowanie nieprzezroczystego Ĺ‚adunku Render( Dynamic->mdLoad, Dynamic->Material(), Squaredistance, { 0.f, Dynamic->LoadOffset, 0.f }, {} ); } + model_ubs.cab_interior = 0.f; walk_sunlight( gfx::vehicle_shade( Dynamic ) > 0.f ? gfx::vehicle_shade( Dynamic ) : 1.f ); } else { @@ -2886,7 +2889,9 @@ bool scene_walk::Render_Alpha(TDynamicObject *Dynamic) // render if( Dynamic->mdLowPolyInt ) { // low poly interior + model_ubs.cab_interior = 2.f; Render_Alpha( Dynamic->mdLowPolyInt, Dynamic->Material(), squaredistance ); + model_ubs.cab_interior = 0.f; } if( Dynamic->mdModel ) { // main model diff --git a/rendering/opengl46/opengl46cabprobe.cpp b/rendering/opengl46/opengl46cabprobe.cpp index 93332899..09afd9b2 100644 --- a/rendering/opengl46/opengl46cabprobe.cpp +++ b/rendering/opengl46/opengl46cabprobe.cpp @@ -82,6 +82,7 @@ void opengl46_cab_probe::bind() { glBindTextureUnit(gl::CAB_PROBE_TEX, (m_light != 0) ? m_light : m_empty); glBindTextureUnit(gl::CAB_PROBE_ALBEDO_TEX, (m_albedo != 0) ? m_albedo : m_empty); + glBindTextureUnit(gl::CAB_PROBE_MASK_TEX, (m_mask != 0) ? m_mask : m_empty); if (m_sh) m_sh->bind_base(); } @@ -91,6 +92,13 @@ bool opengl46_cab_probe::gpu_allocate(int const Size) m_light = make_cube(GL_RGBA16F, Size, true); m_albedo = make_cube(GL_RGBA8, Size, true); m_depth = make_cube(GL_DEPTH_COMPONENT32F, Size, false); + // the mask: the level of the albedo of at most CAB_PROBE_MASK_SIZE pixels a face, as mask_size() has it once the size is set + auto masksize{Size}; + while (masksize > static_cast(gl::CAB_PROBE_MASK_SIZE)) + masksize >>= 1; + m_mask = make_cube(GL_RGBA8, std::max(masksize, 1), false); + glTextureParameteri(m_mask, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_mask, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // all the faces at once, the geometry shader picks the face glCreateFramebuffers(1, &m_framebuffer); glNamedFramebufferTexture(m_framebuffer, GL_COLOR_ATTACHMENT0, m_light, 0); @@ -106,7 +114,7 @@ void opengl46_cab_probe::gpu_release() { if (m_framebuffer != 0) glDeleteFramebuffers(1, &m_framebuffer); - for (auto *texture : {&m_light, &m_albedo, &m_depth}) + for (auto *texture : {&m_light, &m_albedo, &m_depth, &m_mask}) { if (*texture != 0) glDeleteTextures(1, texture); @@ -143,6 +151,9 @@ void opengl46_cab_probe::gpu_end() gl::framebuffer::unbind(); glGenerateTextureMipmap(m_light); glGenerateTextureMipmap(m_albedo); + // where the windows are, for the next drawing + auto const masksize{mask_size()}; + glCopyImageSubData(m_albedo, GL_TEXTURE_CUBE_MAP, mask_level(), 0, 0, 0, m_mask, GL_TEXTURE_CUBE_MAP, 0, 0, 0, 0, masksize, masksize, 6); // the light the cab lights itself with again, from a small level of the light reflected glBindTextureUnit(0, m_light); glProgramUniform2i(*m_sh_program, SH_LEVEL_LOCATION, sh_level(), sh_size()); diff --git a/rendering/opengl46/opengl46cabprobe.h b/rendering/opengl46/opengl46cabprobe.h index 194de4ca..c26f81d3 100644 --- a/rendering/opengl46/opengl46cabprobe.h +++ b/rendering/opengl46/opengl46cabprobe.h @@ -28,8 +28,8 @@ class opengl46_cab_probe : public gfx::cab_probe // creates the programs, the stand-ins and the storage of the spherical harmonics. throws gl::shader_exception void init(); - // puts the maps (or the stand-ins) on their texture units, gl::CAB_PROBE_TEX and gl::CAB_PROBE_ALBEDO_TEX, and the spherical - // harmonics on gl::CAB_PROBE_SSBO_BINDING. once per context and frame + // puts the maps (or the stand-ins) on their texture units, gl::CAB_PROBE_TEX, gl::CAB_PROBE_ALBEDO_TEX and the mask on + // gl::CAB_PROBE_MASK_TEX, and the spherical harmonics on gl::CAB_PROBE_SSBO_BINDING. once per context and frame void bind(); // program of the probe, of the alpha tested materials when Alpha gl::program &program(bool const Alpha) @@ -60,6 +60,8 @@ class opengl46_cab_probe : public gfx::cab_probe GLuint m_albedo{0}; GLuint m_depth{0}; GLuint m_framebuffer{0}; + // the copy of a small level of the albedo of the last drawing, which the next one reads to tell where the windows are + GLuint m_mask{0}; // stand-ins: a cube map of 1x1 faces of nothing, a 1x1 white texture GLuint m_empty{0}; GLuint m_white{0}; diff --git a/rendering/opengl46/opengl46renderer.cpp b/rendering/opengl46/opengl46renderer.cpp index 2a039a25..7784dd38 100644 --- a/rendering/opengl46/opengl46renderer.cpp +++ b/rendering/opengl46/opengl46renderer.cpp @@ -1661,7 +1661,7 @@ void opengl46_renderer::Render_pass(viewport_config &vp, rendermode const Mode) } // the reflections: the ground below the horizon of the environment map, and the screen space ones set up for the colour // pass of the main view only - scene_ubs.env_params = glm::vec4{Global.gfx_ambient_groundalbedo, 0.f, 0.f, 0.f}; + scene_ubs.env_params = glm::vec4{Global.gfx_ambient_groundalbedo, 0.f, Global.gfx_interior_bounce, 0.f}; gfx::screen_reflections::clear(scene_ubs); // the occlusion of the sky by the scenery, seen from the camera of the view m_skyocclusion.setup_scene(scene_ubs, m_colorpass.pass_camera.position()); @@ -4593,7 +4593,7 @@ void opengl46_renderer::Setup_Ao_Lighting( viewport_config &Viewport ) // the compute passes leave the units of the textures to be bound anew ::glBindTextureUnit( gl::SCENE_DEPTH_TEX, Viewport.clusters_depth_tex->id ); ::glBindTextureUnit( gl::SCENE_AO_TEX, Viewport.ssao_blur_tex->id ); - scene_ubs.ao_params = glm::vec4{ 1.f, Global.gfx_postfx_ao_strength, 0.f, 0.f }; + scene_ubs.ao_params = glm::vec4{ 1.f, Global.gfx_postfx_ao_strength, Global.gfx_postfx_ao_cab, 0.f }; scene_ubo->update( scene_ubs ); m_aolighting = true; } @@ -6319,6 +6319,8 @@ void opengl46_renderer::Render_cab_probe() // the lamp with its shadow map, when the map holds the shadows of the lamp where it is auto const shadows{(lamp != nullptr) && m_localshadows.ready(vehicle) && (m_localshadows.lamp() == m_locallights.cab_lamp_local())}; m_cabprobe.setup_drawing(scene_ubs, lamp, m_locallights.cab_lamp_local(), shadows, m_localshadows.weights()); + // the sun coming in through the windows, as the shading of the last frame got it + m_cabprobe.setup_sun(scene_ubs, vehicle, -m_sunlight.direction, glm::vec3{light_data.lights[0].color} * light_data.lights[0].intensity, m_colorpass.pass_camera.position()); scene_ubo->update(scene_ubs); // the cab drawn by the colour mode with the programs of the probe. the camera is put at the vehicle, which Render_cab() places // the cab relative to, and the modelview takes the cab model to the space of the probe diff --git a/rendering/vulkan14/vulkan14ao.cpp b/rendering/vulkan14/vulkan14ao.cpp index 4cd57555..dc6ae981 100644 --- a/rendering/vulkan14/vulkan14ao.cpp +++ b/rendering/vulkan14/vulkan14ao.cpp @@ -176,7 +176,7 @@ bool vulkan14_renderer::compute_ao_lighting(std::size_t const Index) // the block of the scene of the colour pass set up to read the occlusion (light_common.glsl, ambient_occlusion()) void vulkan14_renderer::setup_ao_lighting() { - scene_ubs.ao_params = glm::vec4{1.f, Global.gfx_postfx_ao_strength, 0.f, 0.f}; + scene_ubs.ao_params = glm::vec4{1.f, Global.gfx_postfx_ao_strength, Global.gfx_postfx_ao_cab, 0.f}; upload_scene_block(); } diff --git a/rendering/vulkan14/vulkan14bindings.h b/rendering/vulkan14/vulkan14bindings.h index 42fc109e..0e98ccc8 100644 --- a/rendering/vulkan14/vulkan14bindings.h +++ b/rendering/vulkan14/vulkan14bindings.h @@ -84,7 +84,9 @@ constexpr std::uint32_t CAB_PROBE_SSBO = 30; // the scenery and the shares of the sky and the limits of the key light, read texel by texel constexpr std::uint32_t SKY_OCCLUSION_HEIGHT_TEX = 31; constexpr std::uint32_t SKY_OCCLUSION_TEX = 32; -constexpr std::uint32_t PASS_BINDING_COUNT = 33; +// the probe of the cab drawn last, a small level of its albedo, which the drawing of the probe reads to tell where the windows are +constexpr std::uint32_t CAB_PROBE_MASK_TEX = 33; +constexpr std::uint32_t PASS_BINDING_COUNT = 34; // the offsets of the bindings above with dynamic offsets, in the order of the bindings constexpr std::uint32_t PASS_DYNAMIC_OFFSETS = 6; // the textures of a material take the bindings of their units (#pragma texture(name, unit, ...)), 0 .. MAX_TEXTURES - 1 diff --git a/rendering/vulkan14/vulkan14cabprobe.cpp b/rendering/vulkan14/vulkan14cabprobe.cpp index 098339e3..89a404a3 100644 --- a/rendering/vulkan14/vulkan14cabprobe.cpp +++ b/rendering/vulkan14/vulkan14cabprobe.cpp @@ -178,8 +178,28 @@ bool cab_probe_maps::gpu_allocate(int const Size) let_go(); return false; } + // the mask: the level of the albedo of at most CAB_PROBE_MASK_SIZE pixels a face, as mask_size() has it once the size is set + auto masksize{size}; + while (masksize > static_cast(gl::CAB_PROBE_MASK_SIZE)) + { + masksize >>= 1; + } + image_desc mask; + mask.format = ALBEDO_FORMAT; + mask.extent = {std::max(masksize, 1u), std::max(masksize, 1u), 1}; + mask.layers = 6; + mask.view_type = VK_IMAGE_VIEW_TYPE_CUBE; + mask.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; + mask.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + m_mask = std::make_shared(); + if (false == m_mask->create(*m_memory, mask, "cab probe mask")) + { + let_go(); + return false; + } m_target = draw_target{m_light.get(), m_albedo.get(), m_depth.get(), m_lightview, m_albedoview, size}; m_drawn = false; + m_maskcopied = false; return true; } @@ -192,6 +212,7 @@ void cab_probe_maps::let_go() { m_target = draw_target{}; m_drawn = false; + m_maskcopied = false; std::vector views; for (auto *view : {&m_lightview, &m_albedoview}) { @@ -204,7 +225,7 @@ void cab_probe_maps::let_go() if (m_deletions != nullptr) { // the frames in flight may still read them - m_deletions->push(m_value, [device = m_device, views = std::move(views), light = std::move(m_light), albedo = std::move(m_albedo), depth = std::move(m_depth)]() { + m_deletions->push(m_value, [device = m_device, views = std::move(views), light = std::move(m_light), albedo = std::move(m_albedo), depth = std::move(m_depth), mask = std::move(m_mask)]() { for (auto *view : views) { vkDestroyImageView(device, view, nullptr); @@ -221,6 +242,7 @@ void cab_probe_maps::let_go() m_light.reset(); m_albedo.reset(); m_depth.reset(); + m_mask.reset(); } } // namespace vk14 @@ -341,6 +363,12 @@ void vulkan14_renderer::render_cab_probe() // the lamp with its shadow map, when the map holds the shadows of the lamp where it is auto const shadows{(lamp != nullptr) && scene.lamps.ready(vehicle) && (scene.lamps.lamp() == m_locallights.cab_lamp_local())}; probe.setup_drawing(scene_ubs, lamp, m_locallights.cab_lamp_local(), shadows, scene.lamps.weights()); + // the sun coming in through the windows, as the shading of the last frame got it. the mask is read once it was copied + probe.setup_sun(scene_ubs, vehicle, -m_sunlight.direction, glm::vec3{light_data.lights[0].color} * light_data.lights[0].intensity, m_colorpass.pass_camera.position()); + if (probe.mask_map() == nullptr) + { + scene_ubs.cab_probe_sun.w = 0.f; + } // the cab drawn by the colour mode with the programs of the probe. the camera is put at the vehicle, which Render_cab() places the cab // relative to, and the modelview takes the cab model to the space of the probe m_renderpass.draw_mode = rendermode::color; @@ -389,6 +417,30 @@ void vulkan14_renderer::render_cab_probe() // the mipmaps, and the light the cab lights itself with again from a small level of the light reflected make_cube_mipmaps(commands, *target.light); make_cube_mipmaps(commands, *target.albedo); + // where the windows are, for the next drawing: a small level of the albedo copied to the mask + if (auto *mask{probe.mask_target()}) + { + vk14::barriers(commands, {vk14::use_of(*target.albedo, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_READ_BIT), + vk14::use_of(*mask, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_WRITE_BIT)}); + auto const masksize{static_cast(probe.mask_size())}; + VkImageCopy2 region{}; + region.sType = VK_STRUCTURE_TYPE_IMAGE_COPY_2; + region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, static_cast(probe.mask_level()), 0, 6}; + region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 6}; + region.extent = {masksize, masksize, 1}; + VkCopyImageInfo2 copy{}; + copy.sType = VK_STRUCTURE_TYPE_COPY_IMAGE_INFO_2; + copy.srcImage = target.albedo->handle(); + copy.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + copy.dstImage = mask->handle(); + copy.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy.regionCount = 1; + copy.pRegions = ®ion; + commands->copy_image2(©); + vk14::barriers(commands, {vk14::use_of(*target.albedo, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access), + vk14::use_of(*mask, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, sampled_stage, sampled_access)}); + probe.mask_copied(); + } struct { glm::ivec2 level; diff --git a/rendering/vulkan14/vulkan14cabprobe.h b/rendering/vulkan14/vulkan14cabprobe.h index 672f355c..1b3a0442 100644 --- a/rendering/vulkan14/vulkan14cabprobe.h +++ b/rendering/vulkan14/vulkan14cabprobe.h @@ -61,6 +61,21 @@ class cab_probe_maps : public gfx::cab_probe { return m_drawn ? m_albedo.get() : nullptr; } + // the copy of a small level of the albedo of the last drawing (gfx::cab_probe, the mask), which the next drawing reads to tell + // where the windows are; null until it was copied: the stand-in is read + image *mask_map() const + { + return m_maskcopied ? m_mask.get() : nullptr; + } + image *mask_target() const + { + return m_mask.get(); + } + // the mask was copied and is in the layout the shading reads it in + void mask_copied() + { + m_maskcopied = true; + } // the maps were drawn and are in the layout the shading reads them in void drawn() { @@ -84,10 +99,12 @@ class cab_probe_maps : public gfx::cab_probe std::shared_ptr m_light; std::shared_ptr m_albedo; std::shared_ptr m_depth; + std::shared_ptr m_mask; VkImageView m_lightview{VK_NULL_HANDLE}; VkImageView m_albedoview{VK_NULL_HANDLE}; draw_target m_target; bool m_drawn{false}; + bool m_maskcopied{false}; }; } // namespace vk14 diff --git a/rendering/vulkan14/vulkan14frame.cpp b/rendering/vulkan14/vulkan14frame.cpp index 22129e48..eeef125d 100644 --- a/rendering/vulkan14/vulkan14frame.cpp +++ b/rendering/vulkan14/vulkan14frame.cpp @@ -1531,7 +1531,7 @@ void vulkan14_renderer::render_viewport(std::size_t const Index) scene_ubs.shadow_fit = glm::vec4{0.f}; // the reflections: the ground below the horizon of the environment map, and the screen space ones set up for the colour pass of // the main view only - scene_ubs.env_params = glm::vec4{Global.gfx_ambient_groundalbedo, (m_rtfoliage ? 0.f : 1.f), 0.f, 0.f}; + scene_ubs.env_params = glm::vec4{Global.gfx_ambient_groundalbedo, (m_rtfoliage ? 0.f : 1.f), Global.gfx_interior_bounce, 0.f}; gfx::screen_reflections::clear(scene_ubs); if (vp.main) diff --git a/rendering/vulkan14/vulkan14scene.cpp b/rendering/vulkan14/vulkan14scene.cpp index db6c5144..65b9135a 100644 --- a/rendering/vulkan14/vulkan14scene.cpp +++ b/rendering/vulkan14/vulkan14scene.cpp @@ -206,6 +206,9 @@ std::array, 3> pass_ auto const probeempty{scene.cabprobe_empty ? scene.cabprobe_empty->view() : scene.env_empty->view()}; views[CAB_PROBE_TEX] = (colour && (probelight != nullptr) ? probelight->view() : probeempty); views[CAB_PROBE_ALBEDO_TEX] = (colour && (probealbedo != nullptr) ? probealbedo->view() : probeempty); + // the probe drawn last, which the drawing of the probe (the plain set) reads to tell where the windows are + auto const *probemask{scene.cabprobe.mask_map()}; + views[CAB_PROBE_MASK_TEX] = (probemask != nullptr ? probemask->view() : probeempty); // the occlusion of the sky by the scenery (the track T), for every pass which shades auto const *occlusionheights{scene.skyocclusion.heights()}; auto const *occlusion{scene.skyocclusion.occlusion()}; @@ -1116,6 +1119,7 @@ void vulkan14_renderer::write_pass_sets(vk14::instance_cull *Cull) write(handle, CAB_PROBE_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_TEX])); write(handle, CAB_PROBE_ALBEDO_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_ALBEDO_TEX])); write(handle, CAB_PROBE_SSBO, &cabprobe, nullptr); + write(handle, CAB_PROBE_MASK_TEX, nullptr, image(ctx.sampling.clamp_linear, views[CAB_PROBE_MASK_TEX])); write(handle, SKY_OCCLUSION_HEIGHT_TEX, nullptr, image(ctx.sampling.repeat_nearest, views[SKY_OCCLUSION_HEIGHT_TEX])); write(handle, SKY_OCCLUSION_TEX, nullptr, image(ctx.sampling.repeat_nearest, views[SKY_OCCLUSION_TEX])); } diff --git a/rendering/vulkan14/vulkan14shaders.cpp b/rendering/vulkan14/vulkan14shaders.cpp index 4999b40f..fafe5a64 100644 --- a/rendering/vulkan14/vulkan14shaders.cpp +++ b/rendering/vulkan14/vulkan14shaders.cpp @@ -857,6 +857,7 @@ std::string binding_preamble(bool const Raytracing) define("CAB_PROBE_SSBO", CAB_PROBE_SSBO); define("SKY_OCCLUSION_HEIGHT_TEX", SKY_OCCLUSION_HEIGHT_TEX); define("SKY_OCCLUSION_TEX", SKY_OCCLUSION_TEX); + define("CAB_PROBE_MASK_TEX", CAB_PROBE_MASK_TEX); define("PASS_BINDING_COUNT", PASS_BINDING_COUNT); define("MATERIAL_TEXTURE_FIRST", MATERIAL_TEXTURE_FIRST); define("CL_CLUSTER_UBO", CL_CLUSTER_UBO); diff --git a/shaders/ogl46/cabprobe.frag b/shaders/ogl46/cabprobe.frag index 3e9fdf9e..11710db4 100644 --- a/shaders/ogl46/cabprobe.frag +++ b/shaders/ogl46/cabprobe.frag @@ -11,7 +11,10 @@ layout(location = 3) in vec4 f_pos; layout(binding = 0) uniform sampler2D diffuse; // the cube shadow map of the cab lamp, see gl::LOCAL_SHADOW_TEX layout(binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow probe_shadowmap; +// the probe drawn last, where the windows of the cab are, see gl::CAB_PROBE_MASK_TEX +layout(binding = CAB_PROBE_MASK_TEX) uniform samplerCube cab_probe_mask; +#include "weather_common.glsl" #include "cabprobe.glsl" void main() diff --git a/shaders/ogl46/cabprobe.glsl b/shaders/ogl46/cabprobe.glsl index 284510af..225ff1f7 100644 --- a/shaders/ogl46/cabprobe.glsl +++ b/shaders/ogl46/cabprobe.glsl @@ -2,14 +2,19 @@ // towards the middle of its box lit by the cab lamp alone, with the cube shadow map of the lamp (and what glows in it, the lights // of the desk), and its albedo, each with 1 in the alpha: the probe is cleared to nothing, so the alpha of a direction tells how // much of it the cab fills, and both are premultiplied by it. the light of the cab lamp is the one the shading gives it -// (calc_local_light() in light_common.glsl); the ambient light and the sun are left out, the cab gets those as before. the -// parameters of the lamp are in the block of the scene (gl::scene_ubs::cab_probe_lamp), in the space of the probe +// (calc_local_light() in light_common.glsl). the sun (the moon) lights the cab where it comes in through the windows +// (gl::scene_ubs::cab_probe_sun), so the light which bounces around the cab holds it; the ambient light is left out, the cab gets it as +// before. the parameters of the lamp and of the sun are in the block of the scene (gl::scene_ubs::cab_probe_lamp, cab_probe_sun), in +// the space of the probe. the including shader declares cab_probe_mask (CAB_PROBE_MASK_TEX) and includes weather_common.glsl layout(location = 0) out vec4 out_light; layout(location = 1) out vec4 out_albedo; // closest distance (squared) the light of the lamp is computed for, as LOCAL_LIGHT_MIN_DISTANCE2 of light_common.glsl const float PROBE_LIGHT_MIN_DISTANCE2 = 0.01; +// the light of the sun on a diffuse surface, as SUN_DIFFUSE_SCALE and SUN_NDOTL_SHARPNESS of light_common.glsl +const float PROBE_SUN_SCALE = 1.5; +const float PROBE_SUN_NDOTL_SHARPNESS = 1.25; // the diffuse colour of the submodel or shape, as material_diffuse() of light_common.glsl vec3 probe_material_diffuse() @@ -35,6 +40,20 @@ float probe_lamp_shadow(vec3 position, vec3 normal) return texture(probe_shadowmap, vec4(position, weights.x), reference); } +// share of the sun which reaches a point of the space of the probe through the windows of the cab: the ray towards the sun is taken +// to the box of the cab, and the probe drawn last (the mask) tells how much of the direction of the point it leaves the box at the cab +// fills, seen from the middle of the box. what is in the way inside the cab (the desk, the seats) holds the sun back only where it +// hides that point from the middle +float probe_sun_through_windows(vec3 position, vec3 tosun) +{ + vec3 box = cab_probe_sun_box.xyz; + position = clamp(position, -box, box); + vec3 invdir = (step(0.0, tosun) * 2.0 - 1.0) / max(abs(tosun), vec3(1e-5)); + vec3 far = max((box - position) * invdir, (-box - position) * invdir); + float reach = max(min(min(far.x, far.y), far.z), 0.0); + return 1.0 - textureLod(cab_probe_mask, position + tosun * reach, 0.0).a; +} + void probe_shade(vec4 tex_color) { vec3 albedo = tex_color.rgb * probe_material_diffuse(); @@ -56,6 +75,12 @@ void probe_shade(vec4 tex_color) atten *= 1.0 - probe_lamp_shadow(-tolight, normal); light += cab_probe_lamp_color.rgb * (NdotL * atten); } + if (cab_probe_sun.w > 0.0) + { + float NdotL = max(dot(normal, cab_probe_sun.xyz), 0.0); + if (NdotL > 0.0) + light += cab_probe_sun_color.rgb * (PROBE_SUN_SCALE * pow(NdotL, PROBE_SUN_NDOTL_SHARPNESS) * probe_sun_through_windows(position, cab_probe_sun.xyz) * cloud_shadow(cab_probe_sun_world.xyz)); + } out_light = vec4(albedo * light, 1.0); out_albedo = vec4(albedo, 1.0); } diff --git a/shaders/ogl46/cabprobe_alpha.frag b/shaders/ogl46/cabprobe_alpha.frag index 283c9edb..8f97af16 100644 --- a/shaders/ogl46/cabprobe_alpha.frag +++ b/shaders/ogl46/cabprobe_alpha.frag @@ -12,7 +12,10 @@ layout(location = 3) in vec4 f_pos; layout(binding = 0) uniform sampler2D diffuse; // the cube shadow map of the cab lamp, see gl::LOCAL_SHADOW_TEX layout(binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow probe_shadowmap; +// the probe drawn last, where the windows of the cab are, see gl::CAB_PROBE_MASK_TEX +layout(binding = CAB_PROBE_MASK_TEX) uniform samplerCube cab_probe_mask; +#include "weather_common.glsl" #include "cabprobe.glsl" void main() diff --git a/shaders/ogl46/common.glsl b/shaders/ogl46/common.glsl index 1aad1367..09af27a7 100644 --- a/shaders/ogl46/common.glsl +++ b/shaders/ogl46/common.glsl @@ -111,6 +111,7 @@ #define CAB_PROBE_ALBEDO_TEX 28 #define SKY_OCCLUSION_HEIGHT_TEX 29 #define SKY_OCCLUSION_TEX 30 +#define CAB_PROBE_MASK_TEX 31 #define CAB_PROBE_SSBO_BINDING 17 #define CAB_PROBE_SH_SIZE 16 #define CAB_PROBE_SH_GROUP_SIZE 64 @@ -247,7 +248,7 @@ struct model_draw_s float fog_density; float alpha_mult; float shadow_tone; - // 1 for the parts of the occupied cab, see gl::model_ubs + // 1 for the parts of the occupied cab, 2 for the low poly interiors, see gl::model_ubs float cab_interior; }; @@ -289,7 +290,7 @@ layout(std140, binding = MODEL_UBO_BINDING) uniform model_ubo float fog_density; float alpha_mult; float shadow_tone; - // 1 for the parts of the occupied cab, see gl::model_ubs + // 1 for the parts of the occupied cab, 2 for the low poly interiors, see gl::model_ubs float cab_interior; }; #endif @@ -335,6 +336,11 @@ layout(std140, binding = SCENE_UBO_BINDING) uniform scene_ubo vec4 sky_occlusion_origin; vec4 sky_occlusion_params; vec4 sky_occlusion_center; + // the sun coming in through the windows of the cab, for the drawing of its probe, see gl::scene_ubs + vec4 cab_probe_sun; + vec4 cab_probe_sun_color; + vec4 cab_probe_sun_box; + vec4 cab_probe_sun_world; }; #ifdef STAGE_VERTEX diff --git a/shaders/ogl46/light_common.glsl b/shaders/ogl46/light_common.glsl index 274eaba7..f15751d9 100644 --- a/shaders/ogl46/light_common.glsl +++ b/shaders/ogl46/light_common.glsl @@ -782,7 +782,7 @@ float ao_specular(float visibility, float NdotV, float roughness) // whether the fragment is a part of the occupied cab with its probe (gl::scene_ubs::cab_probe_box, gl::model_ubs::cab_interior) bool cab_probe_active() { - return (cab_probe_box.w > 0.0) && (cab_interior > 0.5); + return (cab_probe_box.w > 0.0) && (cab_interior > 0.5) && (cab_interior < 1.5); } // the light of the lamps the cab reflects which reaches a surface of the cab facing the view space Fragnormal, in the units of the @@ -887,6 +887,10 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref // bounces off surfaces of the albedo) and the reflection of the surroundings; not the light of the sun and the lamps, nor the // reflection of the scene, which the rays find as it is float occlusion = ambient_occlusion(); + // in the cab and the low poly interiors walls and things are near all around, and the occlusion of the view darkens the ambient + // light a lot; they take a share of it (gfx.postfx.ao.cab), the light which bounces inside them comes from the probe or below + if (cab_interior > 0.5) + occlusion = mix(1.0, occlusion, ao_params.z); vec3 diffuse_occlusion = vec3(1.0); float specular_occlusion = 1.0; if (occlusion < 1.0) @@ -971,6 +975,10 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref // the reflection of the surroundings, before the light of the sun and the lamps joins the ambient light vec3 env_reflection = fragcolor * env_spec * ambient_share * specular_occlusion + (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * cubemap_shadow * specular_occlusion)) * cubemap_spec * cubemap_share; + // the sun coming in through the windows and bouncing around a low poly interior, or the cab without its probe (which gives that + // light from where the sun falls in the cab): a share of the light of the sun outside joins the ambient light (gfx.interior.bounce) + if ((cab_interior > 0.5) && (false == cabprobe)) + fragcolor += sunlight * env_params.z; // the occlusion of the ambient light, before the light of the sun and the lamps joins it fragcolor *= diffuse_occlusion; diff --git a/shaders/vk14/cabprobe.frag b/shaders/vk14/cabprobe.frag index cd86b5ce..a853b80b 100644 --- a/shaders/vk14/cabprobe.frag +++ b/shaders/vk14/cabprobe.frag @@ -10,7 +10,10 @@ layout(location = 3) in vec4 f_pos; layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse; // the cube shadow map of the cab lamp layout(set = PASS_SET, binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow probe_shadowmap; +// the probe drawn last, where the windows of the cab are +layout(set = PASS_SET, binding = CAB_PROBE_MASK_TEX) uniform samplerCube cab_probe_mask; +#include "weather_common.glsl" #include "cabprobe.glsl" void main() diff --git a/shaders/vk14/cabprobe.glsl b/shaders/vk14/cabprobe.glsl index dae2699b..5b98ef7b 100644 --- a/shaders/vk14/cabprobe.glsl +++ b/shaders/vk14/cabprobe.glsl @@ -2,14 +2,19 @@ // towards the middle of its box lit by the cab lamp alone, with the cube shadow map of the lamp (and what glows in it, the lights // of the desk), and its albedo, each with 1 in the alpha: the probe is cleared to nothing, so the alpha of a direction tells how // much of it the cab fills, and both are premultiplied by it. the light of the cab lamp is the one the shading gives it -// (calc_local_light() in light_common.glsl); the ambient light and the sun are left out, the cab gets those as before. the -// parameters of the lamp are in the block of the scene (gl::scene_ubs::cab_probe_lamp), in the space of the probe +// (calc_local_light() in light_common.glsl). the sun (the moon) lights the cab where it comes in through the windows +// (gl::scene_ubs::cab_probe_sun), so the light which bounces around the cab holds it; the ambient light is left out, the cab gets it as +// before. the parameters of the lamp and of the sun are in the block of the scene (gl::scene_ubs::cab_probe_lamp, cab_probe_sun), in +// the space of the probe. the including shader declares cab_probe_mask (CAB_PROBE_MASK_TEX) and includes weather_common.glsl layout(location = 0) out vec4 out_light; layout(location = 1) out vec4 out_albedo; // closest distance (squared) the light of the lamp is computed for, as LOCAL_LIGHT_MIN_DISTANCE2 of light_common.glsl const float PROBE_LIGHT_MIN_DISTANCE2 = 0.01; +// the light of the sun on a diffuse surface, as SUN_DIFFUSE_SCALE and SUN_NDOTL_SHARPNESS of light_common.glsl +const float PROBE_SUN_SCALE = 1.5; +const float PROBE_SUN_NDOTL_SHARPNESS = 1.25; // the diffuse colour of the submodel or shape, as material_diffuse() of light_common.glsl vec3 probe_material_diffuse() @@ -35,6 +40,20 @@ float probe_lamp_shadow(vec3 position, vec3 normal) return texture(probe_shadowmap, vec4(position, weights.x), reference); } +// share of the sun which reaches a point of the space of the probe through the windows of the cab: the ray towards the sun is taken +// to the box of the cab, and the probe drawn last (the mask) tells how much of the direction of the point it leaves the box at the cab +// fills, seen from the middle of the box. what is in the way inside the cab (the desk, the seats) holds the sun back only where it +// hides that point from the middle +float probe_sun_through_windows(vec3 position, vec3 tosun) +{ + vec3 box = cab_probe_sun_box.xyz; + position = clamp(position, -box, box); + vec3 invdir = (step(0.0, tosun) * 2.0 - 1.0) / max(abs(tosun), vec3(1e-5)); + vec3 far = max((box - position) * invdir, (-box - position) * invdir); + float reach = max(min(min(far.x, far.y), far.z), 0.0); + return 1.0 - textureLod(cab_probe_mask, position + tosun * reach, 0.0).a; +} + void probe_shade(vec4 tex_color) { vec3 albedo = tex_color.rgb * probe_material_diffuse(); @@ -56,6 +75,12 @@ void probe_shade(vec4 tex_color) atten *= 1.0 - probe_lamp_shadow(-tolight, normal); light += cab_probe_lamp_color.rgb * (NdotL * atten); } + if (cab_probe_sun.w > 0.0) + { + float NdotL = max(dot(normal, cab_probe_sun.xyz), 0.0); + if (NdotL > 0.0) + light += cab_probe_sun_color.rgb * (PROBE_SUN_SCALE * pow(NdotL, PROBE_SUN_NDOTL_SHARPNESS) * probe_sun_through_windows(position, cab_probe_sun.xyz) * cloud_shadow(cab_probe_sun_world.xyz)); + } out_light = vec4(albedo * light, 1.0); out_albedo = vec4(albedo, 1.0); } diff --git a/shaders/vk14/cabprobe_alpha.frag b/shaders/vk14/cabprobe_alpha.frag index 0ab78275..b59456d4 100644 --- a/shaders/vk14/cabprobe_alpha.frag +++ b/shaders/vk14/cabprobe_alpha.frag @@ -11,7 +11,10 @@ layout(location = 3) in vec4 f_pos; layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse; // the cube shadow map of the cab lamp layout(set = PASS_SET, binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow probe_shadowmap; +// the probe drawn last, where the windows of the cab are +layout(set = PASS_SET, binding = CAB_PROBE_MASK_TEX) uniform samplerCube cab_probe_mask; +#include "weather_common.glsl" #include "cabprobe.glsl" void main() diff --git a/shaders/vk14/common.glsl b/shaders/vk14/common.glsl index 78a5af3b..d3391a14 100644 --- a/shaders/vk14/common.glsl +++ b/shaders/vk14/common.glsl @@ -67,7 +67,8 @@ #define CAB_PROBE_SSBO 30 #define SKY_OCCLUSION_HEIGHT_TEX 31 #define SKY_OCCLUSION_TEX 32 -#define PASS_BINDING_COUNT 33 +#define CAB_PROBE_MASK_TEX 33 +#define PASS_BINDING_COUNT 34 #define MATERIAL_TEXTURE_FIRST 0 #define CL_CLUSTER_UBO 0 #define CL_AABB_SSBO 1 @@ -302,6 +303,11 @@ layout(std140, set = PASS_SET, binding = SCENE_UBO) uniform scene_ubo vec4 sky_occlusion_origin; vec4 sky_occlusion_params; vec4 sky_occlusion_center; + // the sun coming in through the windows of the cab, for the drawing of its probe, see gl::scene_ubs + vec4 cab_probe_sun; + vec4 cab_probe_sun_color; + vec4 cab_probe_sun_box; + vec4 cab_probe_sun_world; }; #ifndef LAYOUT_COMPUTE @@ -319,7 +325,7 @@ struct model_draw_s float fog_density; float alpha_mult; float shadow_tone; - // 1 for the parts of the occupied cab, see gl::model_ubs + // 1 for the parts of the occupied cab, 2 for the low poly interiors, see gl::model_ubs float cab_interior; }; diff --git a/shaders/vk14/light_common.glsl b/shaders/vk14/light_common.glsl index 424be4a5..a4471af8 100644 --- a/shaders/vk14/light_common.glsl +++ b/shaders/vk14/light_common.glsl @@ -795,7 +795,7 @@ float ao_specular(float visibility, float NdotV, float roughness) // whether the fragment is a part of the occupied cab with its probe (gl::scene_ubs::cab_probe_box, gl::model_ubs::cab_interior) bool cab_probe_active() { - return (cab_probe_box.w > 0.0) && (cab_interior > 0.5); + return (cab_probe_box.w > 0.0) && (cab_interior > 0.5) && (cab_interior < 1.5); } // the light of the lamps the cab reflects which reaches a surface of the cab facing the view space Fragnormal, in the units of the @@ -900,6 +900,10 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref // bounces off surfaces of the albedo) and the reflection of the surroundings; not the light of the sun and the lamps, nor the // reflection of the scene, which the rays find as it is float occlusion = ambient_occlusion(); + // in the cab and the low poly interiors walls and things are near all around, and the occlusion of the view darkens the ambient + // light a lot; they take a share of it (gfx.postfx.ao.cab), the light which bounces inside them comes from the probe or below + if (cab_interior > 0.5) + occlusion = mix(1.0, occlusion, ao_params.z); vec3 diffuse_occlusion = vec3(1.0); float specular_occlusion = 1.0; if (occlusion < 1.0) @@ -985,6 +989,10 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref // the reflection of the surroundings, before the light of the sun and the lamps joins the ambient light vec3 env_reflection = fragcolor * env_spec * ambient_share * specular_occlusion + (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * cubemap_shadow * specular_occlusion)) * cubemap_spec * cubemap_share; + // the sun coming in through the windows and bouncing around a low poly interior, or the cab without its probe (which gives that + // light from where the sun falls in the cab): a share of the light of the sun outside joins the ambient light (gfx.interior.bounce) + if ((cab_interior > 0.5) && (false == cabprobe)) + fragcolor += sunlight * env_params.z; // the occlusion of the ambient light, before the light of the sun and the lamps joins it fragcolor *= diffuse_occlusion; diff --git a/utilities/Globals.cpp b/utilities/Globals.cpp index 4852fd16..edd6fc55 100644 --- a/utilities/Globals.cpp +++ b/utilities/Globals.cpp @@ -1907,6 +1907,7 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To else if( Token == "gfx.postfx.ao.radius" ) { readvalue( gfx_postfx_ao_radius, 0.1f, 10.f ); } else if( Token == "gfx.postfx.ao.strength" ) { readvalue( gfx_postfx_ao_strength, 0.f, 4.f ); } else if( Token == "gfx.postfx.ao.bounce" ) { readvalue( gfx_postfx_ao_bounce, 0.f, 1.f ); } + else if( Token == "gfx.postfx.ao.cab" ) { readvalue( gfx_postfx_ao_cab, 0.f, 1.f ); } else if( Token == "gfx.postfx.ao.quality" ) { // slices and steps per side Parser.getTokens( 2, false ); @@ -1938,6 +1939,8 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To gfx_cabprobe_interval = std::clamp( gfx_cabprobe_interval, 1, 120 ); } else if( Token == "gfx.cabprobe.bounce" ) { readvalue( gfx_cabprobe_bounce, 0.f, 4.f ); } else if( Token == "gfx.cabprobe.reflections" ) { readswitch( gfx_cabprobe_reflections ); } + else if( Token == "gfx.cabprobe.sun" ) { readvalue( gfx_cabprobe_sun, 0.f, 4.f ); } + else if( Token == "gfx.interior.bounce" ) { readvalue( gfx_interior_bounce, 0.f, 1.f ); } else if( Token == "gfx.skyvisibility" ) { // no/low/medium/high, or the number of the level Parser.getTokens( 1 ); @@ -2417,6 +2420,7 @@ global_settings::export_as_text( std::ostream &Output ) const { export_as_text( Output, "gfx.postfx.ao.radius", gfx_postfx_ao_radius ); export_as_text( Output, "gfx.postfx.ao.strength", gfx_postfx_ao_strength ); export_as_text( Output, "gfx.postfx.ao.bounce", gfx_postfx_ao_bounce ); + export_as_text( Output, "gfx.postfx.ao.cab", gfx_postfx_ao_cab ); Output << "gfx.postfx.ao.quality " << gfx_postfx_ao_slices << " " << gfx_postfx_ao_steps << "\n"; export_as_text( Output, "gfx.postfx.ao.lighting", gfx_postfx_ao_lighting ); export_as_text( Output, "gfx.lights.scenery", gfx_lights_scenery ); @@ -2431,6 +2435,8 @@ global_settings::export_as_text( std::ostream &Output ) const { export_as_text( Output, "gfx.cabprobe.interval", gfx_cabprobe_interval ); export_as_text( Output, "gfx.cabprobe.bounce", gfx_cabprobe_bounce ); export_as_text( Output, "gfx.cabprobe.reflections", gfx_cabprobe_reflections ); + export_as_text( Output, "gfx.cabprobe.sun", gfx_cabprobe_sun ); + export_as_text( Output, "gfx.interior.bounce", gfx_interior_bounce ); export_as_text( Output, "gfx.skyvisibility", std::string{ gfx_skyvisibility == 3 ? "high" : gfx_skyvisibility == 2 ? "medium" : gfx_skyvisibility == 1 ? "low" : "no" } ); export_as_text( Output, "gfx.skyvisibility.sun", gfx_skyvisibility_sun ); export_as_text( Output, "gfx.skyvisibility.fog", gfx_skyvisibility_fog ); diff --git a/utilities/Globals.h b/utilities/Globals.h index 0f9a66d1..f4dff897 100644 --- a/utilities/Globals.h +++ b/utilities/Globals.h @@ -483,6 +483,7 @@ struct global_settings { float gfx_postfx_ao_bounce { 0.3f }; // albedo of the surfaces assumed for the light coming back after more bounces int gfx_postfx_ao_slices { 3 }; // directions searched around each pixel int gfx_postfx_ao_steps { 4 }; // samples on each side of a direction + float gfx_postfx_ao_cab { 0.5f }; // share of the occlusion the cab and the low poly interiors take, 1 = all of it // with gfx_postfx_ao 2 (opengl 4.6 and vulkan): the occlusion is worked out from the depth of the scene drawn before its colour // (which it turns on, see gfx_depthprepass_scene) and darkens the ambient light and the reflection of the surroundings in the // shading, not the light of the sun and the lamps. false: it darkens the finished image, as before @@ -508,6 +509,12 @@ struct global_settings { int gfx_cabprobe_interval { 4 }; float gfx_cabprobe_bounce { 1.f }; bool gfx_cabprobe_reflections { true }; + // the probe of the cab is lit by the sun (the moon) coming in through the windows too, so the light which bounces around the cab + // is the light of the sun where it falls in it; the scale of that light, 0 for none + float gfx_cabprobe_sun { 1.f }; + // the light of the sun coming in through the windows of the low poly interiors of the vehicles and of the cab without its probe, + // bounced around them, as a share of the light of the sun on a surface facing it; 0 for none + float gfx_interior_bounce { 0.1f }; // the occlusion of the sky and of the sun by the scenery around the camera (opengl 4.6 and vulkan, gfx::sky_occlusion, the track T): // tunnels, the ground under bridges and roofs, cuttings and the foot of slopes are dark by their geometry, worked out in the // background from the scenery seen from above. 0 off (nothing is occluded), 1 low (256 m around the camera, texels of 1 m),