mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-08 22:07:20 +01:00
Shade the sky and the sun by the scenery around the camera instead of the track environment flags
This commit is contained in:
@@ -39,14 +39,20 @@ bool has_ceiling(float const Ceiling)
|
||||
{
|
||||
return Ceiling < 0.5f * NONE_CEILING;
|
||||
}
|
||||
// the limits of the heights between which the key light is let through, relative to the top: lit always, never
|
||||
// the height above the top over which the key light comes, relative to the top: lit always, never
|
||||
float const LIT_ABOVE_ALWAYS{-1000.f};
|
||||
float const LIT_BELOW_ALWAYS{1000.f};
|
||||
float const LIT_NEVER_BELOW{-1000.f};
|
||||
float const LIT_ABOVE_NEVER{1000.f};
|
||||
// the height under the ceiling below which the key light comes in, world space: lit always, never
|
||||
float const LIT_BELOW_ALWAYS{1.0e4f};
|
||||
float const LIT_NEVER_BELOW{-1.0e4f};
|
||||
// how high above the top the share of the sky above it is worked out (a surface on the ground isn't held back by the slope it's on),
|
||||
// and how far below the ceiling the share under it (the middle of the height under a vault of a tunnel or a deck of a bridge)
|
||||
// and how far below the ceiling the share under it is worked out near the ceiling; the other share under it is worked out a metre
|
||||
// above the ground around (the ground under a viaduct sees the sky on the sides much more than a point right under the deck), at
|
||||
// most this far below the ceiling
|
||||
float const ABOVE_TOP{1.f};
|
||||
float const BELOW_CEILING{2.5f};
|
||||
float const DEEP_BELOW_CEILING_MAX{30.f};
|
||||
float const DEEP_BELOW_CEILING_UNKNOWN{6.f};
|
||||
// a fragment this far below the top of its texel is under it, fully this much further. as in light_common.glsl, sky_occlusion()
|
||||
float const COVER_START{0.75f};
|
||||
float const COVER_SPAN{1.f};
|
||||
@@ -81,31 +87,47 @@ std::uint64_t hash_mix_double(std::uint64_t Hash, double const Value)
|
||||
return hash_mix(Hash, bits);
|
||||
}
|
||||
|
||||
// the share of the sky of an elevation band, weighed by the cosine on a surface facing up, from the tangents of its ends
|
||||
// the share of the sky of an elevation band, from the tangents of its ends: the share of the solid angle of the hemisphere, not
|
||||
// weighed by the cosine (the ambient light lights the surfaces facing every way, the low sky lights the walls and the sides of what
|
||||
// stands under a roof)
|
||||
float sky_share(float const Tangent)
|
||||
{
|
||||
if (Tangent <= 0.f)
|
||||
return 0.f;
|
||||
if (Tangent >= 1.0e6f)
|
||||
return 1.f;
|
||||
auto const squared{Tangent * Tangent};
|
||||
return squared / (1.f + squared);
|
||||
return Tangent / std::sqrt(1.f + Tangent * Tangent);
|
||||
}
|
||||
|
||||
// the values of a texel nothing is known of or which holds nothing: the heights (no top, no ceiling, the key light under the ceiling
|
||||
// always, the deep share under it at no height) and the occlusion (the whole sky, the key light always)
|
||||
glm::vec4 const NEUTRAL_HEIGHTS{NONE_TOP, NONE_CEILING, LIT_BELOW_ALWAYS, NONE_TOP};
|
||||
glm::vec4 const NEUTRAL_OCCLUSION{1.f, 1.f, 1.f, LIT_ABOVE_ALWAYS};
|
||||
|
||||
float smoothstep(float const Edge0, float const Edge1, float const Value)
|
||||
{
|
||||
auto const t{std::clamp((Value - Edge0) / (Edge1 - Edge0), 0.f, 1.f)};
|
||||
return t * t * (3.f - 2.f * t);
|
||||
}
|
||||
|
||||
// the occlusion of a point at Height, over a texel of the given top and occlusion values: the share of the sky and of the key
|
||||
// light. the same as light_common.glsl, sky_occlusion_texel()
|
||||
glm::vec2 texel_occlusion(float const Top, glm::vec4 const &Occlusion, float const Height)
|
||||
// the occlusion of a point at Height, over a texel of the given heights (the top, the ceiling, the limit of the key light under the
|
||||
// ceiling, the height of the deep share under it) and occlusion values (the share above the top, under the ceiling near it and deep,
|
||||
// the limit of the key light above the top): the share of the sky and of the key light. the same as sky_occlusion.glsl
|
||||
glm::vec2 texel_occlusion(glm::vec4 const &Heights, glm::vec4 const &Occlusion, float const Height)
|
||||
{
|
||||
auto const cover{std::clamp((Top - Height - COVER_START) / COVER_SPAN, 0.f, 1.f)};
|
||||
auto const sky{Occlusion.x + (Occlusion.y - Occlusion.x) * cover};
|
||||
auto const suntop{smoothstep(-SUN_SOFTNESS, SUN_SOFTNESS, Height - (Top + Occlusion.z))};
|
||||
auto const sununder{smoothstep(-SUN_SOFTNESS, SUN_SOFTNESS, (Top + Occlusion.w) - Height)};
|
||||
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)};
|
||||
auto under{0.f};
|
||||
if (has_ceiling(ceiling))
|
||||
{
|
||||
auto const nearheight{ceiling - BELOW_CEILING};
|
||||
auto const t{std::clamp((Height - Heights.w) / std::max(nearheight - Heights.w, 1e-3f), 0.f, 1.f)};
|
||||
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 sununder{smoothstep(-SUN_SOFTNESS, SUN_SOFTNESS, Heights.z - Height)};
|
||||
return {sky, suntop + (sununder - suntop) * cover};
|
||||
}
|
||||
|
||||
@@ -217,21 +239,19 @@ void sky_occlusion::start(int const Level)
|
||||
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};
|
||||
m_heights.assign(texels, NONE_TOP);
|
||||
m_occlusion.assign(texels, glm::vec4{1.f, 1.f, LIT_ABOVE_ALWAYS, LIT_BELOW_ALWAYS});
|
||||
m_heights.assign(texels, NEUTRAL_HEIGHTS);
|
||||
m_occlusion.assign(texels, NEUTRAL_OCCLUSION);
|
||||
auto const tiletexels{static_cast<std::size_t>(m_layout.tile_texels) * m_layout.tile_texels};
|
||||
m_neutralheights.assign(tiletexels, NONE_TOP);
|
||||
m_neutralheights.assign(tiletexels, NEUTRAL_HEIGHTS);
|
||||
m_neutralocclusion.resize(tiletexels * 4);
|
||||
for (std::size_t i = 0; i < tiletexels; ++i)
|
||||
{
|
||||
m_neutralocclusion[i * 4 + 0] = glm::packHalf1x16(1.f);
|
||||
m_neutralocclusion[i * 4 + 1] = glm::packHalf1x16(1.f);
|
||||
m_neutralocclusion[i * 4 + 2] = glm::packHalf1x16(LIT_ABOVE_ALWAYS);
|
||||
m_neutralocclusion[i * 4 + 3] = glm::packHalf1x16(LIT_BELOW_ALWAYS);
|
||||
for (int c = 0; c < 4; ++c)
|
||||
m_neutralocclusion[i * 4 + c] = glm::packHalf1x16(NEUTRAL_OCCLUSION[c]);
|
||||
}
|
||||
for (int index = 0; index < static_cast<int>(count); ++index)
|
||||
{
|
||||
gpu_upload((index % m_layout.tiles) * m_layout.tile_texels, (index / m_layout.tiles) * m_layout.tile_texels, m_layout.tile_texels, m_neutralheights.data(), m_neutralocclusion.data());
|
||||
gpu_upload((index % m_layout.tiles) * m_layout.tile_texels, (index / m_layout.tiles) * m_layout.tile_texels, m_layout.tile_texels, glm::value_ptr(m_neutralheights.front()), m_neutralocclusion.data());
|
||||
}
|
||||
m_rawtop.assign(texels, NONE_TOP);
|
||||
m_rawceiling.assign(texels, NONE_CEILING);
|
||||
@@ -305,10 +325,10 @@ void sky_occlusion::neutral(int const Slot)
|
||||
for (int z = 0; z < px; ++z)
|
||||
{
|
||||
auto const row{static_cast<std::size_t>(z0 + z) * m_side + x0};
|
||||
std::fill_n(m_heights.begin() + row, px, NONE_TOP);
|
||||
std::fill_n(m_occlusion.begin() + row, px, glm::vec4{1.f, 1.f, LIT_ABOVE_ALWAYS, LIT_BELOW_ALWAYS});
|
||||
std::fill_n(m_heights.begin() + row, px, NEUTRAL_HEIGHTS);
|
||||
std::fill_n(m_occlusion.begin() + row, px, NEUTRAL_OCCLUSION);
|
||||
}
|
||||
gpu_upload(x0, z0, px, m_neutralheights.data(), m_neutralocclusion.data());
|
||||
gpu_upload(x0, z0, px, glm::value_ptr(m_neutralheights.front()), m_neutralocclusion.data());
|
||||
++m_uploaded;
|
||||
}
|
||||
|
||||
@@ -327,7 +347,7 @@ void sky_occlusion::apply(tile_result &Result)
|
||||
for (int x = 0; x < px; ++x)
|
||||
{
|
||||
auto const source{static_cast<std::size_t>(z) * px + x};
|
||||
m_heights[row + x] = Result.heights[source];
|
||||
m_heights[row + x] = glm::vec4{Result.heights[source * 4 + 0], Result.heights[source * 4 + 1], Result.heights[source * 4 + 2], Result.heights[source * 4 + 3]};
|
||||
m_occlusion[row + x] = glm::vec4{glm::unpackHalf1x16(Result.occlusion[source * 4 + 0]), glm::unpackHalf1x16(Result.occlusion[source * 4 + 1]),
|
||||
glm::unpackHalf1x16(Result.occlusion[source * 4 + 2]), glm::unpackHalf1x16(Result.occlusion[source * 4 + 3])};
|
||||
}
|
||||
@@ -1077,8 +1097,9 @@ void sky_occlusion::occlude(int const Slot, tile_result &Result)
|
||||
auto const rise{overhead ? 1e6f : keylight.y / across};
|
||||
|
||||
// the share of the sky seen from the height Origin over a texel, the search around it. a ceiling above the origin lets the sky
|
||||
// through below its underside, what has none blocks it from the horizon up to its top
|
||||
auto const sky = [&](int const X, int const Z, float const Origin) {
|
||||
// through below its underside, what has none blocks it from the horizon up to its top. Ground, when given, gets the lowest top
|
||||
// without a ceiling met below the origin (the ground around what stands over the texel), or keeps its value when there's none
|
||||
auto const sky = [&](int const X, int const Z, float const Origin, float *Ground) {
|
||||
float sum{0.f};
|
||||
for (auto const &direction : directions)
|
||||
{
|
||||
@@ -1095,7 +1116,9 @@ void sky_occlusion::occlude(int const Slot, tile_result &Result)
|
||||
high = std::min(high, (c - Origin) / distance);
|
||||
else if (t > Origin)
|
||||
low = std::max(low, (t - Origin) / distance);
|
||||
if (low >= high)
|
||||
else if ((Ground != nullptr) && has_top(t) && (false == has_ceiling(c)))
|
||||
*Ground = std::min(*Ground, t);
|
||||
if ((low >= high) && (Ground == nullptr))
|
||||
break;
|
||||
}
|
||||
sum += std::max(0.f, sky_share(high) - sky_share(low));
|
||||
@@ -1103,7 +1126,7 @@ void sky_occlusion::occlude(int const Slot, tile_result &Result)
|
||||
return sum / static_cast<float>(directions.size());
|
||||
};
|
||||
|
||||
Result.heights.resize(static_cast<std::size_t>(px) * px);
|
||||
Result.heights.resize(static_cast<std::size_t>(px) * px * 4);
|
||||
Result.occlusion.resize(static_cast<std::size_t>(px) * px * 4);
|
||||
for (int z = 0; z < px; ++z)
|
||||
{
|
||||
@@ -1112,18 +1135,22 @@ void sky_occlusion::occlude(int const Slot, tile_result &Result)
|
||||
auto const index{at(x, z)};
|
||||
auto const t0{top[index]};
|
||||
auto const c0{ceiling[index]};
|
||||
glm::vec4 value{1.f, 1.f, LIT_ABOVE_ALWAYS, LIT_BELOW_ALWAYS};
|
||||
auto heights{NEUTRAL_HEIGHTS};
|
||||
auto value{NEUTRAL_OCCLUSION};
|
||||
if (has_top(t0))
|
||||
{
|
||||
heights.x = t0;
|
||||
auto const origin{t0 + ABOVE_TOP};
|
||||
value.x = sky(x, z, origin);
|
||||
// under a top with no ceiling is the inside of what is solid (a hill, a wall seen edge on): no sky, no sun
|
||||
value.x = sky(x, z, origin, nullptr);
|
||||
// under a top with no ceiling is the inside of what is solid (a hill, a wall seen edge on): no sky, no key light
|
||||
value.y = 0.f;
|
||||
value.z = 0.f;
|
||||
heights.z = (sun ? LIT_NEVER_BELOW : LIT_BELOW_ALWAYS);
|
||||
// the key light above the top: the height above which the rays towards it pass over all
|
||||
if (false == sun)
|
||||
value.z = LIT_ABOVE_ALWAYS;
|
||||
value.w = LIT_ABOVE_ALWAYS;
|
||||
else if (false == keyup)
|
||||
value.z = LIT_BELOW_ALWAYS;
|
||||
value.w = LIT_ABOVE_NEVER;
|
||||
else if (false == overhead)
|
||||
{
|
||||
auto shadow{NONE_TOP};
|
||||
@@ -1138,19 +1165,25 @@ void sky_occlusion::occlude(int const Slot, tile_result &Result)
|
||||
continue;
|
||||
shadow = std::max(shadow, top[sample] - drop);
|
||||
}
|
||||
value.z = has_top(shadow) ? std::clamp(shadow - t0, LIT_ABOVE_ALWAYS, LIT_BELOW_ALWAYS) : LIT_ABOVE_ALWAYS;
|
||||
value.w = has_top(shadow) ? std::clamp(shadow - t0, LIT_ABOVE_ALWAYS, LIT_ABOVE_NEVER) : LIT_ABOVE_ALWAYS;
|
||||
}
|
||||
else
|
||||
value.z = LIT_ABOVE_ALWAYS;
|
||||
value.w = LIT_ABOVE_ALWAYS;
|
||||
if (has_ceiling(c0))
|
||||
{
|
||||
// under the ceiling
|
||||
// under the ceiling: the share near it, and the one a metre above the ground around (the deeper the space under the
|
||||
// ceiling, the more of the sky its bottom sees on the sides), which the shading blends by the height of the point
|
||||
heights.y = c0;
|
||||
auto const under{c0 - BELOW_CEILING};
|
||||
value.y = sky(x, z, under);
|
||||
auto ground{std::numeric_limits<float>::max()};
|
||||
value.y = sky(x, z, under, &ground);
|
||||
auto const deep{ground < c0 ? std::clamp(ground + 1.f, c0 - DEEP_BELOW_CEILING_MAX, under) : c0 - DEEP_BELOW_CEILING_UNKNOWN};
|
||||
heights.w = deep;
|
||||
value.z = (deep < under - 0.25f) ? sky(x, z, deep, nullptr) : value.y;
|
||||
if (false == sun)
|
||||
value.w = LIT_BELOW_ALWAYS;
|
||||
heights.z = LIT_BELOW_ALWAYS;
|
||||
else if (false == keyup)
|
||||
value.w = LIT_NEVER_BELOW;
|
||||
heights.z = LIT_NEVER_BELOW;
|
||||
else
|
||||
{
|
||||
// the height below which the rays towards the key light pass under the ceilings, out to the open
|
||||
@@ -1176,18 +1209,16 @@ void sky_occlusion::occlude(int const Slot, tile_result &Result)
|
||||
}
|
||||
}
|
||||
// the rays which don't come out within the reach stay in the tunnel
|
||||
value.w = (out && has_top(limit)) ? std::clamp(limit - t0, LIT_NEVER_BELOW, LIT_BELOW_ALWAYS) : LIT_NEVER_BELOW;
|
||||
heights.z = (out && has_top(limit)) ? limit : LIT_NEVER_BELOW;
|
||||
}
|
||||
}
|
||||
else
|
||||
value.w = (sun ? LIT_NEVER_BELOW : LIT_BELOW_ALWAYS);
|
||||
}
|
||||
auto const target{static_cast<std::size_t>(z) * px + x};
|
||||
Result.heights[target] = t0;
|
||||
Result.occlusion[target * 4 + 0] = glm::packHalf1x16(value.x);
|
||||
Result.occlusion[target * 4 + 1] = glm::packHalf1x16(value.y);
|
||||
Result.occlusion[target * 4 + 2] = glm::packHalf1x16(value.z);
|
||||
Result.occlusion[target * 4 + 3] = glm::packHalf1x16(value.w);
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
Result.heights[target * 4 + c] = heights[c];
|
||||
Result.occlusion[target * 4 + c] = glm::packHalf1x16(value[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1250,7 +1281,8 @@ void sky_occlusion::draw_debug_ui()
|
||||
auto const x{wrap(static_cast<int>(std::floor(wx / texel)), m_side)};
|
||||
auto const z{wrap(static_cast<int>(std::floor(wz / texel)), m_side)};
|
||||
auto const index{static_cast<std::size_t>(z) * m_side + x};
|
||||
auto const top{m_heights[index]};
|
||||
auto const &heights{m_heights[index]};
|
||||
auto const top{heights.x};
|
||||
auto const &occlusion{m_occlusion[index]};
|
||||
float value{0.f};
|
||||
switch (m_previewmode)
|
||||
@@ -1259,10 +1291,11 @@ void sky_occlusion::draw_debug_ui()
|
||||
value = occlusion.x;
|
||||
break;
|
||||
case 2:
|
||||
value = occlusion.y;
|
||||
// near the ground under them
|
||||
value = occlusion.z;
|
||||
break;
|
||||
case 3:
|
||||
value = texel_occlusion(top, occlusion, top + 0.1f).y;
|
||||
value = texel_occlusion(heights, occlusion, top + 0.1f).y;
|
||||
break;
|
||||
default:
|
||||
value = has_top(top) ? std::clamp(0.5f + static_cast<float>(top - camera.y) / 40.f, 0.f, 1.f) : 0.f;
|
||||
|
||||
@@ -117,11 +117,13 @@ class sky_occlusion
|
||||
void draw_debug_ui();
|
||||
|
||||
protected:
|
||||
// the textures of Side x Side texels: the height of the top (one 32 bit float per texel) and the occlusion (four 16 bit floats);
|
||||
// false when they can't be made
|
||||
// the textures of Side x Side texels: the heights (four 32 bit floats per texel: the top, the ceiling, the height below which the
|
||||
// key light comes in under the ceiling, the height the deep share of the sky under the ceiling is worked out at) and the occlusion
|
||||
// (four 16 bit floats: the share of the sky above the top, under the ceiling near it and deep, the height above the top over which
|
||||
// the key light comes); false when they can't be made
|
||||
virtual bool gpu_allocate(int const Side) = 0;
|
||||
virtual void gpu_release() = 0;
|
||||
// copies a square of Size x Size texels at X, Y of the textures: Heights one float per texel, Occlusion four halves per texel,
|
||||
// copies a square of Size x Size texels at X, Y of the textures: Heights four floats per texel, Occlusion four halves per texel,
|
||||
// rows along x, one after another along z
|
||||
virtual void gpu_upload(int const X, int const Y, int const Size, float const *Heights, std::uint16_t const *Occlusion) = 0;
|
||||
|
||||
@@ -141,7 +143,7 @@ class sky_occlusion
|
||||
std::uint32_t generation{0};
|
||||
std::vector<triangle> triangles;
|
||||
};
|
||||
// what it gives back: the top and the occlusion of the texels of a tile
|
||||
// what it gives back: the heights (four per texel) and the occlusion of the texels of a tile
|
||||
struct tile_result
|
||||
{
|
||||
int slot{0};
|
||||
@@ -193,11 +195,11 @@ class sky_occlusion
|
||||
glm::ivec2 m_cameratile{0};
|
||||
glm::dvec3 m_camera{0.0};
|
||||
glm::vec3 m_keylight{0.f, 1.f, 0.f}; // the direction the thread was given last
|
||||
// the copy of the maps the cpu samples (sample()): the top, and the occlusion as floats
|
||||
std::vector<float> m_heights;
|
||||
// the copy of the maps the cpu samples (sample()): the heights, and the occlusion as floats
|
||||
std::vector<glm::vec4> m_heights;
|
||||
std::vector<glm::vec4> m_occlusion;
|
||||
// neutral tiles, uploaded when a tile comes into the square
|
||||
std::vector<float> m_neutralheights;
|
||||
std::vector<glm::vec4> m_neutralheights;
|
||||
std::vector<std::uint16_t> m_neutralocclusion;
|
||||
std::size_t m_checkslot{0}; // the slot whose signature is checked next
|
||||
scene::basic_region const *m_region{nullptr};
|
||||
|
||||
Reference in New Issue
Block a user