mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-28 21:37:31 +01:00
Hold the sky back only around tunnels and keep the cab lit by day
This commit is contained in:
@@ -70,6 +70,7 @@ existed.
|
||||
| `gfx.postfx.autoexposure.key` | `0.3` | average luminance of the image the exposure is set for |
|
||||
| `gfx.postfx.autoexposure.min` | `-1.0` | the most the exposure darkens a bright image, EV |
|
||||
| `gfx.postfx.autoexposure.max` | `0.5` | the most the exposure brightens a dark image, EV. Kept low on purpose: tunnels and the night are not meant to be brightened much |
|
||||
| `gfx.postfx.autoexposure.max.cab` | `2.0` | the same seen from the cab by day: the eye takes to the light of the cab, which the bright windows would leave dark. It goes down to `gfx.postfx.autoexposure.max` with the daylight around the cab (the light level of the simulation, the occlusion of the sky): at night and in tunnels it's that one |
|
||||
| `gfx.postfx.autoexposure.speed.dark` | `1.0` | rate of adaptation to a darker image, 1/s |
|
||||
| `gfx.postfx.autoexposure.speed.bright` | `3.0` | rate of adaptation to a brighter image, 1/s |
|
||||
| `gfx.postfx.autoexposure.compensation` | `0.0` | fixed exposure correction, EV; applies also with the automatic exposure off |
|
||||
@@ -270,6 +271,10 @@ so its probe is drawn without the sun and again with it in the next frame. The c
|
||||
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 parts of the cab of the `sunless` materials get no light of the sun; where it falls on them the ambient light is raised in the
|
||||
proportion their shadow tone gives (at most four times), and their shadow (of the walls of the cab, of the clouds) leaves the
|
||||
ambient light as it is. Before, the shadow darkened it to the shadow tone, so the whole cab went dark as in a tunnel.
|
||||
|
||||
**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
|
||||
@@ -306,6 +311,14 @@ Things narrower than about three texels (poles, masts, fences) are left out. Fro
|
||||
and buildings towards it, under the ceiling what the rays reach under the vault from the portal (a low sun shines a little way
|
||||
into a tunnel).
|
||||
|
||||
With `gfx.skyvisibility.tunnels yes` (the default) all this is done only around the tracks with the `tunnel` flag (see the table):
|
||||
the flag tells where a tunnel is, the geometry how dark it is and where it begins.
|
||||
|
||||
A point is taken as under the scenery only where a ceiling is over it (a tunnel, a bridge, a roof). Under the top with no
|
||||
ceiling over it, which a surface that is seen can't really be (the edges of platforms and walls the texels are blended over,
|
||||
holes cut in the terrain, parts of models sunk in the ground), it's lit as at the top, in the open: no darkness of a tunnel
|
||||
anywhere but under something.
|
||||
|
||||
The shading takes the ambient light, the reflection of the sky and the sun by them, wherever a surface is (the half of a train
|
||||
in the tunnel is dark, the other half in the light, with the passage over a few metres at the portal). The headlights, the lamps
|
||||
and the light of the cab are direct light and are not held back; the headlights fade in daylight by the light around the
|
||||
@@ -330,6 +343,7 @@ and the scenery is lit as in the open everywhere.
|
||||
| `gfx.skyvisibility` | `medium` | `no`/`low`/`medium`/`high` (or 0..3), see above |
|
||||
| `gfx.skyvisibility.sun yes/no` | `yes` | the sun is held back too (beyond the reach of the shadow map, and in the tunnels whatever the shadow map holds); `no`: the sun only by the shadow map |
|
||||
| `gfx.skyvisibility.fog yes/no` | `yes` | the light of the volumetric fog is held back too |
|
||||
| `gfx.skyvisibility.tunnels yes/no` | `yes` | the occlusion only around the tracks with the `tunnel` flag: whole within 20 m of them, fading out by 35 m; there the geometry makes the tunnel dark (the portals, the passage at them, the half of a train in the tunnel), elsewhere the scenery is lit as in the open (no darkening between platforms, in cuttings, under shelters). `no`: everywhere, by the geometry alone |
|
||||
| `gfx.tunnelflags yes/no` | `no` | `yes`: the flags `tunnel`/`canyon` of the tracks darken the tracks and the vehicles on them, as before (on top of the occlusion) |
|
||||
|
||||
All of them can be changed while the simulator runs, in the **Sky and exposure** debug window, which also shows views of the
|
||||
|
||||
@@ -11,7 +11,12 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "rendering/common/exposure.h"
|
||||
|
||||
#include "gl/ubo.h"
|
||||
#include "rendering/common/skyocclusion.h"
|
||||
#include "simulation/simulation.h"
|
||||
#include "utilities/Globals.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
#include "vehicle/Train.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -29,6 +34,18 @@ float const WHITE_BALANCE_REFERENCE{6500.f};
|
||||
// distance off the daylight locus (duv, cie 1960 ucs) of the tint at its ends (-1, 1)
|
||||
float const WHITE_BALANCE_TINT_DUV{0.02f};
|
||||
|
||||
// how much of the daylight is around the camera in the cab of its vehicle, 0..1: 0 out of the cab, at night, in a tunnel. the eye
|
||||
// seated in the cab takes to its light, which the image of the cab with the bright windows would leave dark
|
||||
float cab_daylight()
|
||||
{
|
||||
if (FreeFlyModeFlag || (false == Global.render_cab) || (simulation::Train == nullptr))
|
||||
return 0.f;
|
||||
auto const *vehicle{simulation::Train->Dynamic()};
|
||||
if ((vehicle == nullptr) || (false == vehicle->bDisplayCab) || (vehicle->mdKabina == nullptr) || (vehicle->mdKabina == vehicle->mdModel))
|
||||
return 0.f;
|
||||
return std::clamp(static_cast<float>(Global.fLuminance) * gfx::vehicle_light_level(vehicle), 0.f, 1.f);
|
||||
}
|
||||
|
||||
// chromaticity (cie xy) of the cie daylight of the correlated colour temperature Kelvin; the formula holds for 4000..25000 K
|
||||
glm::dvec2 daylight_chromaticity(double const Kelvin)
|
||||
{
|
||||
@@ -94,7 +111,9 @@ glm::vec4 exposure_settings::adaptation(double const Deltatime)
|
||||
|
||||
glm::vec2 exposure_settings::exposure_range()
|
||||
{
|
||||
return glm::vec2{std::exp2(Global.gfx_postfx_autoexposure_min), std::exp2(Global.gfx_postfx_autoexposure_max)};
|
||||
// seen from the cab by day the image may be brightened more (gfx.postfx.autoexposure.max.cab), by the daylight around the cab
|
||||
auto const highest{std::max(Global.gfx_postfx_autoexposure_max, Global.gfx_postfx_autoexposure_max + (Global.gfx_postfx_autoexposure_max_cab - Global.gfx_postfx_autoexposure_max) * cab_daylight())};
|
||||
return glm::vec2{std::exp2(Global.gfx_postfx_autoexposure_min), std::exp2(highest)};
|
||||
}
|
||||
|
||||
std::vector<glm::ivec2> exposure_settings::bloom_sizes(int const Width, int const Height)
|
||||
@@ -137,6 +156,7 @@ void exposure_settings::draw_debug_ui(float const *Readback)
|
||||
ImGui::SliderFloat("key", &Global.gfx_postfx_autoexposure_key, 0.02f, 2.f, "%.3f", ImGuiSliderFlags_Logarithmic);
|
||||
ImGui::SliderFloat("min EV", &Global.gfx_postfx_autoexposure_min, -4.f, 0.f);
|
||||
ImGui::SliderFloat("max EV", &Global.gfx_postfx_autoexposure_max, 0.f, 4.f);
|
||||
ImGui::SliderFloat("max EV in the cab by day", &Global.gfx_postfx_autoexposure_max_cab, 0.f, 4.f);
|
||||
ImGui::SliderFloat("adaptation to dark (1/s)", &Global.gfx_postfx_autoexposure_speed_dark, 0.05f, 10.f);
|
||||
ImGui::SliderFloat("adaptation to bright (1/s)", &Global.gfx_postfx_autoexposure_speed_bright, 0.05f, 10.f);
|
||||
ImGui::SliderFloat("compensation EV", &Global.gfx_postfx_autoexposure_compensation, -4.f, 4.f);
|
||||
|
||||
@@ -21,6 +21,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "utilities/Logs.h"
|
||||
#include "utilities/utilities.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
#include "world/Track.h"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
@@ -60,6 +61,11 @@ float const COVER_SPAN{1.f};
|
||||
float const SUN_SOFTNESS{0.6f};
|
||||
// a key light moving more than this (the cosine of the angle) has the occlusion of the key light worked out anew
|
||||
double const KEYLIGHT_MOVED{0.99999}; // ~0.26 degree
|
||||
// with Global.gfx_skyvisibility_tunnels: the occlusion is kept whole within this distance of a track with the tunnel flag and fades
|
||||
// out by the second one, metres; the tracks are followed in steps of at most the third
|
||||
float const TUNNEL_MASK_FULL{20.f};
|
||||
float const TUNNEL_MASK_REACH{35.f};
|
||||
double const TUNNEL_MASK_STEP{2.0};
|
||||
|
||||
sky_occlusion *g_current{nullptr};
|
||||
|
||||
@@ -117,7 +123,9 @@ glm::vec2 texel_occlusion(glm::vec4 const &Heights, glm::vec4 const &Occlusion,
|
||||
{
|
||||
auto const top{Heights.x};
|
||||
auto const ceiling{Heights.y};
|
||||
auto const cover{std::clamp((top - Height - COVER_START) / COVER_SPAN, 0.f, 1.f)};
|
||||
// under the scenery only where a ceiling is over the point; under the top with nothing over it it's taken as at the top
|
||||
auto const roofed{has_ceiling(ceiling) ? std::clamp(ceiling - Height + 1.f, 0.f, 1.f) : 0.f};
|
||||
auto const cover{std::clamp((top - Height - COVER_START) / COVER_SPAN, 0.f, 1.f) * roofed};
|
||||
auto under{0.f};
|
||||
if (has_ceiling(ceiling))
|
||||
{
|
||||
@@ -126,7 +134,7 @@ glm::vec2 texel_occlusion(glm::vec4 const &Heights, glm::vec4 const &Occlusion,
|
||||
under = Occlusion.z + (Occlusion.y - Occlusion.z) * t;
|
||||
}
|
||||
auto const sky{Occlusion.x + (under - Occlusion.x) * cover};
|
||||
auto const suntop{smoothstep(-SUN_SOFTNESS, SUN_SOFTNESS, Height - (top + Occlusion.w))};
|
||||
auto const suntop{smoothstep(-SUN_SOFTNESS, SUN_SOFTNESS, std::max(Height, top - COVER_START) - (top + Occlusion.w))};
|
||||
auto const sununder{smoothstep(-SUN_SOFTNESS, SUN_SOFTNESS, Heights.z - Height)};
|
||||
return {sky, suntop + (sununder - suntop) * cover};
|
||||
}
|
||||
@@ -236,6 +244,7 @@ void sky_occlusion::start(int const Level)
|
||||
m_side = side;
|
||||
m_level = Level;
|
||||
m_sun = Global.gfx_skyvisibility_sun;
|
||||
m_tunnels = Global.gfx_skyvisibility_tunnels;
|
||||
auto const count{static_cast<std::size_t>(m_layout.tiles * m_layout.tiles)};
|
||||
m_slots.assign(count, slot{});
|
||||
auto const texels{static_cast<std::size_t>(side) * side};
|
||||
@@ -257,6 +266,7 @@ void sky_occlusion::start(int const Level)
|
||||
m_rawceiling.assign(texels, NONE_CEILING);
|
||||
m_top.assign(texels, NONE_TOP);
|
||||
m_ceiling.assign(texels, NONE_CEILING);
|
||||
m_mask.assign(texels, m_tunnels ? 0.f : 1.f);
|
||||
m_workertiles.assign(count, glm::ivec2{std::numeric_limits<int>::min()});
|
||||
m_workergenerations.assign(count, 0);
|
||||
m_workerraster.assign(count, false);
|
||||
@@ -295,6 +305,7 @@ void sky_occlusion::stop()
|
||||
m_rawceiling.clear();
|
||||
m_top.clear();
|
||||
m_ceiling.clear();
|
||||
m_mask.clear();
|
||||
m_jobs.clear();
|
||||
m_results.clear();
|
||||
if (g_current == this)
|
||||
@@ -359,7 +370,7 @@ void sky_occlusion::apply(tile_result &Result)
|
||||
void sky_occlusion::update(scene::basic_region *Region, glm::dvec3 const &Camera, glm::vec3 const &Keylight)
|
||||
{
|
||||
auto const level{std::clamp(Global.gfx_skyvisibility, 0, 3)};
|
||||
if ((level != m_level) || ((m_side > 0) && (m_sun != Global.gfx_skyvisibility_sun)))
|
||||
if ((level != m_level) || ((m_side > 0) && ((m_sun != Global.gfx_skyvisibility_sun) || (m_tunnels != Global.gfx_skyvisibility_tunnels))))
|
||||
{
|
||||
stop();
|
||||
if (level > 0)
|
||||
@@ -453,6 +464,8 @@ void sky_occlusion::update(scene::basic_region *Region, glm::dvec3 const &Camera
|
||||
job.generation = target.generation;
|
||||
target.signature = signature(Region, target.tile);
|
||||
gather(Region, target.tile, job.triangles);
|
||||
if (m_tunnels)
|
||||
gather_tunnels(Region, target.tile, job.tunnels);
|
||||
m_gathertime += std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - gatherstart).count();
|
||||
m_triangles = static_cast<std::uint32_t>(job.triangles.size());
|
||||
if (target.regather)
|
||||
@@ -601,6 +614,14 @@ std::uint64_t sky_occlusion::signature(scene::basic_region *Region, glm::ivec2 c
|
||||
continue;
|
||||
hash = hash_mix(hash, static_cast<std::uint64_t>(cell.m_shapesopaque.size()));
|
||||
hash = hash_mix(hash, static_cast<std::uint64_t>(cell.m_instancesopaque.size()));
|
||||
if (m_tunnels)
|
||||
{
|
||||
for (auto const *path : cell.m_paths)
|
||||
{
|
||||
if ((path != nullptr) && (path->eEnvironment == e_tunnel))
|
||||
hash = hash_mix(hash, static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(path)));
|
||||
}
|
||||
}
|
||||
for (auto &shape : cell.m_shapesopaque)
|
||||
{
|
||||
auto const &data{shape.data()};
|
||||
@@ -718,6 +739,46 @@ void sky_occlusion::gather(scene::basic_region *Region, glm::ivec2 const &Tile,
|
||||
}
|
||||
}
|
||||
|
||||
void sky_occlusion::gather_tunnels(scene::basic_region *Region, glm::ivec2 const &Tile, std::vector<glm::vec2> &Points)
|
||||
{
|
||||
auto const tilesize{static_cast<double>(m_layout.tile)};
|
||||
auto const reach{static_cast<double>(TUNNEL_MASK_REACH)};
|
||||
glm::dvec2 const corner{Tile.x * tilesize, Tile.y * tilesize};
|
||||
glm::dvec2 const low{corner - glm::dvec2{reach}};
|
||||
glm::dvec2 const high{corner + glm::dvec2{tilesize + reach}};
|
||||
glm::dvec3 const center{corner.x + 0.5 * tilesize, 0.0, corner.y + 0.5 * tilesize};
|
||||
std::vector<TTrack const *> done;
|
||||
auto const &found{Region->sections(center, static_cast<float>(tilesize * 0.75 + reach))};
|
||||
std::vector<scene::basic_section *> const sections{found.begin(), found.end()};
|
||||
for (auto *section : sections)
|
||||
{
|
||||
for (auto &cell : section->m_cells)
|
||||
{
|
||||
auto const &area{cell.area()};
|
||||
auto const radius{static_cast<double>(area.radius)};
|
||||
if ((area.center.x + radius < low.x) || (area.center.x - radius > high.x) || (area.center.z + radius < low.y) || (area.center.z - radius > high.y))
|
||||
continue;
|
||||
for (auto const *path : cell.m_paths)
|
||||
{
|
||||
if ((path == nullptr) || (path->eEnvironment != e_tunnel) || (path->CurrentSegment() == nullptr))
|
||||
continue;
|
||||
if (std::find(done.begin(), done.end(), path) != done.end())
|
||||
continue;
|
||||
done.push_back(path);
|
||||
auto const &segment{*path->CurrentSegment()};
|
||||
auto const steps{std::max(1, static_cast<int>(std::ceil(segment.GetLength() / TUNNEL_MASK_STEP)))};
|
||||
for (int step = 0; step <= steps; ++step)
|
||||
{
|
||||
auto const point{segment.FastGetPoint(static_cast<double>(step) / steps)};
|
||||
if ((point.x < low.x) || (point.x > high.x) || (point.z < low.y) || (point.z > high.y))
|
||||
continue;
|
||||
Points.emplace_back(static_cast<float>(point.x - corner.x), static_cast<float>(point.z - corner.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sky_occlusion::gather_model(TModel3d *Model, std::uint32_t const Alpha, glm::dmat4 const &Transform, glm::dvec2 const &Corner, glm::dvec2 const &Far, std::vector<triangle> &Triangles)
|
||||
{
|
||||
if ((Model == nullptr) || (Model->Root == nullptr))
|
||||
@@ -811,6 +872,7 @@ void sky_occlusion::work()
|
||||
std::fill_n(m_rawceiling.begin() + row, px, NONE_CEILING);
|
||||
std::fill_n(m_top.begin() + row, px, NONE_TOP);
|
||||
std::fill_n(m_ceiling.begin() + row, px, NONE_CEILING);
|
||||
std::fill_n(m_mask.begin() + row, px, m_tunnels ? 0.f : 1.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -897,6 +959,20 @@ void sky_occlusion::rasterize(raster_job &Job)
|
||||
auto const row{static_cast<std::size_t>(z0 + z) * m_side + x0};
|
||||
std::fill_n(m_rawtop.begin() + row, px, NONE_TOP);
|
||||
std::fill_n(m_rawceiling.begin() + row, px, NONE_CEILING);
|
||||
// the mask of the tunnels: by the distance of the middle of a texel from the nearest point along a track with the flag
|
||||
for (int x = 0; x < px; ++x)
|
||||
{
|
||||
auto weight{1.f};
|
||||
if (m_tunnels)
|
||||
{
|
||||
glm::vec2 const middle{(static_cast<float>(x) + 0.5f) * texel, (static_cast<float>(z) + 0.5f) * texel};
|
||||
auto nearest{TUNNEL_MASK_REACH * TUNNEL_MASK_REACH};
|
||||
for (auto const &point : Job.tunnels)
|
||||
nearest = std::min(nearest, glm::dot(point - middle, point - middle));
|
||||
weight = 1.f - smoothstep(TUNNEL_MASK_FULL, TUNNEL_MASK_REACH, std::sqrt(nearest));
|
||||
}
|
||||
m_mask[row + x] = weight;
|
||||
}
|
||||
}
|
||||
for (auto const &entry : Job.triangles)
|
||||
{
|
||||
@@ -1137,7 +1213,9 @@ void sky_occlusion::occlude(int const Slot, tile_result &Result)
|
||||
auto const c0{ceiling[index]};
|
||||
auto heights{NEUTRAL_HEIGHTS};
|
||||
auto value{NEUTRAL_OCCLUSION};
|
||||
if (has_top(t0))
|
||||
// away from the tunnels (Global.gfx_skyvisibility_tunnels) the texel is in the open, fading into it at the edge of the mask
|
||||
auto const weight{m_mask[static_cast<std::size_t>(wrap(tile.y * px + z, m_side)) * m_side + wrap(tile.x * px + x, m_side)]};
|
||||
if (has_top(t0) && (weight > 0.f))
|
||||
{
|
||||
heights.x = t0;
|
||||
auto const origin{t0 + ABOVE_TOP};
|
||||
@@ -1213,6 +1291,22 @@ void sky_occlusion::occlude(int const Slot, tile_result &Result)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (weight <= 0.f)
|
||||
{
|
||||
heights = NEUTRAL_HEIGHTS;
|
||||
value = NEUTRAL_OCCLUSION;
|
||||
}
|
||||
else if (weight < 1.f)
|
||||
{
|
||||
value.x = 1.f - (1.f - value.x) * weight;
|
||||
value.y = 1.f - (1.f - value.y) * weight;
|
||||
value.z = 1.f - (1.f - value.z) * weight;
|
||||
if (weight < 0.5f)
|
||||
{
|
||||
heights.z = LIT_BELOW_ALWAYS;
|
||||
value.w = LIT_ABOVE_ALWAYS;
|
||||
}
|
||||
}
|
||||
auto const target{static_cast<std::size_t>(z) * px + x};
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
@@ -1253,6 +1347,7 @@ void sky_occlusion::draw_debug_ui()
|
||||
ImGui::Combo("occlusion of the sky by the scenery (gfx.skyvisibility)", &Global.gfx_skyvisibility, levels, IM_ARRAYSIZE(levels));
|
||||
ImGui::Checkbox("the sun held back too (gfx.skyvisibility.sun)", &Global.gfx_skyvisibility_sun);
|
||||
ImGui::Checkbox("in the volumetric fog (gfx.skyvisibility.fog)", &Global.gfx_skyvisibility_fog);
|
||||
ImGui::Checkbox("only around the tracks with the tunnel flag (gfx.skyvisibility.tunnels)", &Global.gfx_skyvisibility_tunnels);
|
||||
ImGui::Checkbox("environment flags of the tracks darken as before (gfx.tunnelflags)", &Global.gfx_tunnelflags);
|
||||
if (m_side <= 0)
|
||||
return;
|
||||
|
||||
@@ -142,6 +142,8 @@ class sky_occlusion
|
||||
int slot{0};
|
||||
std::uint32_t generation{0};
|
||||
std::vector<triangle> triangles;
|
||||
// points along the tracks with the tunnel flag over the tile and around it, x and z relative to the corner of the tile
|
||||
std::vector<glm::vec2> tunnels;
|
||||
};
|
||||
// what it gives back: the heights (four per texel) and the occlusion of the texels of a tile
|
||||
struct tile_result
|
||||
@@ -175,6 +177,8 @@ class sky_occlusion
|
||||
void gather(scene::basic_region *Region, glm::ivec2 const &Tile, std::vector<triangle> &Triangles);
|
||||
void gather_model(TModel3d *Model, std::uint32_t const Alpha, glm::dmat4 const &Transform, glm::dvec2 const &Corner, glm::dvec2 const &Far, std::vector<triangle> &Triangles);
|
||||
void gather_submodel(TSubModel *Submodel, std::uint32_t const Alpha, glm::dmat4 const &Transform, glm::dvec2 const &Corner, glm::dvec2 const &Far, std::vector<triangle> &Triangles);
|
||||
// the points along the tracks with the tunnel flag within the reach of the mask of the tunnels of a tile (gfx_skyvisibility_tunnels)
|
||||
void gather_tunnels(scene::basic_region *Region, glm::ivec2 const &Tile, std::vector<glm::vec2> &Points);
|
||||
// what the scenery over a tile is made of, cheap to work out: a change of it means the tile is gathered again
|
||||
std::uint64_t signature(scene::basic_region *Region, glm::ivec2 const &Tile);
|
||||
// the values of a tile nothing is known of yet, in the textures and in the copy of the cpu
|
||||
@@ -191,6 +195,8 @@ class sky_occlusion
|
||||
layout m_layout;
|
||||
int m_level{0};
|
||||
bool m_sun{true};
|
||||
// the occlusion only around the tracks with the tunnel flag, as at the start (Global.gfx_skyvisibility_tunnels)
|
||||
bool m_tunnels{true};
|
||||
std::vector<slot> m_slots;
|
||||
glm::ivec2 m_cameratile{0};
|
||||
glm::dvec3 m_camera{0.0};
|
||||
@@ -220,6 +226,9 @@ class sky_occlusion
|
||||
std::thread m_thread;
|
||||
// the maps of the thread: the top and the ceiling as rasterized, and opened
|
||||
std::vector<float> m_rawtop, m_rawceiling, m_top, m_ceiling;
|
||||
// how much of the occlusion a texel keeps, 0..1, by its distance from the tracks with the tunnel flag (1 everywhere without
|
||||
// m_tunnels)
|
||||
std::vector<float> m_mask;
|
||||
|
||||
// statistics
|
||||
std::uint32_t m_gathered{0};
|
||||
|
||||
@@ -825,7 +825,7 @@ vec3 cab_probe_reflection(vec3 surroundings, vec3 fragnormal, float roughness, v
|
||||
// Texturecolor is the albedo (the base colour of a metal), Specularity the specular level of a dielectric (0.5 is the
|
||||
// reflectance of 4% at normal incidence), Reflectivity how much of the reflected surroundings is the cubemap rather than the
|
||||
// ambient light, Shadowtone the share of the sun left in its shadow (1 for the materials which take no shadows).
|
||||
// a Sunless surface (the interior of a cab) gets no light of the sun, its shadow only darkens the ambient light
|
||||
// a Sunless surface (the interior of a cab) gets no light of the sun, where the sun falls it brightens the ambient light
|
||||
vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone, bool sunless)
|
||||
{
|
||||
// Diffuse colour is applied once, to the albedo; the light terms below are not scaled by it again
|
||||
@@ -964,10 +964,12 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
|
||||
float cubemap_shadow = 1.0;
|
||||
if (sunless)
|
||||
{
|
||||
// the sun adds no light in the cab, where it would fall its shadow darkens the ambient light
|
||||
// the sun adds no light of its own in the cab; where it falls the ambient light is raised as much as the shadow used to lower
|
||||
// it (the share Shadowtone, at most 4 times), so the shadows of the walls of the cab and of the clouds leave the ambient light
|
||||
// as it is instead of darkening the cab
|
||||
float diffuseamount = sun_NdotL * lights[0].intensity;
|
||||
float darkness = (shadowtone < 1.0 ? max(shadow, 1.0 - cloudsun) : 0.0);
|
||||
fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * darkness, 0.0, 1.0));
|
||||
float sunlit = (shadowtone < 1.0 ? 1.0 - max(shadow, 1.0 - cloudsun) : 0.0);
|
||||
fragcolor *= 1.0 + clamp(diffuseamount * sunlit, 0.0, 1.0) * (1.0 - shadowtone) / max(shadowtone, 0.25);
|
||||
}
|
||||
else
|
||||
cubemap_shadow = 1.0 - (1.0 - shadowtone) * shadow * 0.5;
|
||||
@@ -1003,7 +1005,7 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl
|
||||
return shade_surface(fragcolor, fragnormal, texturecolor, reflectivity, specularity, shadowtone, false);
|
||||
}
|
||||
|
||||
// the interior of a cab: no light of the sun, its shadow darkens the ambient light
|
||||
// the interior of a cab: no light of the sun, where it falls it brightens the ambient light
|
||||
vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone)
|
||||
{
|
||||
return shade_surface(fragcolor, fragnormal, texturecolor, reflectivity, specularity, shadowtone, true);
|
||||
|
||||
@@ -57,13 +57,17 @@ vec2 sky_occlusion_world(vec3 world, vec3 worldnormal)
|
||||
vec4 skydeep = textureGather(sky_occlusion_map, uv, 2);
|
||||
vec4 litabove = textureGather(sky_occlusion_map, uv, 3);
|
||||
vec4 height = vec4(point.y + sky_occlusion_origin.z);
|
||||
vec4 cover = clamp((tops - height - SKY_OCCLUSION_COVER_START) / SKY_OCCLUSION_COVER_SPAN, 0.0, 1.0);
|
||||
// a point is under the scenery only where a ceiling is over it (the vault of a tunnel, the deck of a bridge, a roof): under the top
|
||||
// with nothing over it (the edge of a platform or a wall the texels are blended over, a hole cut in the terrain, a part of a
|
||||
// model in the ground) it's taken as at the top, in the open
|
||||
vec4 roofed = step(ceilings, vec4(5000.0)) * clamp(ceilings - height + 1.0, 0.0, 1.0);
|
||||
vec4 cover = clamp((tops - height - SKY_OCCLUSION_COVER_START) / SKY_OCCLUSION_COVER_SPAN, 0.0, 1.0) * roofed;
|
||||
vec4 nearheight = ceilings - SKY_OCCLUSION_BELOW_CEILING;
|
||||
vec4 skyunder = mix(skydeep, skynear, clamp((height - deeps) / max(nearheight - deeps, vec4(1e-3)), 0.0, 1.0));
|
||||
// no ceiling: the inside of what is solid
|
||||
skyunder *= step(ceilings, vec4(5000.0));
|
||||
vec4 sky = mix(skytop, skyunder, cover);
|
||||
vec4 suntop = smoothstep(vec4(-SKY_OCCLUSION_SUN_SOFTNESS), vec4(SKY_OCCLUSION_SUN_SOFTNESS), height - (tops + litabove));
|
||||
vec4 suntop = smoothstep(vec4(-SKY_OCCLUSION_SUN_SOFTNESS), vec4(SKY_OCCLUSION_SUN_SOFTNESS), max(height, tops - SKY_OCCLUSION_COVER_START) - (tops + litabove));
|
||||
vec4 sununder = smoothstep(vec4(-SKY_OCCLUSION_SUN_SOFTNESS), vec4(SKY_OCCLUSION_SUN_SOFTNESS), litbelow - height);
|
||||
vec4 sun = mix(suntop, sununder, cover);
|
||||
return mix(vec2(dot(sky, weights), dot(sun, weights)), vec2(1.0), fade);
|
||||
@@ -81,7 +85,11 @@ float sky_occlusion_cover(vec3 world)
|
||||
if (false == sky_occlusion_texels(world.xz, uv, weights, fade))
|
||||
return 0.0;
|
||||
vec4 tops = textureGather(sky_occlusion_height, uv, 0);
|
||||
vec4 cover = clamp((tops - vec4(world.y + sky_occlusion_origin.z) - SKY_OCCLUSION_COVER_START) / SKY_OCCLUSION_COVER_SPAN, 0.0, 1.0);
|
||||
vec4 ceilings = textureGather(sky_occlusion_height, uv, 1);
|
||||
vec4 height = vec4(world.y + sky_occlusion_origin.z);
|
||||
// under a ceiling only, as sky_occlusion_world() takes it
|
||||
vec4 roofed = step(ceilings, vec4(5000.0)) * clamp(ceilings - height + 1.0, 0.0, 1.0);
|
||||
vec4 cover = clamp((tops - height - SKY_OCCLUSION_COVER_START) / SKY_OCCLUSION_COVER_SPAN, 0.0, 1.0) * roofed;
|
||||
return dot(cover, weights) * (1.0 - fade);
|
||||
}
|
||||
|
||||
|
||||
@@ -838,7 +838,7 @@ vec3 cab_probe_reflection(vec3 surroundings, vec3 fragnormal, float roughness, v
|
||||
// Texturecolor is the albedo (the base colour of a metal), Specularity the specular level of a dielectric (0.5 is the
|
||||
// reflectance of 4% at normal incidence), Reflectivity how much of the reflected surroundings is the cubemap rather than the
|
||||
// ambient light, Shadowtone the share of the sun left in its shadow (1 for the materials which take no shadows).
|
||||
// a Sunless surface (the interior of a cab) gets no light of the sun, its shadow only darkens the ambient light
|
||||
// a Sunless surface (the interior of a cab) gets no light of the sun, where the sun falls it brightens the ambient light
|
||||
vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone, bool sunless)
|
||||
{
|
||||
// Diffuse colour is applied once, to the albedo; the light terms below are not scaled by it again
|
||||
@@ -978,10 +978,12 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
|
||||
float cubemap_shadow = 1.0;
|
||||
if (sunless)
|
||||
{
|
||||
// the sun adds no light in the cab, where it would fall its shadow darkens the ambient light
|
||||
// the sun adds no light of its own in the cab; where it falls the ambient light is raised as much as the shadow used to lower
|
||||
// it (the share Shadowtone, at most 4 times), so the shadows of the walls of the cab and of the clouds leave the ambient light
|
||||
// as it is instead of darkening the cab
|
||||
float diffuseamount = sun_NdotL * lights[0].intensity;
|
||||
float darkness = (shadowtone < 1.0 ? max(shadow, 1.0 - cloudsun) : 0.0);
|
||||
fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * darkness, 0.0, 1.0));
|
||||
float sunlit = (shadowtone < 1.0 ? 1.0 - max(shadow, 1.0 - cloudsun) : 0.0);
|
||||
fragcolor *= 1.0 + clamp(diffuseamount * sunlit, 0.0, 1.0) * (1.0 - shadowtone) / max(shadowtone, 0.25);
|
||||
}
|
||||
else
|
||||
cubemap_shadow = 1.0 - (1.0 - shadowtone) * shadow * 0.5;
|
||||
@@ -1017,7 +1019,7 @@ vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float refl
|
||||
return shade_surface(fragcolor, fragnormal, texturecolor, reflectivity, specularity, shadowtone, false);
|
||||
}
|
||||
|
||||
// the interior of a cab: no light of the sun, its shadow darkens the ambient light
|
||||
// the interior of a cab: no light of the sun, where it falls it brightens the ambient light
|
||||
vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone)
|
||||
{
|
||||
return shade_surface(fragcolor, fragnormal, texturecolor, reflectivity, specularity, shadowtone, true);
|
||||
|
||||
@@ -57,13 +57,17 @@ vec2 sky_occlusion_world(vec3 world, vec3 worldnormal)
|
||||
vec4 skydeep = textureGather(sky_occlusion_map, uv, 2);
|
||||
vec4 litabove = textureGather(sky_occlusion_map, uv, 3);
|
||||
vec4 height = vec4(point.y + sky_occlusion_origin.z);
|
||||
vec4 cover = clamp((tops - height - SKY_OCCLUSION_COVER_START) / SKY_OCCLUSION_COVER_SPAN, 0.0, 1.0);
|
||||
// a point is under the scenery only where a ceiling is over it (the vault of a tunnel, the deck of a bridge, a roof): under the top
|
||||
// with nothing over it (the edge of a platform or a wall the texels are blended over, a hole cut in the terrain, a part of a
|
||||
// model in the ground) it's taken as at the top, in the open
|
||||
vec4 roofed = step(ceilings, vec4(5000.0)) * clamp(ceilings - height + 1.0, 0.0, 1.0);
|
||||
vec4 cover = clamp((tops - height - SKY_OCCLUSION_COVER_START) / SKY_OCCLUSION_COVER_SPAN, 0.0, 1.0) * roofed;
|
||||
vec4 nearheight = ceilings - SKY_OCCLUSION_BELOW_CEILING;
|
||||
vec4 skyunder = mix(skydeep, skynear, clamp((height - deeps) / max(nearheight - deeps, vec4(1e-3)), 0.0, 1.0));
|
||||
// no ceiling: the inside of what is solid
|
||||
skyunder *= step(ceilings, vec4(5000.0));
|
||||
vec4 sky = mix(skytop, skyunder, cover);
|
||||
vec4 suntop = smoothstep(vec4(-SKY_OCCLUSION_SUN_SOFTNESS), vec4(SKY_OCCLUSION_SUN_SOFTNESS), height - (tops + litabove));
|
||||
vec4 suntop = smoothstep(vec4(-SKY_OCCLUSION_SUN_SOFTNESS), vec4(SKY_OCCLUSION_SUN_SOFTNESS), max(height, tops - SKY_OCCLUSION_COVER_START) - (tops + litabove));
|
||||
vec4 sununder = smoothstep(vec4(-SKY_OCCLUSION_SUN_SOFTNESS), vec4(SKY_OCCLUSION_SUN_SOFTNESS), litbelow - height);
|
||||
vec4 sun = mix(suntop, sununder, cover);
|
||||
return mix(vec2(dot(sky, weights), dot(sun, weights)), vec2(1.0), fade);
|
||||
@@ -81,7 +85,11 @@ float sky_occlusion_cover(vec3 world)
|
||||
if (false == sky_occlusion_texels(world.xz, uv, weights, fade))
|
||||
return 0.0;
|
||||
vec4 tops = textureGather(sky_occlusion_height, uv, 0);
|
||||
vec4 cover = clamp((tops - vec4(world.y + sky_occlusion_origin.z) - SKY_OCCLUSION_COVER_START) / SKY_OCCLUSION_COVER_SPAN, 0.0, 1.0);
|
||||
vec4 ceilings = textureGather(sky_occlusion_height, uv, 1);
|
||||
vec4 height = vec4(world.y + sky_occlusion_origin.z);
|
||||
// under a ceiling only, as sky_occlusion_world() takes it
|
||||
vec4 roofed = step(ceilings, vec4(5000.0)) * clamp(ceilings - height + 1.0, 0.0, 1.0);
|
||||
vec4 cover = clamp((tops - height - SKY_OCCLUSION_COVER_START) / SKY_OCCLUSION_COVER_SPAN, 0.0, 1.0) * roofed;
|
||||
return dot(cover, weights) * (1.0 - fade);
|
||||
}
|
||||
|
||||
|
||||
@@ -1889,6 +1889,7 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To
|
||||
else if( Token == "gfx.postfx.autoexposure.key" ) { readvalue( gfx_postfx_autoexposure_key, 0.001f, 100.f ); }
|
||||
else if( Token == "gfx.postfx.autoexposure.min" ) { readvalue( gfx_postfx_autoexposure_min, -16.f, 0.f ); }
|
||||
else if( Token == "gfx.postfx.autoexposure.max" ) { readvalue( gfx_postfx_autoexposure_max, 0.f, 16.f ); }
|
||||
else if( Token == "gfx.postfx.autoexposure.max.cab" ) { readvalue( gfx_postfx_autoexposure_max_cab, 0.f, 16.f ); }
|
||||
else if( Token == "gfx.postfx.autoexposure.speed.dark" ) { readvalue( gfx_postfx_autoexposure_speed_dark, 0.01f, 100.f ); }
|
||||
else if( Token == "gfx.postfx.autoexposure.speed.bright" ) { readvalue( gfx_postfx_autoexposure_speed_bright, 0.01f, 100.f ); }
|
||||
else if( Token == "gfx.postfx.autoexposure.compensation" ) { readvalue( gfx_postfx_autoexposure_compensation, -16.f, 16.f ); }
|
||||
@@ -1953,6 +1954,7 @@ global_settings::ConfigParse_gfx_sky( cParser &Parser, std::string_view const To
|
||||
0; }
|
||||
else if( Token == "gfx.skyvisibility.sun" ) { readswitch( gfx_skyvisibility_sun ); }
|
||||
else if( Token == "gfx.skyvisibility.fog" ) { readswitch( gfx_skyvisibility_fog ); }
|
||||
else if( Token == "gfx.skyvisibility.tunnels" ) { readswitch( gfx_skyvisibility_tunnels ); }
|
||||
else if( Token == "gfx.tunnelflags" ) { readswitch( gfx_tunnelflags ); }
|
||||
else if( Token == "gfx.lights.range" ) { readvalue( gfx_lights_range, 0.f, 2000.f ); }
|
||||
else if( Token == "gfx.lights.shadows" ) {
|
||||
@@ -2407,6 +2409,7 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
export_as_text( Output, "gfx.postfx.autoexposure.key", gfx_postfx_autoexposure_key );
|
||||
export_as_text( Output, "gfx.postfx.autoexposure.min", gfx_postfx_autoexposure_min );
|
||||
export_as_text( Output, "gfx.postfx.autoexposure.max", gfx_postfx_autoexposure_max );
|
||||
export_as_text( Output, "gfx.postfx.autoexposure.max.cab", gfx_postfx_autoexposure_max_cab );
|
||||
export_as_text( Output, "gfx.postfx.autoexposure.speed.dark", gfx_postfx_autoexposure_speed_dark );
|
||||
export_as_text( Output, "gfx.postfx.autoexposure.speed.bright", gfx_postfx_autoexposure_speed_bright );
|
||||
export_as_text( Output, "gfx.postfx.autoexposure.compensation", gfx_postfx_autoexposure_compensation );
|
||||
@@ -2440,6 +2443,7 @@ global_settings::export_as_text( std::ostream &Output ) const {
|
||||
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 );
|
||||
export_as_text( Output, "gfx.skyvisibility.tunnels", gfx_skyvisibility_tunnels );
|
||||
export_as_text( Output, "gfx.tunnelflags", gfx_tunnelflags );
|
||||
export_as_text( Output, "gfx.lights.range", gfx_lights_range );
|
||||
export_as_text( Output, "gfx.lights.shadows", std::string{ gfx_lights_shadows == 3 ? "high" : gfx_lights_shadows == 2 ? "medium" : gfx_lights_shadows == 1 ? "low" : "no" } );
|
||||
|
||||
@@ -466,6 +466,7 @@ struct global_settings {
|
||||
float gfx_postfx_autoexposure_key { 0.3f }; // average luminance of the image the exposure is set for
|
||||
float gfx_postfx_autoexposure_min { -1.f }; // lowest exposure correction, EV (darkening of bright images)
|
||||
float gfx_postfx_autoexposure_max { 0.5f }; // highest exposure correction, EV (brightening of dark images, tunnels)
|
||||
float gfx_postfx_autoexposure_max_cab { 2.f }; // the same seen from the cab by day: the eye takes to the light of the cab
|
||||
float gfx_postfx_autoexposure_speed_dark { 1.f }; // rate of adaptation to a darker image, 1/s
|
||||
float gfx_postfx_autoexposure_speed_bright { 3.f }; // rate of adaptation to a brighter image, 1/s
|
||||
float gfx_postfx_autoexposure_compensation { 0.f }; // fixed exposure correction, EV, applied with and without the adaptation
|
||||
@@ -523,6 +524,9 @@ struct global_settings {
|
||||
int gfx_skyvisibility { 2 };
|
||||
bool gfx_skyvisibility_sun { true };
|
||||
bool gfx_skyvisibility_fog { true };
|
||||
// the occlusion only around the tracks with the tunnel flag (the geometry there makes the tunnel dark, the portals, the passage at
|
||||
// them); elsewhere the scenery is lit as in the open. off: everywhere
|
||||
bool gfx_skyvisibility_tunnels { true };
|
||||
// the environment flags of the tracks (tunnel, canyon) darken the tracks and the vehicles on them, as before the track T. off: the
|
||||
// renderers leave them alone and the light comes from the geometry (gfx_skyvisibility); the flags stay in the scenery files for
|
||||
// the sound and the driver
|
||||
|
||||
Reference in New Issue
Block a user