mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 05:09:20 +02:00
Give the volumetric clouds lumps, sky and ground light and a fog colour by the angle from the sun
This commit is contained in:
@@ -196,7 +196,9 @@ are no longer loaded.
|
|||||||
- **Clouds** are ray marched through layers of the genera of aviation reports (cumulus, cumulonimbus, stratus, cirrus...),
|
- **Clouds** are ray marched through layers of the genera of aviation reports (cumulus, cumulonimbus, stratus, cirrus...),
|
||||||
each with its own shape, height and depth, moving with the wind. The main view gets them at a fraction of its resolution
|
each with its own shape, height and depth, moving with the wind. The main view gets them at a fraction of its resolution
|
||||||
and blended over frames; reflections and the other views read a panorama of the sky with them, renewed in bands over a few
|
and blended over frames; reflections and the other views read a panorama of the sky with them, renewed in bands over a few
|
||||||
frames. Low clouds hide the hills and the far scenery they are in.
|
frames. Low clouds hide the hills and the far scenery they are in. They are lit by the sun through themselves (dark bellies,
|
||||||
|
edges glowing against the sun, thin wisps darker with the sun behind), by the sky above them in its colour (with the
|
||||||
|
directional ambient light, `gfx.ambient.sh`) and from below by the ground.
|
||||||
- **Cloud shadows**: the clouds cast their shadows on the ground, the models, the vehicles and into the fog, from a map
|
- **Cloud shadows**: the clouds cast their shadows on the ground, the models, the vehicles and into the fog, from a map
|
||||||
around the camera along the sun (or the moon). With them on, the overcast no longer dims the sun and softens its shadows
|
around the camera along the sun (or the moon). With them on, the overcast no longer dims the sun and softens its shadows
|
||||||
as a whole: the sun is dimmed where the clouds are, and shines in the gaps.
|
as a whole: the sun is dimmed where the clouds are, and shines in the gaps.
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ layout(binding = CLOUD_DETAIL_TEX) uniform sampler3D cloud_detail_noise;
|
|||||||
layout(binding = CLOUD_COVERAGE_TEX) uniform sampler2D cloud_coverage_noise;
|
layout(binding = CLOUD_COVERAGE_TEX) uniform sampler2D cloud_coverage_noise;
|
||||||
|
|
||||||
const float CLOUD_EARTH_RADIUS = 6360000.0;
|
const float CLOUD_EARTH_RADIUS = 6360000.0;
|
||||||
|
// how much of the shape noise is left standing at the edge and in the core of a cloud area (cloud_layer_density)
|
||||||
|
const float CLOUD_FILL_EDGE = 0.45;
|
||||||
|
const float CLOUD_FILL_CORE = 0.85;
|
||||||
|
// how fast the density rises inside the edge of a cloud: above 1 the edges are crisper, the inside keeps its density
|
||||||
|
const float CLOUD_EDGE_SHARPNESS = 2.0;
|
||||||
|
|
||||||
float cloud_remap(float value, float low, float high, float newlow, float newhigh)
|
float cloud_remap(float value, float low, float high, float newlow, float newhigh)
|
||||||
{
|
{
|
||||||
@@ -134,20 +139,27 @@ float cloud_layer_density(int index, vec3 position, float height, bool detail)
|
|||||||
vec2 plane = layer.motion.xy + cloud_noise_plane(layer, position.xz);
|
vec2 plane = layer.motion.xy + cloud_noise_plane(layer, position.xz);
|
||||||
vec3 coords = vec3(plane.x, height + layer.extra.y * 4096.0, plane.y) * layer.shape.z;
|
vec3 coords = vec3(plane.x, height + layer.extra.y * 4096.0, plane.y) * layer.shape.z;
|
||||||
vec4 shape = textureLod(cloud_shape_noise, coords, 0.0);
|
vec4 shape = textureLod(cloud_shape_noise, coords, 0.0);
|
||||||
float worley = shape.g * 0.625 + shape.b * 0.25 + shape.a * 0.125;
|
// the smaller cells of the worley octaves bite billows into the heaps of the perlin-worley noise; the octaves keep to a narrow
|
||||||
float base = clamp(cloud_remap(shape.r, worley - 1.0, 1.0, 0.0, 1.0), 0.0, 1.0);
|
// band (clouds_noise.comp: WORLEY_FBM_LOW, WORLEY_FBM_HIGH), spread over 0-1 first
|
||||||
|
float worley = clamp((shape.g * 0.625 + shape.b * 0.25 + shape.a * 0.125 - 0.30) / 0.35, 0.0, 1.0);
|
||||||
|
float base = clamp(cloud_remap(shape.r, (1.0 - worley) * 0.35, 1.0, 0.0, 1.0), 0.0, 1.0);
|
||||||
// the sheets (little erosion) keep few holes, the heaps are made of lumps
|
// the sheets (little erosion) keep few holes, the heaps are made of lumps
|
||||||
base = mix(1.0, base, clamp(layer.shape.w * 2.5, 0.25, 1.0));
|
base = mix(1.0, base, clamp(layer.shape.w * 2.5, 0.25, 1.0));
|
||||||
float density = clamp(cloud_remap(base * gradient, 1.0 - coverage.x, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x;
|
// inside its area a cloud is not a solid block: the lumps of the shape noise are cut out of it, the more the nearer its edge,
|
||||||
|
// so it breaks up into single heaps with rounded tops and gaps between them. the sheets keep their base above the cut, and a
|
||||||
|
// layer closing up (broken to overcast) keeps its lumps whole, so it has no more gaps than its coverage
|
||||||
|
float fill = coverage.x * mix(mix(CLOUD_FILL_EDGE, CLOUD_FILL_CORE, coverage.y), 1.0, smoothstep(0.5, 1.0, layer.shape.x));
|
||||||
|
float density = clamp(cloud_remap(base * gradient, 1.0 - fill, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x;
|
||||||
if (detail && density > 0.0 && layer.shape.w > 0.0)
|
if (detail && density > 0.0 && layer.shape.w > 0.0)
|
||||||
{
|
{
|
||||||
vec4 small = textureLod(cloud_detail_noise, coords * 6.0, 0.0);
|
vec4 small = textureLod(cloud_detail_noise, coords * 6.0, 0.0);
|
||||||
float fbm = small.r * 0.625 + small.g * 0.25 + small.b * 0.125;
|
// spread over 0-1 as the worley octaves of the shape; left in their narrow band they would only thin the cloud evenly
|
||||||
|
float fbm = clamp((small.r * 0.625 + small.g * 0.25 + small.b * 0.125 - 0.30) / 0.35, 0.0, 1.0);
|
||||||
// wispy at the base, billowing at the top
|
// wispy at the base, billowing at the top
|
||||||
float erosion = mix(fbm, 1.0 - fbm, clamp(h * 4.0, 0.0, 1.0));
|
float erosion = mix(fbm, 1.0 - fbm, clamp(h * 4.0, 0.0, 1.0));
|
||||||
density = clamp(cloud_remap(density, erosion * layer.shape.w * 0.5, 1.0, 0.0, 1.0), 0.0, 1.0);
|
density = clamp(cloud_remap(density, erosion * layer.shape.w * 0.7, 1.0, 0.0, 1.0), 0.0, 1.0);
|
||||||
}
|
}
|
||||||
return density * layer.shape.y;
|
return min(density * CLOUD_EDGE_SHARPNESS, 1.0) * layer.shape.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the same without the detail, cheaper, for the shadows and the fog
|
// the same without the detail, cheaper, for the shadows and the fog
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ const float PANORAMA_LOW = -0.1;
|
|||||||
// scales of the light of the clouds against the shading of the surfaces (light_common.glsl: SUN_DIFFUSE_SCALE, AMBIENT_SCALE)
|
// scales of the light of the clouds against the shading of the surfaces (light_common.glsl: SUN_DIFFUSE_SCALE, AMBIENT_SCALE)
|
||||||
const float CLOUD_SUN_SCALE = 1.5;
|
const float CLOUD_SUN_SCALE = 1.5;
|
||||||
const float CLOUD_AMBIENT_SCALE = 1.0;
|
const float CLOUD_AMBIENT_SCALE = 1.0;
|
||||||
|
// the ground under the clouds lights their bases back with this share of the light falling on it
|
||||||
|
const float CLOUD_GROUND_ALBEDO = 0.2;
|
||||||
|
// how fast the light of the sky fades into a cloud, per optical depth of the cloud above (or below) a point
|
||||||
|
const float CLOUD_AMBIENT_FADE = 0.05;
|
||||||
|
// how dense a cloud has to be to scatter the sunlight back at full: the thin edges and wisps of the side lit by the sun, seen with the
|
||||||
|
// sun behind, are darker than its bulk (the "powder" effect: the light takes a few scatterings to turn back towards the view)
|
||||||
|
const float CLOUD_POWDER = 6.0;
|
||||||
|
|
||||||
float henyey_greenstein(float cosangle, float g)
|
float henyey_greenstein(float cosangle, float g)
|
||||||
{
|
{
|
||||||
@@ -41,9 +48,18 @@ float cloud_phase(float cosangle, float scale)
|
|||||||
return mix(henyey_greenstein(cosangle, 0.8 * scale), henyey_greenstein(cosangle, -0.25 * scale), 0.3);
|
return mix(henyey_greenstein(cosangle, 0.8 * scale), henyey_greenstein(cosangle, -0.25 * scale), 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the light of the sky a face looking straight up (Up 1) or down (-1) gets: the uniform ambient light and the directional part of
|
||||||
|
// the sky light (sky_ssbo, light_common.glsl: sky_ambient_directional) along the vertical. with the physical atmosphere it is bluer
|
||||||
|
// and brighter from above than from the sides, so the shaded sides of the clouds take the colour of the sky above them
|
||||||
|
vec3 cloud_sky_light(float up)
|
||||||
|
{
|
||||||
|
vec3 directional = sky_ambient_sh[1].rgb * (0.488603 * up) - sky_ambient_sh[6].rgb * 0.315392 - sky_ambient_sh[8].rgb * 0.546274;
|
||||||
|
return max(weather_ambient.rgb + directional * 1.3, vec3(0.0)) * CLOUD_AMBIENT_SCALE;
|
||||||
|
}
|
||||||
|
|
||||||
// light of the sun reaching a point inside a layer through the cloud towards it, with the light scattered many times
|
// light of the sun reaching a point inside a layer through the cloud towards it, with the light scattered many times
|
||||||
// (octaves after Wrenninge), and the phase of the view
|
// (octaves after Wrenninge), and the phase of the view
|
||||||
float cloud_sunlight(int index, vec3 position, float cosangle, int steps)
|
float cloud_sunlight(int index, vec3 position, float cosangle, int steps, float powder)
|
||||||
{
|
{
|
||||||
vec3 sun = weather_sun_direction.xyz;
|
vec3 sun = weather_sun_direction.xyz;
|
||||||
cloud_layer_s layer = cloud_layers[index];
|
cloud_layer_s layer = cloud_layers[index];
|
||||||
@@ -65,7 +81,7 @@ float cloud_sunlight(int index, vec3 position, float cosangle, int steps)
|
|||||||
float spread = 1.0;
|
float spread = 1.0;
|
||||||
for (int octave = 0; octave < 3; octave++)
|
for (int octave = 0; octave < 3; octave++)
|
||||||
{
|
{
|
||||||
light += amount * exp(-depth * fade) * cloud_phase(cosangle, spread);
|
light += amount * exp(-depth * fade) * cloud_phase(cosangle, spread) * (octave == 0 ? powder : 1.0);
|
||||||
amount *= 0.5;
|
amount *= 0.5;
|
||||||
fade *= 0.5;
|
fade *= 0.5;
|
||||||
spread *= 0.5;
|
spread *= 0.5;
|
||||||
@@ -143,8 +159,12 @@ void main()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 ambienttop = weather_ambient.rgb * CLOUD_AMBIENT_SCALE;
|
vec3 ambienttop = cloud_sky_light(1.0);
|
||||||
vec3 sunlight = weather_sun_color.rgb * CLOUD_SUN_SCALE * sunup;
|
vec3 sunlight = weather_sun_color.rgb * CLOUD_SUN_SCALE * sunup;
|
||||||
|
// the bases are lit from below by the ground, which the sun lights through the gaps between the clouds
|
||||||
|
vec3 ambientbottom = (weather_sun_color.rgb * max(weather_sun_direction.y, 0.0) * sunup * fog_light.y + ambienttop) * CLOUD_GROUND_ALBEDO;
|
||||||
|
// seen against the sun the thin edges glow (the forward scattering); seen with the sun behind, they are dark
|
||||||
|
float powderview = clamp(1.0 - cosangle, 0.0, 1.0);
|
||||||
for (int k = 0; k < count && transmittance > 0.01; k++)
|
for (int k = 0; k < count && transmittance > 0.01; k++)
|
||||||
{
|
{
|
||||||
int index = order[k];
|
int index = order[k];
|
||||||
@@ -162,11 +182,14 @@ void main()
|
|||||||
float density = cloud_layer_density(index, position, height, detail);
|
float density = cloud_layer_density(index, position, height, detail);
|
||||||
if (density <= 0.0)
|
if (density <= 0.0)
|
||||||
continue;
|
continue;
|
||||||
float h = clamp((height - layer.heights.x) * layer.heights.z, 0.0, 1.0);
|
float powder = mix(1.0, 1.0 - exp(-density / max(layer.shape.y, 1e-6) * CLOUD_POWDER), powderview);
|
||||||
float sunshare = (sunup > 0.0) ? cloud_sunlight(index, position, cosangle, lightsteps) : 0.0;
|
float sunshare = (sunup > 0.0) ? cloud_sunlight(index, position, cosangle, lightsteps, powder) : 0.0;
|
||||||
// the light of the sky reaches inside through the cloud above: the base of a thick cloud is darker than its top
|
// the light of the sky reaches inside through the cloud above, that of the ground through the cloud below: the base of a
|
||||||
float above = layer.shape.y * layer.shape.x * (layer.heights.y - height);
|
// thick cloud is darker than its top, and greyer, lit only by the ground and the sky at its sides
|
||||||
vec3 light = sunlight * sunshare + ambienttop * mix(0.35, 1.0, h) * mix(0.25, 1.0, exp(-0.05 * above));
|
float column = layer.shape.y * layer.shape.x;
|
||||||
|
float above = column * (layer.heights.y - height);
|
||||||
|
float below = column * (height - layer.heights.x);
|
||||||
|
vec3 light = sunlight * sunshare + (ambienttop * mix(0.15, 1.0, exp(-CLOUD_AMBIENT_FADE * above)) + ambientbottom * exp(-CLOUD_AMBIENT_FADE * below)) * powder;
|
||||||
float through = exp(-density * step);
|
float through = exp(-density * step);
|
||||||
vec3 inscattered = light * (1.0 - through);
|
vec3 inscattered = light * (1.0 - through);
|
||||||
scattered += transmittance * inscattered;
|
scattered += transmittance * inscattered;
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ float worley_fbm(vec3 position, int frequency, uint seed)
|
|||||||
+ worley(position * float(frequency * 4), frequency * 4, seed + 2U) * 0.125;
|
+ worley(position * float(frequency * 4), frequency * 4, seed + 2U) * 0.125;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the worley fbm keeps about to this range (5th and 95th percentile 0.35 and 0.61)
|
||||||
|
const float WORLEY_FBM_LOW = 0.30;
|
||||||
|
const float WORLEY_FBM_HIGH = 0.65;
|
||||||
|
|
||||||
float remap(float value, float low, float high, float newlow, float newhigh)
|
float remap(float value, float low, float high, float newlow, float newhigh)
|
||||||
{
|
{
|
||||||
return newlow + (value - low) / (high - low) * (newhigh - newlow);
|
return newlow + (value - low) / (high - low) * (newhigh - newlow);
|
||||||
@@ -111,9 +115,13 @@ void main()
|
|||||||
vec3 position = (vec3(texel) + 0.5) / float(size);
|
vec3 position = (vec3(texel) + 0.5) / float(size);
|
||||||
if (kind == 0)
|
if (kind == 0)
|
||||||
{
|
{
|
||||||
float low = perlin_fbm(position, 4, 4, 3U) * 0.5 + 0.5;
|
// both spread over 0-1 first: as sums of octaves they keep to a narrow band about the middle, and a cloud made of them
|
||||||
|
// would be an even block with no lumps. the cells then cut the perlin noise into rounded heaps: whole in their middles,
|
||||||
|
// only the highest perlin left between them
|
||||||
|
float low = even(perlin_fbm(position, 4, 4, 3U), 0.22);
|
||||||
float cells = worley_fbm(position, 4, 5U);
|
float cells = worley_fbm(position, 4, 5U);
|
||||||
float perlinworley = clamp(remap(low, cells - 1.0, 1.0, 0.0, 1.0), 0.0, 1.0);
|
float spreadcells = clamp((cells - WORLEY_FBM_LOW) / (WORLEY_FBM_HIGH - WORLEY_FBM_LOW), 0.0, 1.0);
|
||||||
|
float perlinworley = clamp(remap(low, (1.0 - spreadcells) * 0.6, 1.0, 0.0, 1.0), 0.0, 1.0);
|
||||||
imageStore(noise_volume, texel, vec4(perlinworley, cells, worley_fbm(position, 8, 7U), worley_fbm(position, 16, 9U)));
|
imageStore(noise_volume, texel, vec4(perlinworley, cells, worley_fbm(position, 8, 7U), worley_fbm(position, 16, 9U)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -98,11 +98,13 @@ vec2 fog_optical_depth(float origin, float up, float near, float far, float haze
|
|||||||
vec3 fog_color_world(vec3 direction)
|
vec3 fog_color_world(vec3 direction)
|
||||||
{
|
{
|
||||||
vec3 sun = weather_sun_direction.xyz;
|
vec3 sun = weather_sun_direction.xyz;
|
||||||
vec2 flat_view = direction.xz;
|
// the light of the haze goes with the angle from the sun, not with the azimuth alone: the table is taken at the point of the
|
||||||
vec2 flat_sun = sun.xz;
|
// horizon as far from the sun as the view is. level views get the colour of their azimuth as before; a view high up over the
|
||||||
|
// sun no longer gets the glow of the horizon under it (a column of light standing over a low sun)
|
||||||
|
float sunflat = length(sun.xz);
|
||||||
float cosangle = 1.0;
|
float cosangle = 1.0;
|
||||||
if (dot(flat_view, flat_view) > 1e-8 && dot(flat_sun, flat_sun) > 1e-8)
|
if (sunflat > 1e-4)
|
||||||
cosangle = dot(normalize(flat_view), normalize(flat_sun));
|
cosangle = clamp(dot(normalize(direction), sun) / sunflat, -1.0, 1.0);
|
||||||
float position = acos(clamp(cosangle, -1.0, 1.0)) / WEATHER_PI * float(FOG_HORIZON_SAMPLES - 1);
|
float position = acos(clamp(cosangle, -1.0, 1.0)) / WEATHER_PI * float(FOG_HORIZON_SAMPLES - 1);
|
||||||
int index = int(floor(position));
|
int index = int(floor(position));
|
||||||
vec3 color = mix(fog_horizon[clamp(index, 0, FOG_HORIZON_SAMPLES - 1)].rgb, fog_horizon[clamp(index + 1, 0, FOG_HORIZON_SAMPLES - 1)].rgb, fract(position));
|
vec3 color = mix(fog_horizon[clamp(index, 0, FOG_HORIZON_SAMPLES - 1)].rgb, fog_horizon[clamp(index + 1, 0, FOG_HORIZON_SAMPLES - 1)].rgb, fract(position));
|
||||||
|
|||||||
Reference in New Issue
Block a user