From 96100f1155f024010bf754599c0293100feaba14 Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Thu, 29 Oct 2026 20:05:03 +0100 Subject: [PATCH] Draw the cab probe with all the lights of the cab --- docs/opengl46-renderer.md | 8 +++-- gl/glsl_common.cpp | 1 + gl/ubo.h | 20 ++++++++---- rendering/common/cabprobe.cpp | 39 +++++++++++++++------- rendering/common/cabprobe.h | 20 ++++++------ rendering/common/locallights.cpp | 1 + rendering/common/locallights.h | 6 +--- rendering/opengl46/opengl46renderer.cpp | 13 ++++---- rendering/vulkan14/vulkan14cabprobe.cpp | 13 ++++---- rendering/vulkan14/vulkan14shaders.cpp | 1 + shaders/ogl46/cabprobe.glsl | 43 ++++++++++++++++--------- shaders/ogl46/common.glsl | 12 ++++--- shaders/vk14/cabprobe.glsl | 43 ++++++++++++++++--------- shaders/vk14/common.glsl | 12 ++++--- 14 files changed, 143 insertions(+), 89 deletions(-) diff --git a/docs/opengl46-renderer.md b/docs/opengl46-renderer.md index 52ed7793..614a56c4 100644 --- a/docs/opengl46-renderer.md +++ b/docs/opengl46-renderer.md @@ -261,10 +261,12 @@ shadows of the sun, see `headlight shadows:` in Shift+F12); with the vehicle on | `gfx.lights.shadows.headlights.range` | `250` | 10..1000: distance ahead of the vehicle within which things cast the shadows of its headlights, metres | **The probe of the cab** (`gfx.cabprobe`, OpenGL and Vulkan): two cube maps drawn from the middle of the box of the cab model, -in the space of the cab (they stay right while the vehicle moves): the light of the cab lamp the cab reflects (with the -shadows of the lamp) and the light of what glows in it (the lights of the desk), and the albedo of the cab. The glasses +in the space of the cab (they stay right while the vehicle moves): the light of the lights of the cab the cab reflects (the +lamp, or the light points of the cab model, the brightest 16 of them, each with its cone, radius and shadows) and the light +of what glows in it, and the albedo of the cab. So a lamp aimed down lights the floor and the desk, and their light lights the +ceiling and the rest of the cab again. The glasses aren't drawn, so through them the probe shows nothing. They are drawn every `gfx.cabprobe.interval` frames (for the moving -parts and the lights of the desk) and at once when the cab or the lamp change, the six faces in one pass with the opaque +parts and the lights of the desk) and at once when the cab or its lights change (switched, dimmed), the six faces in one pass with the opaque parts of the cab (as the shadow map of the lamp). The parts of the cab read them: - the light of the lamp the cab reflects lights the cab again (`gfx.cabprobe.bounce`, from spherical harmonics of the probe) in place of `gfx.lights.cab.bounce`, so the light bounces where the lamp actually lights the cab; diff --git a/gl/glsl_common.cpp b/gl/glsl_common.cpp index 0ccc30d9..cb6e4e1f 100644 --- a/gl/glsl_common.cpp +++ b/gl/glsl_common.cpp @@ -88,6 +88,7 @@ void gl::glsl_common_setup() "#define LOCAL_SHADOW_LAMPS_TEX " + std::to_string(LOCAL_SHADOW_LAMPS_TEX) + "\n" + "#define HEADLIGHT_SHADOW_TEX " + std::to_string(HEADLIGHT_SHADOW_TEX) + "\n" + "#define MAX_HEADLIGHT_SHADOWS " + std::to_string(MAX_HEADLIGHT_SHADOWS) + "U\n" + + "#define MAX_CAB_PROBE_LAMPS " + std::to_string(MAX_CAB_PROBE_LAMPS) + "U\n" + "#define FOG_VOLUME_TEX " + std::to_string(FOG_VOLUME_TEX) + "\n" + "#define CLOUD_SHADOW_TEX " + std::to_string(CLOUD_SHADOW_TEX) + "\n" + "#define CLOUD_TEX " + std::to_string(CLOUD_TEX) + "\n" + diff --git a/gl/ubo.h b/gl/ubo.h index 38157bcf..a0ec5847 100644 --- a/gl/ubo.h +++ b/gl/ubo.h @@ -67,6 +67,8 @@ namespace gl // shadow maps of the headlights of vehicles (2d array, depth compared; opengl46_local_shadows), and how many of them there may be const size_t HEADLIGHT_SHADOW_TEX = MAX_TEXTURES + HELPER_TEXTURES + 3; const size_t MAX_HEADLIGHT_SHADOWS = 4; + // lights of the occupied cab the probe of the cab is drawn with (gfx::cab_probe, gl::scene_ubs::cab_probe_lamp), at most + const size_t MAX_CAB_PROBE_LAMPS = 16; // weather (opengl46_weather): the fog of the main view (3d, apply_fog.glsl) and the shadows the clouds cast (2d), read by the // shading. bound directly, like the ones above const size_t FOG_VOLUME_TEX = MAX_TEXTURES + HELPER_TEXTURES + 4; @@ -161,11 +163,17 @@ namespace gl // x the share of the light of the lamps the cab reflects which lights it again (gfx.cabprobe.bounce), y 1 when the cab reflects // itself (gfx.cabprobe.reflections), z the last level of the mipmaps of the probe, w unused glm::vec4 cab_probe_params; - // the drawing of the probe (cabprobe.glsl), set for it only: xyz the cab lamp in the space of the probe, w 1 / its range - // squared (0: no lamp); rgb its light, a 1 with its shadow map (LOCAL_SHADOW_TEX); the headlight_weights of that map - glm::vec4 cab_probe_lamp; - glm::vec4 cab_probe_lamp_color; - glm::vec4 cab_probe_lamp_shadow; + // the drawing of the probe (cabprobe.glsl), set for it only: the lights of the cab (gfx::local_lights::cab_lights()), the + // brightest first, each: xyz the light in the space of the probe, w 1 / its range squared (0: no more lights); rgb its light, + // a 1 with a shadow map (LOCAL_SHADOW_TEX); the headlight_weights of that map; xyz the axis of its cone in the space of the + // probe, w the cosine of its outer half angle (-1: all around); x the cosine of the inner one, y its radius (negative: the + // soft falloff), zw unused; xyz the light the map was drawn for (the light itself, or the closest one with a map), w unused + glm::vec4 cab_probe_lamp[MAX_CAB_PROBE_LAMPS]; + glm::vec4 cab_probe_lamp_color[MAX_CAB_PROBE_LAMPS]; + glm::vec4 cab_probe_lamp_shadow[MAX_CAB_PROBE_LAMPS]; + glm::vec4 cab_probe_lamp_cone[MAX_CAB_PROBE_LAMPS]; + glm::vec4 cab_probe_lamp_shape[MAX_CAB_PROBE_LAMPS]; + glm::vec4 cab_probe_lamp_map[MAX_CAB_PROBE_LAMPS]; // the occlusion of the sky and of the sun by the scenery around the camera (gfx.skyvisibility, gfx::sky_occlusion), zero when // there are no maps: x, y the world x, z of the camera of the pass wrapped to the size of the maps (the place of the camera on // them), metres; z the world height of the camera; w 1 with the maps, 2 when the volumetric fog reads them too @@ -186,7 +194,7 @@ namespace gl glm::vec4 cab_probe_sun_world; }; - static_assert(sizeof(scene_ubs) == 976 + 80 * MAX_HEADLIGHT_SHADOWS, "bad size of ubs"); + static_assert(sizeof(scene_ubs) == 928 + 80 * MAX_HEADLIGHT_SHADOWS + 96 * MAX_CAB_PROBE_LAMPS, "bad size of ubs"); const size_t MAX_PARAMS = 3; diff --git a/rendering/common/cabprobe.cpp b/rendering/common/cabprobe.cpp index ec89a3b8..b2458ea6 100644 --- a/rendering/common/cabprobe.cpp +++ b/rendering/common/cabprobe.cpp @@ -73,7 +73,8 @@ void cab_probe::release() 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) +bool cab_probe::update_wanted(TDynamicObject const *Vehicle, TModel3d const *Cab, glm::vec3 const &Center, glm::vec3 const &Extent, std::vector const &Lights, + unsigned int const Framestamp) { auto const level{std::clamp(Global.gfx_cabprobe, 0, 3)}; if (level != m_level) @@ -92,7 +93,12 @@ bool cab_probe::update_wanted(TDynamicObject const *Vehicle, TModel3d const *Cab m_masked = false; return false; } - auto const lamp{Lamp != nullptr ? Lamp->color : glm::vec3{0.f}}; + // the lights the probe is drawn with, together: one of them switched or dimmed changes it + glm::vec3 lamp{0.f}; + for (std::size_t idx = 0; idx < std::min(Lights.size(), gl::MAX_CAB_PROBE_LAMPS); ++idx) + { + lamp += Lights[idx].source.color; + } 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)) @@ -120,19 +126,28 @@ bool cab_probe::update_wanted(TDynamicObject const *Vehicle, TModel3d const *Cab 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 +void cab_probe::setup_drawing(gl::scene_ubs &Scene, std::vector const &Lights, gfx::local_shadows const &Shadows, TDynamicObject const *Vehicle) const { - if (Lamp == nullptr) + for (std::size_t idx = 0; idx < gl::MAX_CAB_PROBE_LAMPS; ++idx) { - 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; + if (idx >= Lights.size()) + { + // the end of the lights + Scene.cab_probe_lamp[idx] = glm::vec4{0.f}; + Scene.cab_probe_lamp_color[idx] = glm::vec4{0.f}; + continue; + } + auto const &light{Lights[idx]}; + // as the light buffer has it (Write_Local_Light()): the shading takes the light to end at 1 / sqrt(quadratic) + Scene.cab_probe_lamp[idx] = glm::vec4{light.local - m_center, 1.f / sq(std::max(light.source.range, 0.01f))}; + Scene.cab_probe_lamp_cone[idx] = glm::vec4{light.local_direction, light.source.cos_outer}; + Scene.cab_probe_lamp_shape[idx] = glm::vec4{light.source.cos_inner, (light.source.soft_falloff ? -light.source.radius : light.source.radius), 0.f, 0.f}; + // with the shadow map of the light, or of the closest light with one + auto const slot{Shadows.cab_slot_of(Vehicle, light)}; + Scene.cab_probe_lamp_color[idx] = glm::vec4{light.source.color, (slot >= 0 ? 1.f : 0.f)}; + Scene.cab_probe_lamp_shadow[idx] = (slot >= 0 ? Shadows.weights(static_cast(slot)) : glm::vec4{0.f}); + Scene.cab_probe_lamp_map[idx] = glm::vec4{(slot >= 0 ? Shadows.cab(static_cast(slot)).lamp : light.local) - m_center, 0.f}; } - // 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::setup_sun(gl::scene_ubs &Scene, TDynamicObject const *Vehicle, glm::vec3 const &Tosun, glm::vec3 const &Sunlight, glm::dvec3 const &Camera) const diff --git a/rendering/common/cabprobe.h b/rendering/common/cabprobe.h index 3c9eb882..88eed383 100644 --- a/rendering/common/cabprobe.h +++ b/rendering/common/cabprobe.h @@ -15,6 +15,7 @@ http://mozilla.org/MPL/2.0/. #include "gl/ubo.h" #include "rendering/common/locallights.h" +#include "rendering/common/localshadows.h" class TDynamicObject; class TModel3d; @@ -25,8 +26,8 @@ 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 +// the vehicle moves and turns: the light of the lights of the cab (the lamp, the light points of the cab model, the brightest +// gl::MAX_CAB_PROBE_LAMPS of them, with their shadow maps) the cab reflects 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 @@ -53,12 +54,13 @@ class cab_probe // 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; + // model, Center and Extent the box of the model (model space); Lights the lights of the cab of the frame, the brightest first + // (empty: 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, std::vector const &Lights, + unsigned int const Framestamp); + // the fields of the block of the scene the drawing of the probe reads: the lights of the cab of Vehicle (Lights, the brightest first) + // with the shadow maps Shadows holds for them + void setup_drawing(gl::scene_ubs &Scene, std::vector const &Lights, gfx::local_shadows const &Shadows, TDynamicObject const *Vehicle) 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 @@ -123,7 +125,7 @@ class cab_probe 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 + // the light of the lights of the cab the probe was drawn with (their sum), 0 without them glm::vec3 m_lamp{0.f}; unsigned int m_drawnframe{0}; // statistics diff --git a/rendering/common/locallights.cpp b/rendering/common/locallights.cpp index 9116dd8f..60f62f4e 100644 --- a/rendering/common/locallights.cpp +++ b/rendering/common/locallights.cpp @@ -350,6 +350,7 @@ void gfx::local_lights::gather_cab_lights(gfx::geometrybank_manager const &Geome cab_light cablight; cablight.source = m_lights[idx]; cablight.local = glm::vec3{m_lights[idx].position}; + cablight.local_direction = m_lights[idx].direction; cablight.source.position = vehicle->GetWorldPosition(m_lights[idx].position); auto const direction{glm::vec3{rotation * glm::dvec3{m_lights[idx].direction}}}; cablight.source.direction = (glm::length2(direction) > 0.f ? glm::normalize(direction) : glm::vec3{0.f, -1.f, 0.f}); diff --git a/rendering/common/locallights.h b/rendering/common/locallights.h index 3dfcb52d..fa0390c6 100644 --- a/rendering/common/locallights.h +++ b/rendering/common/locallights.h @@ -87,17 +87,13 @@ class local_lights { light source; glm::vec3 local{0.f}; + glm::vec3 local_direction{0.f, -1.f, 0.f}; // the axis of its cone in the space of the cab model }; // the lights of the occupied cab which are on, when the cab is drawn, the brightest first; empty otherwise std::vector const &cab_lights() const { return m_cablights; } - // the brightest light of the cab, which lights its probe; nullptr when there is none - cab_light const *cab_principal() const - { - return m_cablights.empty() ? nullptr : &m_cablights.front(); - } // the vehicle of the lights of the cab, when any of them is on TDynamicObject const *cab_lamp_vehicle() const { diff --git a/rendering/opengl46/opengl46renderer.cpp b/rendering/opengl46/opengl46renderer.cpp index 347d6eff..af5649ad 100644 --- a/rendering/opengl46/opengl46renderer.cpp +++ b/rendering/opengl46/opengl46renderer.cpp @@ -6320,16 +6320,15 @@ void opengl46_renderer::Render_cab_probe() glm::vec3 extent{0.f}; if ((vehicle != nullptr) && (false == m_locallights.cab_bounds(vehicle->mdKabina, m_geometry, center, extent))) vehicle = nullptr; - // the brightest light of the cab lights the probe when it's a light of the frame - auto const *principal{(vehicle != nullptr) && (m_locallights.cab_lamp_vehicle() == vehicle) ? m_locallights.cab_principal() : nullptr}; - auto const *lamp{principal != nullptr ? &principal->source : nullptr}; - if (false == m_cabprobe.update_wanted(vehicle, (vehicle != nullptr ? vehicle->mdKabina : nullptr), center, extent, lamp, m_framestamp)) + // the lights of the cab light the probe when they're lights of the frame + std::vector const nolights; + auto const &lights{(vehicle != nullptr) && (m_locallights.cab_lamp_vehicle() == vehicle) ? m_locallights.cab_lights() : nolights}; + if (false == m_cabprobe.update_wanted(vehicle, (vehicle != nullptr ? vehicle->mdKabina : nullptr), center, extent, lights, m_framestamp)) return; glDebug("render cab probe"); - // the light with its shadow map, when the map holds the shadows of the light where it is - auto const slot{principal != nullptr ? m_localshadows.cab_slot_of(vehicle, *principal) : -1}; - m_cabprobe.setup_drawing(scene_ubs, lamp, (principal != nullptr ? principal->local : glm::vec3{0.f}), (slot >= 0), (slot >= 0 ? m_localshadows.weights(static_cast(slot)) : glm::vec4{0.f})); + // the lights with their shadow maps, where the maps hold their shadows + m_cabprobe.setup_drawing(scene_ubs, lights, m_localshadows, vehicle); // 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); diff --git a/rendering/vulkan14/vulkan14cabprobe.cpp b/rendering/vulkan14/vulkan14cabprobe.cpp index 726c4837..891f6a36 100644 --- a/rendering/vulkan14/vulkan14cabprobe.cpp +++ b/rendering/vulkan14/vulkan14cabprobe.cpp @@ -348,10 +348,10 @@ void vulkan14_renderer::render_cab_probe() { vehicle = nullptr; } - // the brightest light of the cab lights the probe when it's a light of the frame - auto const *principal{(vehicle != nullptr) && (m_locallights.cab_lamp_vehicle() == vehicle) ? m_locallights.cab_principal() : nullptr}; - auto const *lamp{principal != nullptr ? &principal->source : nullptr}; - if (false == probe.update_wanted(vehicle, (vehicle != nullptr ? vehicle->mdKabina : nullptr), center, extent, lamp, m_framestamp)) + // the lights of the cab light the probe when they're lights of the frame + std::vector const nolights; + auto const &lights{(vehicle != nullptr) && (m_locallights.cab_lamp_vehicle() == vehicle) ? m_locallights.cab_lights() : nolights}; + if (false == probe.update_wanted(vehicle, (vehicle != nullptr ? vehicle->mdKabina : nullptr), center, extent, lights, m_framestamp)) { return; } @@ -361,9 +361,8 @@ void vulkan14_renderer::render_cab_probe() return; } ctx.passes.begin_pass("cab probe"); - // the light with its shadow map, when the map holds the shadows of the light where it is - auto const slot{principal != nullptr ? scene.lamps.cab_slot_of(vehicle, *principal) : -1}; - probe.setup_drawing(scene_ubs, lamp, (principal != nullptr ? principal->local : glm::vec3{0.f}), (slot >= 0), (slot >= 0 ? scene.lamps.weights(static_cast(slot)) : glm::vec4{0.f})); + // the lights with their shadow maps, where the maps hold their shadows + probe.setup_drawing(scene_ubs, lights, scene.lamps, vehicle); // 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) diff --git a/rendering/vulkan14/vulkan14shaders.cpp b/rendering/vulkan14/vulkan14shaders.cpp index fafe5a64..08d9e779 100644 --- a/rendering/vulkan14/vulkan14shaders.cpp +++ b/rendering/vulkan14/vulkan14shaders.cpp @@ -889,6 +889,7 @@ std::string binding_preamble(bool const Raytracing) define("MAX_CASCADES", static_cast(gl::MAX_CASCADES), "U"); define("MAX_PARAMS", static_cast(gl::MAX_PARAMS), "U"); define("MAX_HEADLIGHT_SHADOWS", static_cast(gl::MAX_HEADLIGHT_SHADOWS), "U"); + define("MAX_CAB_PROBE_LAMPS", static_cast(gl::MAX_CAB_PROBE_LAMPS), "U"); define("CLUSTER_GRID_X", gl::CLUSTER_GRID_X); define("CLUSTER_GRID_Y", gl::CLUSTER_GRID_Y); define("CLUSTER_GRID_Z", gl::CLUSTER_GRID_Z); diff --git a/shaders/ogl46/cabprobe.glsl b/shaders/ogl46/cabprobe.glsl index d33196e6..76ad4231 100644 --- a/shaders/ogl46/cabprobe.glsl +++ b/shaders/ogl46/cabprobe.glsl @@ -1,10 +1,10 @@ // the shading of the probe of the occupied cab (gfx::cab_probe), for cabprobe.frag and cabprobe_alpha.frag: what the cab shows -// 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 sun (the moon) lights the cab where it comes in through the windows +// towards the middle of its box lit by the lights of the cab alone (its lamp or the light points of the cab model, the brightest +// MAX_CAB_PROBE_LAMPS, each with its cube shadow map; and what glows in it), 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 a light of the cab is about the one the shading gives +// it (calc_local_light() in light_common.glsl): its cone, its sphere or soft falloff, its shadows. 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 +// before. the parameters of the lights 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; @@ -24,11 +24,11 @@ vec3 probe_material_diffuse() return (value > 0.0 ? diffusecolor * (param[1].x / value) : vec3(0.0)); } -// share of the light of the cab lamp held back before the point of the space of its cube shadow map (origin at the lamp), 0..1, as -// local_light_shadow() of light_common.glsl takes it with a single sample -float probe_lamp_shadow(vec3 position, vec3 normal) +// share of the light of the light Lamp of the cab held back before the point of the space of its cube shadow map (origin at the +// light the map was drawn for), 0..1, as local_light_shadow() of light_common.glsl takes it with a single sample +float probe_lamp_shadow(int lamp, vec3 position, vec3 normal) { - vec4 weights = cab_probe_lamp_shadow; + vec4 weights = cab_probe_lamp_shadow[lamp]; float distance = length(position); float texel = weights.z * distance; vec3 tolamp = -position / max(distance, 1e-4); @@ -63,17 +63,28 @@ void probe_shade(vec4 tex_color) if (dot(normal, position) > 0.0) normal = -normal; vec3 light = vec3(emission); - if (cab_probe_lamp.w > 0.0) + for (int i = 0; i < int(MAX_CAB_PROBE_LAMPS); i++) { - vec3 tolight = cab_probe_lamp.xyz - position; + vec4 lamp = cab_probe_lamp[i]; + if (lamp.w <= 0.0) + break; + vec3 tolight = lamp.xyz - position; float distance2 = dot(tolight, tolight); vec3 light_dir = tolight * inversesqrt(max(distance2, 1e-8)); - float edge = clamp(1.0 - distance2 * distance2 * (cab_probe_lamp.w * cab_probe_lamp.w), 0.0, 1.0); - float atten = edge * edge / max(distance2, PROBE_LIGHT_MIN_DISTANCE2); + float edge = clamp(1.0 - distance2 * distance2 * (lamp.w * lamp.w), 0.0, 1.0); + // as lamp_falloff() of common.glsl: the soft falloff (a negative radius), or the light of the sphere, which is the one of a + // point outside it + float radius = cab_probe_lamp_shape[i].y; + float falloff = (radius < 0.0) ? 1.0 / max(distance2 + radius * radius, 1e-6) : 1.0 / max(distance2, (radius > 0.0) ? radius * radius : PROBE_LIGHT_MIN_DISTANCE2); + float atten = edge * edge * falloff; + // the cone of a spot light + vec4 cone = cab_probe_lamp_cone[i]; + if (cone.w > -1.0) + atten *= smoothstep(cone.w, cab_probe_lamp_shape[i].x, dot(-light_dir, normalize(cone.xyz))); float NdotL = max(dot(normal, light_dir), 0.0); - if ((atten > 0.0) && (NdotL > 0.0) && (cab_probe_lamp_color.a > 0.5)) - atten *= 1.0 - probe_lamp_shadow(-tolight, normal); - light += cab_probe_lamp_color.rgb * (NdotL * atten); + if ((atten > 0.0) && (NdotL > 0.0) && (cab_probe_lamp_color[i].a > 0.5)) + atten *= 1.0 - probe_lamp_shadow(i, position - cab_probe_lamp_map[i].xyz, normal); + light += cab_probe_lamp_color[i].rgb * (NdotL * atten); } if (cab_probe_sun.w > 0.0) { diff --git a/shaders/ogl46/common.glsl b/shaders/ogl46/common.glsl index bf7a06c0..5d626761 100644 --- a/shaders/ogl46/common.glsl +++ b/shaders/ogl46/common.glsl @@ -97,6 +97,7 @@ #define LOCAL_SHADOW_LAMPS_TEX 14 #define HEADLIGHT_SHADOW_TEX 15 #define MAX_HEADLIGHT_SHADOWS 4U +#define MAX_CAB_PROBE_LAMPS 16U #define FOG_VOLUME_TEX 16 #define CLOUD_SHADOW_TEX 17 #define CLOUD_TEX 18 @@ -343,13 +344,16 @@ layout(std140, binding = SCENE_UBO_BINDING) uniform scene_ubo vec4 rain_wind; // the ambient occlusion in the light of the surfaces, see gl::scene_ubs vec4 ao_params; - // the probe of the occupied cab, and the lamp it's drawn with, see gl::scene_ubs + // the probe of the occupied cab, and the lights of the cab it's drawn with, see gl::scene_ubs mat4 cab_probe_view; vec4 cab_probe_box; vec4 cab_probe_params; - vec4 cab_probe_lamp; - vec4 cab_probe_lamp_color; - vec4 cab_probe_lamp_shadow; + vec4 cab_probe_lamp[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_color[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_shadow[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_cone[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_shape[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_map[MAX_CAB_PROBE_LAMPS]; // the occlusion of the sky and of the sun by the scenery around the camera, see gl::scene_ubs vec4 sky_occlusion_origin; vec4 sky_occlusion_params; diff --git a/shaders/vk14/cabprobe.glsl b/shaders/vk14/cabprobe.glsl index 42903add..4ae2c4fa 100644 --- a/shaders/vk14/cabprobe.glsl +++ b/shaders/vk14/cabprobe.glsl @@ -1,10 +1,10 @@ // the shading of the probe of the occupied cab (gfx::cab_probe, as shaders/ogl46/cabprobe.glsl), for cabprobe.frag and cabprobe_alpha.frag: what the cab shows -// 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 sun (the moon) lights the cab where it comes in through the windows +// towards the middle of its box lit by the lights of the cab alone (its lamp or the light points of the cab model, the brightest +// MAX_CAB_PROBE_LAMPS, each with its cube shadow map; and what glows in it), 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 a light of the cab is about the one the shading gives +// it (calc_local_light() in light_common.glsl): its cone, its sphere or soft falloff, its shadows. 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 +// before. the parameters of the lights 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; @@ -24,11 +24,11 @@ vec3 probe_material_diffuse() return (value > 0.0 ? diffusecolor * (param[1].x / value) : vec3(0.0)); } -// share of the light of the cab lamp held back before the point of the space of its cube shadow map (origin at the lamp), 0..1, as -// local_light_shadow() of light_common.glsl takes it with a single sample -float probe_lamp_shadow(vec3 position, vec3 normal) +// share of the light of the light Lamp of the cab held back before the point of the space of its cube shadow map (origin at the +// light the map was drawn for), 0..1, as local_light_shadow() of light_common.glsl takes it with a single sample +float probe_lamp_shadow(int lamp, vec3 position, vec3 normal) { - vec4 weights = cab_probe_lamp_shadow; + vec4 weights = cab_probe_lamp_shadow[lamp]; float distance = length(position); float texel = weights.z * distance; vec3 tolamp = -position / max(distance, 1e-4); @@ -63,17 +63,28 @@ void probe_shade(vec4 tex_color) if (dot(normal, position) > 0.0) normal = -normal; vec3 light = vec3(emission); - if (cab_probe_lamp.w > 0.0) + for (int i = 0; i < int(MAX_CAB_PROBE_LAMPS); i++) { - vec3 tolight = cab_probe_lamp.xyz - position; + vec4 lamp = cab_probe_lamp[i]; + if (lamp.w <= 0.0) + break; + vec3 tolight = lamp.xyz - position; float distance2 = dot(tolight, tolight); vec3 light_dir = tolight * inversesqrt(max(distance2, 1e-8)); - float edge = clamp(1.0 - distance2 * distance2 * (cab_probe_lamp.w * cab_probe_lamp.w), 0.0, 1.0); - float atten = edge * edge / max(distance2, PROBE_LIGHT_MIN_DISTANCE2); + float edge = clamp(1.0 - distance2 * distance2 * (lamp.w * lamp.w), 0.0, 1.0); + // as lamp_falloff() of common.glsl: the soft falloff (a negative radius), or the light of the sphere, which is the one of a + // point outside it + float radius = cab_probe_lamp_shape[i].y; + float falloff = (radius < 0.0) ? 1.0 / max(distance2 + radius * radius, 1e-6) : 1.0 / max(distance2, (radius > 0.0) ? radius * radius : PROBE_LIGHT_MIN_DISTANCE2); + float atten = edge * edge * falloff; + // the cone of a spot light + vec4 cone = cab_probe_lamp_cone[i]; + if (cone.w > -1.0) + atten *= smoothstep(cone.w, cab_probe_lamp_shape[i].x, dot(-light_dir, normalize(cone.xyz))); float NdotL = max(dot(normal, light_dir), 0.0); - if ((atten > 0.0) && (NdotL > 0.0) && (cab_probe_lamp_color.a > 0.5)) - atten *= 1.0 - probe_lamp_shadow(-tolight, normal); - light += cab_probe_lamp_color.rgb * (NdotL * atten); + if ((atten > 0.0) && (NdotL > 0.0) && (cab_probe_lamp_color[i].a > 0.5)) + atten *= 1.0 - probe_lamp_shadow(i, position - cab_probe_lamp_map[i].xyz, normal); + light += cab_probe_lamp_color[i].rgb * (NdotL * atten); } if (cab_probe_sun.w > 0.0) { diff --git a/shaders/vk14/common.glsl b/shaders/vk14/common.glsl index 1146a17c..3dc8df4b 100644 --- a/shaders/vk14/common.glsl +++ b/shaders/vk14/common.glsl @@ -98,6 +98,7 @@ #define MAX_CASCADES 3U #define MAX_PARAMS 3U #define MAX_HEADLIGHT_SHADOWS 4U +#define MAX_CAB_PROBE_LAMPS 16U #define CLUSTER_GRID_X 16 #define CLUSTER_GRID_Y 9 #define CLUSTER_GRID_Z 24 @@ -310,13 +311,16 @@ layout(std140, set = PASS_SET, binding = SCENE_UBO) uniform scene_ubo vec4 rain_wind; // the ambient occlusion in the light of the surfaces, see gl::scene_ubs vec4 ao_params; - // the probe of the occupied cab, and the lamp it's drawn with, see gl::scene_ubs + // the probe of the occupied cab, and the lights of the cab it's drawn with, see gl::scene_ubs mat4 cab_probe_view; vec4 cab_probe_box; vec4 cab_probe_params; - vec4 cab_probe_lamp; - vec4 cab_probe_lamp_color; - vec4 cab_probe_lamp_shadow; + vec4 cab_probe_lamp[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_color[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_shadow[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_cone[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_shape[MAX_CAB_PROBE_LAMPS]; + vec4 cab_probe_lamp_map[MAX_CAB_PROBE_LAMPS]; // the occlusion of the sky and of the sun by the scenery around the camera, see gl::scene_ubs vec4 sky_occlusion_origin; vec4 sky_occlusion_params;