17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-22 17:53:21 +02:00
Files
maszyna/shaders/particle_light.glsl

147 lines
6.2 KiB
GLSL

// light of the particles of the gpu (opengl46_gpu_particles: smoke, rain, snow) at a point of the view space of the pass, as a
// particle takes it as a whole, at its middle: the ambient light, the sun (the moon at night) held back by the shadow map and
// by the clouds, and the lamps and headlights of the light cluster the point is in (the first lights of lights[] without the
// clusters). each light is scattered towards the camera by the phase of the particle, Forward: 0 evenly, towards 1 mostly
// onwards. the scale is the one of the shading of the surfaces (light_common.glsl): a white particle takes as much light as
// a white face turned half way to it. for the compute passes; needs <common> and <weather_common.glsl>
#ifndef PARTICLE_LIGHT_GLSL
#define PARTICLE_LIGHT_GLSL
#if SHADOWMAP_ENABLED
uniform sampler2DArrayShadow shadowmap;
#endif
uniform sampler2D headlightmap;
layout(binding = HEADLIGHT_SHADOW_TEX) uniform sampler2DArrayShadow headlight_shadowmap;
// AMBIENT_SCALE and SUN_DIFFUSE_SCALE of light_common.glsl
const float PARTICLE_AMBIENT_SCALE = 0.65;
const float PARTICLE_SUN_SCALE = 1.5;
// Henyey-Greenstein, 1 for every angle when G is 0
float particle_phase(float cosangle, float g)
{
float g2 = g * g;
return (1.0 - g2) / pow(max(1.0 + g2 - 2.0 * g * cosangle, 1e-4), 1.5);
}
// share of the sunlight the shadow map holds back at a point of the view space, one comparison (as fog_inject.comp)
float particle_sun_shadow(vec3 position)
{
#if SHADOWMAP_ENABLED
float span_distance = dot(position, position);
uint cascade;
for (cascade = 0U; cascade < MAX_CASCADES; cascade++)
if (span_distance <= cascade_end[cascade])
break;
for (; cascade < MAX_CASCADES; cascade++)
{
vec4 lightpos = lightview[cascade] * vec4(position, 1.0);
vec3 coords = lightpos.xyz / lightpos.w;
if (all(greaterThanEqual(coords.xy, vec2(0.0))) && all(lessThanEqual(coords.xy, vec2(1.0))))
{
if (coords.z < 0.0)
return 0.0;
return texture(shadowmap, vec4(coords.xy, float(cascade), coords.z + 0.0001 * float(cascade + 1U)));
}
// outside the map: lit, unless the cascades are fitted to the depth of the view (gfx.shadow.sdsm), whose maps cover only
// the area of the surfaces seen; then the first further cascade whose map holds the point
if (shadow_fit.x <= 0.0)
return 0.0;
}
return 0.0;
#else
return 0.0;
#endif
}
// light of a lamp or of the beam of headlights reaching a point of the view space (as fog_inject.comp; the beams with their
// shadow maps, the lamps without)
vec3 particle_lamp(light_s light, vec3 position)
{
if (light.type == LIGHT_HEADLIGHTS)
{
vec4 projected = light.headlight_projection * vec4(position, 1.0);
vec3 coords = projected.xyz / projected.w;
if (coords.z < 0.0 || coords.z > 1.0 || any(lessThan(coords.xy, vec2(0.0))) || any(greaterThan(coords.xy, vec2(1.0))))
return vec3(0.0);
float span_distance = length(light.pos - position);
float atten = 1.0 / (1.0 + light.linear * span_distance + light.quadratic * span_distance * span_distance);
atten *= mix(1.0, 0.0, clamp((coords.z - 0.998) * 500.0, 0.0, 1.0));
vec3 beam = textureLod(headlightmap, coords.xy, 1.0).rgb * light.headlight_weights.rgb;
float intensity = max(max(beam.r, beam.g), beam.b);
if ((light.headlight_weights.w > 0.5) && (intensity > 0.0))
{
uint map = uint(light.headlight_weights.w - 0.5);
vec4 shadowpos = headlight_shadow[map] * vec4(position, 1.0);
if (shadowpos.w > 0.0)
{
vec2 shadowcoords = shadowpos.xy / shadowpos.w;
if (all(greaterThanEqual(shadowcoords, vec2(0.0))) && all(lessThanEqual(shadowcoords, vec2(1.0))))
{
float texel = headlight_shadow_params[map].x * shadowpos.w;
float reference = shadowpos.z / max(shadowpos.w - texel * 1.5, 1e-3);
intensity *= 1.0 - texture(headlight_shadowmap, vec4(shadowcoords, float(map), reference));
}
}
}
return light.color * (atten * intensity * light.intensity);
}
vec3 tolight = light.pos - position;
float distance2 = dot(tolight, tolight);
float edge = clamp(1.0 - distance2 * distance2 * (light.quadratic * light.quadratic), 0.0, 1.0);
float atten = edge * edge / max(distance2, 0.01);
if (light.type == LIGHT_SPOT)
atten *= smoothstep(light.out_cutoff, light.in_cutoff, dot(-tolight * inversesqrt(max(distance2, 1e-8)), normalize(light.dir)));
return light.color * (atten * light.intensity);
}
// the light cluster of a point of the view space in front of the camera, as fragment_cluster() of light_common.glsl finds it for
// the fragment drawn there
uint particle_cluster(vec3 position)
{
vec4 clip = cluster_projection * vec4(position, 1.0);
vec2 screen = clip.xy / clip.w * 0.5 + 0.5;
ivec2 tile = clamp(ivec2(screen * vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y)), ivec2(0), ivec2(CLUSTER_GRID_X - 1, CLUSTER_GRID_Y - 1));
int slice = clamp(int(floor(cluster_slice_bias + log(max(clip.w, 1e-4)) * cluster_slice_scale)), 0, CLUSTER_GRID_Z - 1);
return uint(tile.x + CLUSTER_GRID_X * (tile.y + CLUSTER_GRID_Y * slice));
}
// Position: of the particle in the view space; World: its offset from the camera along the world axes
vec3 particle_light(vec3 position, vec3 world, float forward)
{
vec3 tocamera = -normalize(position);
vec3 light = ambient * PARTICLE_AMBIENT_SCALE;
if (lights_count > 0U)
{
float sunlit = cloud_shadow(world) * (1.0 - particle_sun_shadow(position));
if (sunlit > 0.0)
light += lights[0].color * (0.5 * PARTICLE_SUN_SCALE * lights[0].intensity * sunlit * particle_phase(dot(normalize(lights[0].dir), tocamera), forward));
}
vec3 lamps = vec3(0.0);
if (lights_clustered != 0U)
{
if ((cluster_valid != 0U) && (position.z < 0.0))
{
cluster_range_s range = cluster_ranges[particle_cluster(position)];
for (uint i = 0U; i < range.count; i++)
{
light_s lamp = lights[cluster_light_indices[range.offset + i]];
vec3 fromlamp = position - lamp.pos;
lamps += particle_lamp(lamp, position) * particle_phase(dot(fromlamp, tocamera) * inversesqrt(max(dot(fromlamp, fromlamp), 1e-8)), forward);
}
}
}
else
{
for (uint i = 1U; i < lights_count; i++)
{
vec3 fromlamp = position - lights[i].pos;
lamps += particle_lamp(lights[i], position) * particle_phase(dot(fromlamp, tocamera) * inversesqrt(max(dot(fromlamp, fromlamp), 1e-8)), forward);
}
}
return light + lamps * 0.5;
}
#endif