mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-12 02:58:14 +01:00
feat(vulkan): vulkan 1.4 core with the user interface and the launcher, shaders split into ogl46/vk14 with SPIR-V binaries loaded first
This commit is contained in:
12
shaders/ogl46/alphashadowmap.frag
Normal file
12
shaders/ogl46/alphashadowmap.frag
Normal file
@@ -0,0 +1,12 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D tex1;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (texture(tex1, f_coord).a < 0.5f)
|
||||
discard;
|
||||
}
|
||||
41
shaders/ogl46/apply_fog.glsl
Normal file
41
shaders/ogl46/apply_fog.glsl
Normal file
@@ -0,0 +1,41 @@
|
||||
// fog between the camera and a fragment, see fog_volume.glsl and weather_common.glsl. a draw with a fog_density of 0 is not
|
||||
// fogged; one with another density than the regular one (the glare of lamps) gets the optical depth scaled by the ratio
|
||||
#include "fog_volume.glsl"
|
||||
|
||||
// rgb: light the fog adds, a: what it lets through
|
||||
vec4 fog_at_fragment()
|
||||
{
|
||||
if (fog_density <= 0.0)
|
||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec4 fog = fog_along_view(f_pos.xyz, gl_FragCoord.xy);
|
||||
float scale = fog_density / max(fog_light.w, 1e-12);
|
||||
if (abs(scale - 1.0) > 1e-3)
|
||||
{
|
||||
float transmittance = exp(log(max(fog.a, 1e-8)) * scale);
|
||||
fog.rgb *= (1.0 - transmittance) / max(1.0 - fog.a, 1e-4);
|
||||
fog.a = transmittance;
|
||||
}
|
||||
return fog;
|
||||
}
|
||||
|
||||
vec3 get_fog_color()
|
||||
{
|
||||
vec4 fog = fog_at_fragment();
|
||||
return fog.rgb / max(1.0 - fog.a, 1e-4);
|
||||
}
|
||||
|
||||
float get_fog_amount()
|
||||
{
|
||||
return 1.0 - fog_at_fragment().a;
|
||||
}
|
||||
|
||||
vec3 apply_fog(vec3 color)
|
||||
{
|
||||
vec4 fog = fog_at_fragment();
|
||||
return color * fog.a + fog.rgb;
|
||||
}
|
||||
|
||||
vec3 add_fog(vec3 color, float amount)
|
||||
{
|
||||
return color + fog_at_fragment().rgb * amount;
|
||||
}
|
||||
129
shaders/ogl46/atmosphere_ambient.comp
Normal file
129
shaders/ogl46/atmosphere_ambient.comp
Normal file
@@ -0,0 +1,129 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// what the cpu needs from the atmosphere, worked out from the sky-view table in one work group: the light arriving at the
|
||||
// observer from every direction as spherical harmonics (the ground below the horizon lit by the sun and the sky), the
|
||||
// transmittance towards the sun, and the average light of the sky above the horizon and over the whole sky.
|
||||
// run after the sky-view table; the result is read back by the cpu a few frames later
|
||||
|
||||
#include "common.glsl"
|
||||
#include "atmosphere_common.glsl"
|
||||
|
||||
layout(binding = ATMOSPHERE_TRANSMITTANCE_UNIT) uniform sampler2D atmo_transmittance_lut;
|
||||
layout(binding = ATMOSPHERE_SKYVIEW_UNIT) uniform sampler2D atmo_skyview_lut;
|
||||
|
||||
layout(local_size_x = ATMOSPHERE_AMBIENT_GROUP_SIZE) in;
|
||||
|
||||
layout(std430, binding = ATMOSPHERE_RESULT_SSBO_BINDING) writeonly buffer atmosphere_result_ssbo
|
||||
{
|
||||
vec4 result_radiance_sh[9];
|
||||
vec4 result_sun_transmittance;
|
||||
vec4 result_horizon;
|
||||
vec4 result_average;
|
||||
vec4 result_zenith_transmittance;
|
||||
};
|
||||
|
||||
// directions of the upper hemisphere each invocation takes
|
||||
const uint DIRECTIONS_PER_INVOCATION = 4U;
|
||||
const uint DIRECTIONS = ATMOSPHERE_AMBIENT_GROUP_SIZE * DIRECTIONS_PER_INVOCATION;
|
||||
// the sky up to this sine of the elevation is the horizon (about 6 degrees)
|
||||
const float HORIZON_BAND = 0.1;
|
||||
|
||||
// partial sums: the 9 coefficients, the light on an upward facing surface, the horizon and its weight, the sky average
|
||||
const int SUMS = 12;
|
||||
shared vec4 shared_sums[ATMOSPHERE_AMBIENT_GROUP_SIZE];
|
||||
|
||||
void sh_basis(vec3 n, out float basis[9])
|
||||
{
|
||||
basis[0] = 0.282095;
|
||||
basis[1] = 0.488603 * n.y;
|
||||
basis[2] = 0.488603 * n.z;
|
||||
basis[3] = 0.488603 * n.x;
|
||||
basis[4] = 1.092548 * n.x * n.y;
|
||||
basis[5] = 1.092548 * n.y * n.z;
|
||||
basis[6] = 0.315392 * (3.0 * n.z * n.z - 1.0);
|
||||
basis[7] = 1.092548 * n.x * n.z;
|
||||
basis[8] = 0.546274 * (n.x * n.x - n.y * n.y);
|
||||
}
|
||||
|
||||
// sum of the value of every invocation, valid in invocation 0
|
||||
vec4 reduce(vec4 value)
|
||||
{
|
||||
uint index = gl_LocalInvocationID.x;
|
||||
barrier();
|
||||
shared_sums[index] = value;
|
||||
barrier();
|
||||
for (uint stride = ATMOSPHERE_AMBIENT_GROUP_SIZE / 2U; stride > 0U; stride >>= 1)
|
||||
{
|
||||
if (index < stride)
|
||||
shared_sums[index] += shared_sums[index + stride];
|
||||
barrier();
|
||||
}
|
||||
return shared_sums[0];
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_LocalInvocationID.x;
|
||||
vec3 sundirection = normalize(atmo_sun_direction.xyz);
|
||||
|
||||
vec3 sums[SUMS];
|
||||
for (int i = 0; i < SUMS; ++i)
|
||||
sums[i] = vec3(0.0);
|
||||
|
||||
// even spread over the upper hemisphere: a fibonacci spiral in the sine of the elevation
|
||||
const float goldenangle = 2.39996323;
|
||||
const float solidangle = 2.0 * ATMO_PI / float(DIRECTIONS);
|
||||
for (uint k = 0U; k < DIRECTIONS_PER_INVOCATION; ++k)
|
||||
{
|
||||
uint i = index * DIRECTIONS_PER_INVOCATION + k;
|
||||
float y = (float(i) + 0.5) / float(DIRECTIONS);
|
||||
float r = sqrt(max(1.0 - y * y, 0.0));
|
||||
float azimuth = goldenangle * float(i);
|
||||
vec3 direction = vec3(r * cos(azimuth), y, r * sin(azimuth));
|
||||
|
||||
vec3 radiance = textureLod(atmo_skyview_lut, atmo_skyview_uv_world(direction, sundirection), 0.0).rgb;
|
||||
float basis[9];
|
||||
sh_basis(direction, basis);
|
||||
for (int c = 0; c < 9; ++c)
|
||||
sums[c] += radiance * (basis[c] * solidangle);
|
||||
sums[9] += radiance * (y * solidangle);
|
||||
if (y < HORIZON_BAND)
|
||||
{
|
||||
sums[10] += radiance;
|
||||
sums[11] += vec3(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
vec3 total[SUMS];
|
||||
for (int i = 0; i < SUMS; ++i)
|
||||
total[i] = reduce(vec4(sums[i], 0.0)).rgb;
|
||||
|
||||
if (index != 0U)
|
||||
return;
|
||||
|
||||
// the transmittance towards the sun, zero once the planet is in the way
|
||||
float radius = atmo_observer_radius;
|
||||
vec3 origin = vec3(0.0, radius, 0.0);
|
||||
vec3 suntransmittance = texture(atmo_transmittance_lut, atmo_transmittance_uv(radius, sundirection.y)).rgb;
|
||||
if (atmo_ray_sphere_nearest(origin, sundirection, atmo_bottom_radius) >= 0.0)
|
||||
suntransmittance = vec3(0.0);
|
||||
|
||||
// the ground, a lambertian surface lit by the sun and the sky, fills the lower hemisphere with uniform light. its
|
||||
// spherical harmonics have only the constant term and the vertical one: for a uniform radiance L over the lower
|
||||
// hemisphere, 0.282095 * 2 pi * L and 0.488603 * (-pi) * L
|
||||
// the band 0 coefficient of the sky alone over the solid angle of the hemisphere
|
||||
vec3 skyaverage = total[0] / (0.282095 * 2.0 * ATMO_PI);
|
||||
vec3 irradiance = suntransmittance * max(sundirection.y, 0.0) + total[9];
|
||||
vec3 groundradiance = atmo_ground.rgb / ATMO_PI * irradiance;
|
||||
|
||||
total[0] += groundradiance * (0.282095 * 2.0 * ATMO_PI);
|
||||
total[1] += groundradiance * (0.488603 * -ATMO_PI);
|
||||
for (int c = 0; c < 9; ++c)
|
||||
result_radiance_sh[c] = vec4(total[c], 0.0);
|
||||
|
||||
result_sun_transmittance = vec4(suntransmittance, 0.0);
|
||||
result_horizon = vec4(total[10] / max(total[11].x, 1.0), 0.0);
|
||||
result_average = vec4(skyaverage, 0.0);
|
||||
// what the sun high in the sky looks like at the observer, for the white balance
|
||||
result_zenith_transmittance = vec4(texture(atmo_transmittance_lut, atmo_transmittance_uv(radius, 1.0)).rgb, 0.0);
|
||||
}
|
||||
194
shaders/ogl46/atmosphere_common.glsl
Normal file
194
shaders/ogl46/atmosphere_common.glsl
Normal file
@@ -0,0 +1,194 @@
|
||||
// physically based sky after S. Hillaire, "A Scalable and Production Ready Sky and Atmosphere Rendering Technique"
|
||||
// (EGSR 2020): transmittance, multiple scattering and sky-view look-up tables. distances in kilometres, coefficients per
|
||||
// kilometre. the planet is centred at the origin; the look-up tables are worked out in a frame with z up at the observer,
|
||||
// the world (y up) is mapped to it by the callers. radiance is that of a sun of illuminance 1
|
||||
|
||||
layout(std140, binding = ATMOSPHERE_UBO_BINDING) uniform atmosphere_ubo
|
||||
{
|
||||
// rgb: rayleigh scattering, w: rayleigh scale height
|
||||
vec4 atmo_rayleigh;
|
||||
// x: mie scattering, y: mie extinction, z: mie scale height, w: mie phase asymmetry
|
||||
vec4 atmo_mie;
|
||||
// rgb: ozone absorption, w: altitude of the middle of the ozone layer
|
||||
vec4 atmo_ozone;
|
||||
// rgb: albedo of the ground, w: half width of the ozone layer
|
||||
vec4 atmo_ground;
|
||||
// world space direction towards the sun (y up)
|
||||
vec4 atmo_sun_direction;
|
||||
float atmo_bottom_radius;
|
||||
float atmo_top_radius;
|
||||
// distance of the observer from the centre of the planet
|
||||
float atmo_observer_radius;
|
||||
};
|
||||
|
||||
const float ATMO_PI = 3.14159265359;
|
||||
|
||||
struct atmo_medium_s
|
||||
{
|
||||
vec3 scattering;
|
||||
vec3 extinction;
|
||||
vec3 rayleigh_scattering;
|
||||
vec3 mie_scattering;
|
||||
};
|
||||
|
||||
atmo_medium_s atmo_sample_medium(float radius)
|
||||
{
|
||||
float height = max(radius - atmo_bottom_radius, 0.0);
|
||||
float rayleighdensity = exp(-height / atmo_rayleigh.w);
|
||||
float miedensity = exp(-height / atmo_mie.z);
|
||||
float ozonedensity = max(0.0, 1.0 - abs(height - atmo_ozone.w) / atmo_ground.w);
|
||||
|
||||
atmo_medium_s medium;
|
||||
medium.rayleigh_scattering = atmo_rayleigh.rgb * rayleighdensity;
|
||||
medium.mie_scattering = vec3(atmo_mie.x * miedensity);
|
||||
medium.scattering = medium.rayleigh_scattering + medium.mie_scattering;
|
||||
medium.extinction = medium.rayleigh_scattering + vec3(atmo_mie.y * miedensity) + atmo_ozone.rgb * ozonedensity;
|
||||
return medium;
|
||||
}
|
||||
|
||||
// distance along a ray (unit direction) to the nearest point ahead of the origin where it meets the sphere, -1 if it does not
|
||||
float atmo_ray_sphere_nearest(vec3 origin, vec3 direction, float radius)
|
||||
{
|
||||
float b = dot(origin, direction);
|
||||
float c = dot(origin, origin) - radius * radius;
|
||||
float discriminant = b * b - c;
|
||||
if (discriminant < 0.0)
|
||||
return -1.0;
|
||||
float root = sqrt(discriminant);
|
||||
float t0 = -b - root;
|
||||
float t1 = -b + root;
|
||||
if (t0 < 0.0 && t1 < 0.0)
|
||||
return -1.0;
|
||||
if (t0 < 0.0)
|
||||
return t1;
|
||||
if (t1 < 0.0)
|
||||
return t0;
|
||||
return min(t0, t1);
|
||||
}
|
||||
|
||||
float atmo_rayleigh_phase(float cosangle)
|
||||
{
|
||||
return 3.0 / (16.0 * ATMO_PI) * (1.0 + cosangle * cosangle);
|
||||
}
|
||||
|
||||
// Cornette-Shanks
|
||||
float atmo_mie_phase(float cosangle)
|
||||
{
|
||||
float g = atmo_mie.w;
|
||||
float k = 3.0 / (8.0 * ATMO_PI) * (1.0 - g * g) / (2.0 + g * g);
|
||||
return k * (1.0 + cosangle * cosangle) / pow(max(1.0 + g * g - 2.0 * g * cosangle, 1e-4), 1.5);
|
||||
}
|
||||
|
||||
// texel centres of a table of the given resolution span [0, 1] of the parameter
|
||||
float atmo_unit_to_sub_uv(float u, float resolution)
|
||||
{
|
||||
return (u + 0.5 / resolution) * (resolution / (resolution + 1.0));
|
||||
}
|
||||
|
||||
float atmo_sub_uv_to_unit(float u, float resolution)
|
||||
{
|
||||
return (u - 0.5 / resolution) * (resolution / (resolution - 1.0));
|
||||
}
|
||||
|
||||
// transmittance table: x the distance to the top of the atmosphere along the view, y the height
|
||||
vec2 atmo_transmittance_uv(float radius, float cosviewzenith)
|
||||
{
|
||||
float H = sqrt(max(0.0, atmo_top_radius * atmo_top_radius - atmo_bottom_radius * atmo_bottom_radius));
|
||||
float rho = sqrt(max(0.0, radius * radius - atmo_bottom_radius * atmo_bottom_radius));
|
||||
float discriminant = radius * radius * (cosviewzenith * cosviewzenith - 1.0) + atmo_top_radius * atmo_top_radius;
|
||||
float d = max(0.0, -radius * cosviewzenith + sqrt(max(discriminant, 0.0)));
|
||||
float dmin = atmo_top_radius - radius;
|
||||
float dmax = rho + H;
|
||||
return vec2((d - dmin) / max(dmax - dmin, 1e-6), rho / H);
|
||||
}
|
||||
|
||||
void atmo_transmittance_params(vec2 uv, out float radius, out float cosviewzenith)
|
||||
{
|
||||
float H = sqrt(max(0.0, atmo_top_radius * atmo_top_radius - atmo_bottom_radius * atmo_bottom_radius));
|
||||
float rho = H * uv.y;
|
||||
radius = sqrt(rho * rho + atmo_bottom_radius * atmo_bottom_radius);
|
||||
float dmin = atmo_top_radius - radius;
|
||||
float dmax = rho + H;
|
||||
float d = dmin + uv.x * (dmax - dmin);
|
||||
cosviewzenith = (d == 0.0) ? 1.0 : (H * H - rho * rho - d * d) / (2.0 * radius * d);
|
||||
cosviewzenith = clamp(cosviewzenith, -1.0, 1.0);
|
||||
}
|
||||
|
||||
// multiple scattering table: x the cosine of the sun zenith angle, y the height
|
||||
vec2 atmo_multiscattering_uv(float radius, float cossunzenith)
|
||||
{
|
||||
vec2 uv = vec2(cossunzenith * 0.5 + 0.5, (radius - atmo_bottom_radius) / (atmo_top_radius - atmo_bottom_radius));
|
||||
return clamp(vec2(atmo_unit_to_sub_uv(uv.x, float(ATMOSPHERE_MULTISCATTERING_SIZE)), atmo_unit_to_sub_uv(uv.y, float(ATMOSPHERE_MULTISCATTERING_SIZE))), 0.0, 1.0);
|
||||
}
|
||||
|
||||
// sky-view table: x the angle between the view and the sun around the zenith (sqrt spaced), y the angle of the view from
|
||||
// the zenith, packed towards the horizon, which splits the table in the sky (lower half) and the ground (upper half)
|
||||
vec2 atmo_skyview_uv(bool intersectsground, float cosviewzenith, float coslightview, float radius)
|
||||
{
|
||||
float vhorizon = sqrt(max(radius * radius - atmo_bottom_radius * atmo_bottom_radius, 0.0));
|
||||
float cosbeta = vhorizon / radius;
|
||||
float beta = acos(clamp(cosbeta, -1.0, 1.0));
|
||||
float zenithhorizonangle = ATMO_PI - beta;
|
||||
|
||||
vec2 uv;
|
||||
if (!intersectsground)
|
||||
{
|
||||
float coord = acos(clamp(cosviewzenith, -1.0, 1.0)) / zenithhorizonangle;
|
||||
coord = 1.0 - coord;
|
||||
coord = sqrt(max(coord, 0.0));
|
||||
coord = 1.0 - coord;
|
||||
uv.y = coord * 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
float coord = (acos(clamp(cosviewzenith, -1.0, 1.0)) - zenithhorizonangle) / max(beta, 1e-6);
|
||||
coord = sqrt(max(coord, 0.0));
|
||||
uv.y = coord * 0.5 + 0.5;
|
||||
}
|
||||
uv.x = sqrt(clamp(-coslightview * 0.5 + 0.5, 0.0, 1.0));
|
||||
return vec2(atmo_unit_to_sub_uv(uv.x, float(ATMOSPHERE_SKYVIEW_WIDTH)), atmo_unit_to_sub_uv(uv.y, float(ATMOSPHERE_SKYVIEW_HEIGHT)));
|
||||
}
|
||||
|
||||
void atmo_skyview_params(vec2 uv, float radius, out float cosviewzenith, out float coslightview)
|
||||
{
|
||||
uv = vec2(atmo_sub_uv_to_unit(uv.x, float(ATMOSPHERE_SKYVIEW_WIDTH)), atmo_sub_uv_to_unit(uv.y, float(ATMOSPHERE_SKYVIEW_HEIGHT)));
|
||||
uv = clamp(uv, 0.0, 1.0);
|
||||
|
||||
float vhorizon = sqrt(max(radius * radius - atmo_bottom_radius * atmo_bottom_radius, 0.0));
|
||||
float cosbeta = vhorizon / radius;
|
||||
float beta = acos(clamp(cosbeta, -1.0, 1.0));
|
||||
float zenithhorizonangle = ATMO_PI - beta;
|
||||
|
||||
if (uv.y < 0.5)
|
||||
{
|
||||
float coord = 2.0 * uv.y;
|
||||
coord = 1.0 - coord;
|
||||
coord *= coord;
|
||||
coord = 1.0 - coord;
|
||||
cosviewzenith = cos(zenithhorizonangle * coord);
|
||||
}
|
||||
else
|
||||
{
|
||||
float coord = uv.y * 2.0 - 1.0;
|
||||
coord *= coord;
|
||||
cosviewzenith = cos(zenithhorizonangle + beta * coord);
|
||||
}
|
||||
float coord = uv.x * uv.x;
|
||||
coslightview = -(coord * 2.0 - 1.0);
|
||||
}
|
||||
|
||||
// the same mapping for a world space view direction and sun direction (y up), as seen by the observer
|
||||
vec2 atmo_skyview_uv_world(vec3 viewdirection, vec3 sundirection)
|
||||
{
|
||||
float radius = atmo_observer_radius;
|
||||
vec3 origin = vec3(0.0, 0.0, radius);
|
||||
float cosviewzenith = viewdirection.y;
|
||||
// angle between the view and the sun around the zenith
|
||||
vec2 viewflat = viewdirection.xz;
|
||||
vec2 sunflat = sundirection.xz;
|
||||
float lengths = length(viewflat) * length(sunflat);
|
||||
float coslightview = (lengths > 1e-6) ? clamp(dot(viewflat, sunflat) / lengths, -1.0, 1.0) : 1.0;
|
||||
vec3 localview = vec3(sqrt(max(1.0 - cosviewzenith * cosviewzenith, 0.0)), 0.0, cosviewzenith);
|
||||
bool intersectsground = atmo_ray_sphere_nearest(origin, localview, atmo_bottom_radius) >= 0.0;
|
||||
return atmo_skyview_uv(intersectsground, cosviewzenith, coslightview, radius);
|
||||
}
|
||||
105
shaders/ogl46/atmosphere_integrate.glsl
Normal file
105
shaders/ogl46/atmosphere_integrate.glsl
Normal file
@@ -0,0 +1,105 @@
|
||||
// scattering along a ray through the atmosphere, see atmosphere_common.glsl. the includer declares the samplers
|
||||
// atmo_transmittance_lut and (when multiple scattering is used) atmo_multiscattering_lut
|
||||
|
||||
// above the ground by this much (km), so the test for the shadow of the planet does not hit the point itself
|
||||
const float ATMO_PLANET_RADIUS_OFFSET = 0.01;
|
||||
|
||||
struct atmo_scattering_s
|
||||
{
|
||||
// light scattered towards the origin
|
||||
vec3 luminance;
|
||||
// transmittance from the origin to the end of the ray
|
||||
vec3 transmittance;
|
||||
// light scattered once more, as if the light arriving at every point was 1 (multiple scattering table)
|
||||
vec3 multiscattering_as_1;
|
||||
};
|
||||
|
||||
vec3 atmo_transmittance_to_top(float radius, float cossunzenith)
|
||||
{
|
||||
return texture(atmo_transmittance_lut, atmo_transmittance_uv(radius, cossunzenith)).rgb;
|
||||
}
|
||||
|
||||
// Origin and Direction (unit) in the frame of the planet. with Ground the light the ground reflects at the end of the ray
|
||||
// is included. Physicalphase: rayleigh and mie phase functions, otherwise isotropic (multiple scattering table).
|
||||
// Multiscattering: light scattered more than once comes from the multiple scattering table
|
||||
atmo_scattering_s atmo_integrate(vec3 origin, vec3 direction, vec3 sundirection, bool ground, int samples, bool physicalphase, bool multiscattering)
|
||||
{
|
||||
atmo_scattering_s result;
|
||||
result.luminance = vec3(0.0);
|
||||
result.transmittance = vec3(1.0);
|
||||
result.multiscattering_as_1 = vec3(0.0);
|
||||
|
||||
float tbottom = atmo_ray_sphere_nearest(origin, direction, atmo_bottom_radius);
|
||||
float ttop = atmo_ray_sphere_nearest(origin, direction, atmo_top_radius);
|
||||
float tmax;
|
||||
if (tbottom < 0.0)
|
||||
{
|
||||
if (ttop < 0.0)
|
||||
return result;
|
||||
tmax = ttop;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmax = (ttop > 0.0) ? min(ttop, tbottom) : tbottom;
|
||||
}
|
||||
|
||||
float cosangle = dot(direction, sundirection);
|
||||
const float uniformphase = 1.0 / (4.0 * ATMO_PI);
|
||||
float miephase = physicalphase ? atmo_mie_phase(cosangle) : uniformphase;
|
||||
float rayleighphase = physicalphase ? atmo_rayleigh_phase(cosangle) : uniformphase;
|
||||
|
||||
vec3 throughput = vec3(1.0);
|
||||
float invsamples = 1.0 / float(samples);
|
||||
for (int s = 0; s < samples; ++s)
|
||||
{
|
||||
// the steps grow with the distance: the medium near the observer matters most
|
||||
float t0 = float(s) * invsamples;
|
||||
float t1 = float(s + 1) * invsamples;
|
||||
t0 = t0 * t0 * tmax;
|
||||
t1 = t1 * t1 * tmax;
|
||||
float dt = t1 - t0;
|
||||
float t = t0 + dt * 0.3;
|
||||
|
||||
vec3 position = origin + t * direction;
|
||||
float radius = length(position);
|
||||
vec3 up = position / radius;
|
||||
atmo_medium_s medium = atmo_sample_medium(radius);
|
||||
vec3 extinction = max(medium.extinction, vec3(1e-7));
|
||||
vec3 sampletransmittance = exp(-extinction * dt);
|
||||
|
||||
float cossunzenith = dot(sundirection, up);
|
||||
vec3 suntransmittance = atmo_transmittance_to_top(radius, cossunzenith);
|
||||
float earthshadow = (atmo_ray_sphere_nearest(position + up * ATMO_PLANET_RADIUS_OFFSET, sundirection, atmo_bottom_radius) >= 0.0) ? 0.0 : 1.0;
|
||||
|
||||
vec3 multiscatteredluminance = vec3(0.0);
|
||||
#ifdef ATMO_MULTISCATTERING_LUT
|
||||
if (multiscattering)
|
||||
multiscatteredluminance = texture(atmo_multiscattering_lut, atmo_multiscattering_uv(radius, cossunzenith)).rgb;
|
||||
#endif
|
||||
|
||||
vec3 phasescattering = medium.mie_scattering * miephase + medium.rayleigh_scattering * rayleighphase;
|
||||
vec3 S = earthshadow * suntransmittance * phasescattering + multiscatteredluminance * medium.scattering;
|
||||
// analytic integral over the step of the light scattered at the sample, attenuated on the way to its start
|
||||
vec3 Sint = (S - S * sampletransmittance) / extinction;
|
||||
result.luminance += throughput * Sint;
|
||||
|
||||
vec3 MSint = (medium.scattering - medium.scattering * sampletransmittance) / extinction;
|
||||
result.multiscattering_as_1 += throughput * MSint;
|
||||
|
||||
throughput *= sampletransmittance;
|
||||
}
|
||||
|
||||
if (ground && tbottom > 0.0 && tmax == tbottom)
|
||||
{
|
||||
vec3 position = origin + tbottom * direction;
|
||||
float radius = length(position);
|
||||
vec3 up = position / radius;
|
||||
float cossunzenith = dot(sundirection, up);
|
||||
vec3 suntransmittance = atmo_transmittance_to_top(radius, cossunzenith);
|
||||
float NdotL = clamp(dot(up, sundirection), 0.0, 1.0);
|
||||
result.luminance += suntransmittance * throughput * NdotL * atmo_ground.rgb / ATMO_PI;
|
||||
}
|
||||
|
||||
result.transmittance = throughput;
|
||||
return result;
|
||||
}
|
||||
71
shaders/ogl46/atmosphere_multiscattering.comp
Normal file
71
shaders/ogl46/atmosphere_multiscattering.comp
Normal file
@@ -0,0 +1,71 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// light scattered more than once, for the multiple scattering table (Hillaire 2020, section 5.5): for a point at a height
|
||||
// lit by the sun at an angle, the second order scattering from 64 directions around it, summed as a geometric series of
|
||||
// all the orders. one work group per texel, one invocation per direction. run after the transmittance table
|
||||
|
||||
#include "common.glsl"
|
||||
#include "atmosphere_common.glsl"
|
||||
|
||||
layout(binding = ATMOSPHERE_TRANSMITTANCE_UNIT) uniform sampler2D atmo_transmittance_lut;
|
||||
#include "atmosphere_integrate.glsl"
|
||||
|
||||
layout(local_size_x = 64) in;
|
||||
|
||||
layout(binding = ATMOSPHERE_MULTISCATTERING_UNIT, rgba16f) writeonly uniform image2D multiscattering_target;
|
||||
|
||||
const int MULTISCATTERING_SAMPLES = 20;
|
||||
const int SQRT_DIRECTIONS = 8;
|
||||
|
||||
shared vec3 shared_luminance[64];
|
||||
shared vec3 shared_multiscattering[64];
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_WorkGroupID.xy);
|
||||
uint index = gl_LocalInvocationID.x;
|
||||
|
||||
vec2 uv = (vec2(texel) + 0.5) / float(ATMOSPHERE_MULTISCATTERING_SIZE);
|
||||
uv = vec2(atmo_sub_uv_to_unit(uv.x, float(ATMOSPHERE_MULTISCATTERING_SIZE)), atmo_sub_uv_to_unit(uv.y, float(ATMOSPHERE_MULTISCATTERING_SIZE)));
|
||||
uv = clamp(uv, 0.0, 1.0);
|
||||
|
||||
float cossunzenith = uv.x * 2.0 - 1.0;
|
||||
vec3 sundirection = vec3(0.0, sqrt(max(1.0 - cossunzenith * cossunzenith, 0.0)), cossunzenith);
|
||||
float radius = atmo_bottom_radius + clamp(uv.y + 1e-4, 0.0, 1.0) * (atmo_top_radius - atmo_bottom_radius - 1e-3);
|
||||
vec3 origin = vec3(0.0, 0.0, radius);
|
||||
|
||||
// directions spread evenly over the sphere
|
||||
float a = (0.5 + float(index / uint(SQRT_DIRECTIONS))) / float(SQRT_DIRECTIONS);
|
||||
float b = (0.5 + float(index % uint(SQRT_DIRECTIONS))) / float(SQRT_DIRECTIONS);
|
||||
float theta = 2.0 * ATMO_PI * a;
|
||||
float phi = acos(clamp(1.0 - 2.0 * b, -1.0, 1.0));
|
||||
vec3 direction = vec3(cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi));
|
||||
|
||||
atmo_scattering_s result = atmo_integrate(origin, direction, sundirection, true, MULTISCATTERING_SAMPLES, false, false);
|
||||
|
||||
const float spheresolidangle = 4.0 * ATMO_PI;
|
||||
const float directions = float(SQRT_DIRECTIONS * SQRT_DIRECTIONS);
|
||||
shared_luminance[index] = result.luminance * spheresolidangle / directions;
|
||||
shared_multiscattering[index] = result.multiscattering_as_1 * spheresolidangle / directions;
|
||||
barrier();
|
||||
|
||||
for (uint stride = 32U; stride > 0U; stride >>= 1)
|
||||
{
|
||||
if (index < stride)
|
||||
{
|
||||
shared_luminance[index] += shared_luminance[index + stride];
|
||||
shared_multiscattering[index] += shared_multiscattering[index + stride];
|
||||
}
|
||||
barrier();
|
||||
}
|
||||
|
||||
if (index != 0U)
|
||||
return;
|
||||
|
||||
const float isotropicphase = 1.0 / spheresolidangle;
|
||||
vec3 inscatteredluminance = shared_luminance[0] * isotropicphase;
|
||||
vec3 multiscatteringas1 = shared_multiscattering[0] * isotropicphase;
|
||||
// every further order of scattering is the previous one times multiscatteringas1
|
||||
vec3 luminance = inscatteredluminance / max(vec3(1.0) - multiscatteringas1, vec3(1e-3));
|
||||
imageStore(multiscattering_target, texel, vec4(luminance, 1.0));
|
||||
}
|
||||
41
shaders/ogl46/atmosphere_skyview.comp
Normal file
41
shaders/ogl46/atmosphere_skyview.comp
Normal file
@@ -0,0 +1,41 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// light of the sky seen by the observer in every direction, for the sky-view table (atmosphere_common.glsl), which the
|
||||
// sky, the ambient light and the fog colour are read from. one invocation per texel. run every frame the sky is drawn with
|
||||
// the atmosphere, after the transmittance and multiple scattering tables
|
||||
|
||||
#include "common.glsl"
|
||||
#include "atmosphere_common.glsl"
|
||||
|
||||
layout(binding = ATMOSPHERE_TRANSMITTANCE_UNIT) uniform sampler2D atmo_transmittance_lut;
|
||||
layout(binding = ATMOSPHERE_MULTISCATTERING_UNIT) uniform sampler2D atmo_multiscattering_lut;
|
||||
#define ATMO_MULTISCATTERING_LUT
|
||||
#include "atmosphere_integrate.glsl"
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8) in;
|
||||
|
||||
layout(binding = ATMOSPHERE_SKYVIEW_UNIT, rgba16f) writeonly uniform image2D skyview_target;
|
||||
|
||||
const int SKYVIEW_SAMPLES = 32;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (texel.x >= int(ATMOSPHERE_SKYVIEW_WIDTH) || texel.y >= int(ATMOSPHERE_SKYVIEW_HEIGHT))
|
||||
return;
|
||||
|
||||
vec2 uv = (vec2(texel) + 0.5) / vec2(ATMOSPHERE_SKYVIEW_WIDTH, ATMOSPHERE_SKYVIEW_HEIGHT);
|
||||
float radius = atmo_observer_radius;
|
||||
float cosviewzenith;
|
||||
float coslightview;
|
||||
atmo_skyview_params(uv, radius, cosviewzenith, coslightview);
|
||||
|
||||
float cossunzenith = clamp(atmo_sun_direction.y, -1.0, 1.0);
|
||||
vec3 sundirection = normalize(vec3(sqrt(max(1.0 - cossunzenith * cossunzenith, 0.0)), 0.0, cossunzenith));
|
||||
float sinviewzenith = sqrt(max(1.0 - cosviewzenith * cosviewzenith, 0.0));
|
||||
vec3 direction = vec3(sinviewzenith * coslightview, sinviewzenith * sqrt(max(1.0 - coslightview * coslightview, 0.0)), cosviewzenith);
|
||||
vec3 origin = vec3(0.0, 0.0, radius);
|
||||
|
||||
atmo_scattering_s result = atmo_integrate(origin, direction, sundirection, false, SKYVIEW_SAMPLES, true, true);
|
||||
imageStore(skyview_target, texel, vec4(result.luminance, 1.0));
|
||||
}
|
||||
42
shaders/ogl46/atmosphere_transmittance.comp
Normal file
42
shaders/ogl46/atmosphere_transmittance.comp
Normal file
@@ -0,0 +1,42 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// transmittance from a point in the atmosphere to its top, for the transmittance table (atmosphere_common.glsl).
|
||||
// one invocation per texel. run when the parameters of the atmosphere change
|
||||
|
||||
#include "common.glsl"
|
||||
#include "atmosphere_common.glsl"
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8) in;
|
||||
|
||||
layout(binding = ATMOSPHERE_TRANSMITTANCE_UNIT, rgba16f) writeonly uniform image2D transmittance_target;
|
||||
|
||||
const int TRANSMITTANCE_SAMPLES = 40;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (texel.x >= int(ATMOSPHERE_TRANSMITTANCE_WIDTH) || texel.y >= int(ATMOSPHERE_TRANSMITTANCE_HEIGHT))
|
||||
return;
|
||||
|
||||
vec2 uv = (vec2(texel) + 0.5) / vec2(ATMOSPHERE_TRANSMITTANCE_WIDTH, ATMOSPHERE_TRANSMITTANCE_HEIGHT);
|
||||
float radius;
|
||||
float cosviewzenith;
|
||||
atmo_transmittance_params(uv, radius, cosviewzenith);
|
||||
|
||||
vec3 origin = vec3(0.0, 0.0, radius);
|
||||
vec3 direction = vec3(sqrt(max(1.0 - cosviewzenith * cosviewzenith, 0.0)), 0.0, cosviewzenith);
|
||||
|
||||
float tbottom = atmo_ray_sphere_nearest(origin, direction, atmo_bottom_radius);
|
||||
float ttop = atmo_ray_sphere_nearest(origin, direction, atmo_top_radius);
|
||||
float tmax = (tbottom > 0.0) ? ((ttop > 0.0) ? min(ttop, tbottom) : tbottom) : max(ttop, 0.0);
|
||||
|
||||
vec3 opticaldepth = vec3(0.0);
|
||||
float dt = tmax / float(TRANSMITTANCE_SAMPLES);
|
||||
for (int s = 0; s < TRANSMITTANCE_SAMPLES; ++s)
|
||||
{
|
||||
vec3 position = origin + (float(s) + 0.5) * dt * direction;
|
||||
opticaldepth += atmo_sample_medium(length(position)).extinction * dt;
|
||||
}
|
||||
|
||||
imageStore(transmittance_target, texel, vec4(exp(-opticaldepth), 1.0));
|
||||
}
|
||||
29
shaders/ogl46/billboard.frag
Normal file
29
shaders/ogl46/billboard.frag
Normal file
@@ -0,0 +1,29 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D tex1;
|
||||
|
||||
#include "common.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(tex1, f_coord);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = tex_color * param[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(tex_color * param[0]);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
87
shaders/ogl46/celestial.frag
Normal file
87
shaders/ogl46/celestial.frag
Normal file
@@ -0,0 +1,87 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// the sun, the moon and the textured sprite of the easter egg, drawn over the sky on a quad around the direction of the body
|
||||
// (celestial.vert). param[0]: colour (rgb) and alpha, param[1]: position in view space and half the size of the quad,
|
||||
// param[2].xy: corner (sprite) or centre (moon) of the picture in the texture, param[2].z: its size (sprite) or the radius of
|
||||
// the disc in it (moon), in texture units, param[2].w: kind of body, 0: textured sprite, 1: sun, 2: moon.
|
||||
// the colour goes out premultiplied, the alpha is how much of the sky behind the body it hides (blending ONE, ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 15) in vec2 f_local;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D tex1;
|
||||
|
||||
#include "common.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
// shape and light of the sun and the moon, set by opengl46_renderer::Render(world_environment *):
|
||||
// [0].xy: direction to the zenith on the quad, [0].z: height over width of the disc (flattened by the refraction near the
|
||||
// horizon), [0].w: radius of the disc in units of half the quad
|
||||
// [1].xyz: direction to the sun in view space, [1].w: light of the earth on the dark part of the moon, against the light of
|
||||
// the sun on the lit part
|
||||
// [2].rgb: limb darkening of the sun per channel (linear law)
|
||||
layout(location = 0) uniform vec4 body[3];
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
void main()
|
||||
{
|
||||
int kind = int(param[2].w + 0.5);
|
||||
vec4 color = vec4(0.0);
|
||||
if (kind == 0)
|
||||
{
|
||||
vec4 tex_color = texture(tex1, f_coord) * param[0];
|
||||
color = vec4(tex_color.rgb * tex_color.a, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the point on the disc in its radii, x along the horizon, y to the zenith, with the flattening undone
|
||||
vec2 zenith = body[0].xy;
|
||||
vec2 across = vec2(zenith.y, -zenith.x);
|
||||
vec2 p = vec2(dot(f_local, across), dot(f_local, zenith)) / body[0].w;
|
||||
p.y /= body[0].z;
|
||||
float r = length(p);
|
||||
float coverage = clamp((1.0 - r) / max(fwidth(r), 1e-4) + 0.5, 0.0, 1.0);
|
||||
// the horizon hides what is under it
|
||||
vec3 direction = normalize(param[1].xyz + vec3(f_local * param[1].w, 0.0));
|
||||
float height = (mat3(inv_view) * direction).y;
|
||||
coverage *= clamp(height / max(fwidth(height), 1e-7) + 0.5, 0.0, 1.0);
|
||||
// cosine of the angle between the line of sight and the surface of the sphere
|
||||
float mu = sqrt(max(1.0 - r * r, 0.0));
|
||||
if (kind == 1)
|
||||
{
|
||||
// limb darkening, kept to the average brightness of the disc
|
||||
vec3 limb = (vec3(1.0) - body[2].rgb * (1.0 - mu)) / (vec3(1.0) - body[2].rgb / 3.0);
|
||||
color = vec4(param[0].rgb * limb * coverage, 0.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the moon as a sphere lit from the direction of the sun. the frame of the disc in view space, z to the viewer
|
||||
vec3 z = -normalize(param[1].xyz);
|
||||
vec3 x = vec3(across, 0.0);
|
||||
x = normalize(x - z * dot(x, z));
|
||||
vec3 y = cross(z, x);
|
||||
vec2 q = p / max(r, 1.0);
|
||||
vec3 normal = q.x * x + q.y * y + mu * z;
|
||||
float mu0 = max(dot(normal, normalize(body[1].xyz)), 0.0);
|
||||
// Lommel-Seeliger law: an even disc at the full moon, as the real one, no darkening towards the limb
|
||||
float lit = 2.0 * mu0 / (mu0 + max(mu, 1e-3));
|
||||
vec3 albedo = texture(tex1, param[2].xy + p * param[2].z).rgb;
|
||||
color = vec4(albedo * param[0].rgb * ((lit + body[1].w) * coverage), coverage * param[0].a);
|
||||
}
|
||||
}
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = vec4(tonemap(vec4(color.rgb, 1.0)).rgb, color.a);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
34
shaders/ogl46/celestial.vert
Normal file
34
shaders/ogl46/celestial.vert
Normal file
@@ -0,0 +1,34 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
const vec2 vert[4] = vec2[]
|
||||
(
|
||||
vec2(-1.0, 1.0),
|
||||
vec2(-1.0, -1.0),
|
||||
vec2( 1.0, 1.0),
|
||||
vec2( 1.0, -1.0)
|
||||
);
|
||||
|
||||
layout(location = 2) out vec2 f_coord;
|
||||
// the corner of the quad, -1...1 across it
|
||||
layout(location = 15) out vec2 f_local;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * (vec4(param[1].xyz, 1.0) + vec4(vert[gl_VertexID] * param[1].w, 0.0, 0.0));
|
||||
|
||||
f_local = vert[gl_VertexID];
|
||||
|
||||
vec2 coord = param[2].xy;
|
||||
float size = param[2].z;
|
||||
|
||||
if (gl_VertexID == 0)
|
||||
f_coord = vec2(coord.x, coord.y);
|
||||
if (gl_VertexID == 1)
|
||||
f_coord = vec2(coord.x, coord.y - size);
|
||||
if (gl_VertexID == 2)
|
||||
f_coord = vec2(coord.x + size, coord.y);
|
||||
if (gl_VertexID == 3)
|
||||
f_coord = vec2(coord.x + size, coord.y - size);
|
||||
}
|
||||
291
shaders/ogl46/clouds_common.glsl
Normal file
291
shaders/ogl46/clouds_common.glsl
Normal file
@@ -0,0 +1,291 @@
|
||||
// clouds of the weather (opengl46_weather): density of the layers of weather_common.glsl, made of three tiling noises.
|
||||
// the shape noise (3d, clouds_noise.comp) gives the lumps of a cloud, the detail noise (3d) eats into their edges, the coverage
|
||||
// noise (2d) spreads the clouds of a layer over the sky so they take up its coverage. positions are world offsets from the
|
||||
// camera, in metres, y up; the earth curves away from the camera, so far clouds sink below the horizon
|
||||
#include "weather_common.glsl"
|
||||
|
||||
layout(binding = CLOUD_SHAPE_TEX) uniform sampler3D cloud_shape_noise;
|
||||
layout(binding = CLOUD_DETAIL_TEX) uniform sampler3D cloud_detail_noise;
|
||||
layout(binding = CLOUD_COVERAGE_TEX) uniform sampler2D cloud_coverage_noise;
|
||||
|
||||
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 = 3.0;
|
||||
// how far the base and the top of the single clouds stand off those of their layer, up or down, as shares of the depth of the layer
|
||||
// and at most in metres: the clouds of a layer don't all end at one height, some reach a little out of it
|
||||
// how high the base of a cloud curves up at its edge and how far into the cloud area (cloud_coverage: y) it does, and how much higher
|
||||
// the base of a billow is between the cells of the shape noise than in their middles; shares of the height of the cloud
|
||||
const float CLOUD_BASE_ROUND = 0.35;
|
||||
const float CLOUD_BASE_ROUND_DEPTH = 0.3;
|
||||
const float CLOUD_BASE_LUMPS = 0.08;
|
||||
const float CLOUD_BASE_SHIFT = 0.07;
|
||||
const float CLOUD_BASE_SHIFT_MAX = 100.0;
|
||||
const float CLOUD_TOP_SHIFT = 0.15;
|
||||
const float CLOUD_TOP_SHIFT_MAX = 400.0;
|
||||
// the detail noise: its tile against the tile of the shape noise and how deep it eats into the edges (times the erosion of the
|
||||
// genus and the tuning of the debug window, cloud_wind.w); the finer octave of the thin edges: its tile against the detail tile,
|
||||
// how far the coarser octave pushes its lookup about (in its tiles), and the density below which it is used
|
||||
const float CLOUD_DETAIL_SCALE = 6.0;
|
||||
const float CLOUD_EROSION = 2.0;
|
||||
const float CLOUD_FINE_SCALE = 3.7;
|
||||
const float CLOUD_FINE_PUSH = 0.6;
|
||||
const float CLOUD_FINE_BELOW = 0.6;
|
||||
|
||||
float cloud_remap(float value, float low, float high, float newlow, float newhigh)
|
||||
{
|
||||
return newlow + (value - low) / max(high - low, 1e-5) * (newhigh - newlow);
|
||||
}
|
||||
|
||||
// height of a point above the ground reference, the earth curving down with the square of the distance
|
||||
float cloud_height(vec3 position)
|
||||
{
|
||||
return weather_camera.x + position.y - dot(position.xz, position.xz) * (0.5 / CLOUD_EARTH_RADIUS);
|
||||
}
|
||||
|
||||
// distances along a ray (unit Direction from the camera) where it is at Height: the height goes as
|
||||
// camera + up * t - (1 - up^2) / (2 R) * t^2. returns the number of crossings ahead (0-2), sorted into T
|
||||
int cloud_ray_height(float up, float height, out vec2 t)
|
||||
{
|
||||
t = vec2(-1.0);
|
||||
float a = (1.0 - up * up) * (0.5 / CLOUD_EARTH_RADIUS);
|
||||
float b = -up;
|
||||
float c = height - weather_camera.x;
|
||||
// a t^2 + b t + c = 0
|
||||
if (a < 1e-12)
|
||||
{
|
||||
if (abs(b) < 1e-8)
|
||||
return 0;
|
||||
float root = -c / b;
|
||||
t = vec2(root, -1.0);
|
||||
return root > 0.0 ? 1 : 0;
|
||||
}
|
||||
float discriminant = b * b - 4.0 * a * c;
|
||||
if (discriminant < 0.0)
|
||||
return 0;
|
||||
float q = -0.5 * (b + (b >= 0.0 ? 1.0 : -1.0) * sqrt(discriminant));
|
||||
float r0 = q / a;
|
||||
float r1 = (abs(q) > 1e-12) ? c / q : r0;
|
||||
vec2 roots = vec2(min(r0, r1), max(r0, r1));
|
||||
if (roots.y <= 0.0)
|
||||
return 0;
|
||||
if (roots.x <= 0.0)
|
||||
{
|
||||
t = vec2(roots.y, -1.0);
|
||||
return 1;
|
||||
}
|
||||
t = roots;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// the first stretch of a ray within the heights of a layer, x to y; y <= x when it never gets there
|
||||
vec2 cloud_layer_segment(float up, float base, float top)
|
||||
{
|
||||
vec2 tb, tt;
|
||||
int nb = cloud_ray_height(up, base, tb);
|
||||
int nt = cloud_ray_height(up, top, tt);
|
||||
// the crossings in order; the ray goes in and out of the layer at each
|
||||
float crossings[4];
|
||||
int count = 0;
|
||||
for (int i = 0; i < nb; i++)
|
||||
crossings[count++] = tb[i];
|
||||
for (int i = 0; i < nt; i++)
|
||||
crossings[count++] = tt[i];
|
||||
for (int i = 1; i < count; i++)
|
||||
for (int j = i; j > 0 && crossings[j - 1] > crossings[j]; j--)
|
||||
{
|
||||
float swap = crossings[j];
|
||||
crossings[j] = crossings[j - 1];
|
||||
crossings[j - 1] = swap;
|
||||
}
|
||||
bool inside = (weather_camera.x >= base) && (weather_camera.x <= top);
|
||||
float start = 0.0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (inside)
|
||||
return vec2(start, crossings[i]);
|
||||
inside = true;
|
||||
start = crossings[i];
|
||||
}
|
||||
return inside ? vec2(start, 1e9) : vec2(0.0, -1.0);
|
||||
}
|
||||
|
||||
// the position in the noise of a layer: the camera's place in it and the offset from the camera, stretched along the wind
|
||||
vec2 cloud_noise_plane(cloud_layer_s layer, vec2 offset)
|
||||
{
|
||||
vec2 wind = cloud_wind.xy;
|
||||
float along = dot(offset, wind);
|
||||
vec2 across = offset - wind * along;
|
||||
return across + wind * (along / max(layer.motion.w, 1.0));
|
||||
}
|
||||
|
||||
// how far the base (x) and the top (y) of the clouds of a layer may stand off those of the layer, metres
|
||||
vec2 cloud_layer_shift(cloud_layer_s layer)
|
||||
{
|
||||
float thickness = layer.heights.y - layer.heights.x;
|
||||
return vec2(min(thickness * CLOUD_BASE_SHIFT, CLOUD_BASE_SHIFT_MAX), min(thickness * CLOUD_TOP_SHIFT, CLOUD_TOP_SHIFT_MAX));
|
||||
}
|
||||
|
||||
// the lowest base (x) and the highest top (y) the clouds of a layer may reach
|
||||
vec2 cloud_layer_bounds(cloud_layer_s layer)
|
||||
{
|
||||
vec2 shift = cloud_layer_shift(layer);
|
||||
return vec2(layer.heights.x - shift.x, layer.heights.y + shift.y);
|
||||
}
|
||||
|
||||
// share of the layer filled at a place, x: the soft mask of the coverage, y: how far into a cloud area (0 at its edge). Shift: how
|
||||
// far the base (x) and the top (y) of the cloud there stand off those of the layer, from the second and the third noise of the
|
||||
// coverage texture, so the clouds which reach lower don't all reach higher too
|
||||
vec2 cloud_coverage(cloud_layer_s layer, vec2 offset, out vec2 shift)
|
||||
{
|
||||
vec2 coords = (layer.extra.zw + cloud_noise_plane(layer, offset)) * layer.motion.z;
|
||||
vec3 noise = textureLod(cloud_coverage_noise, coords + layer.extra.y, 0.0).rgb;
|
||||
shift = (noise.yz * 2.0 - 1.0) * cloud_layer_shift(layer);
|
||||
float coverage = layer.shape.x;
|
||||
float threshold = 1.0 - coverage;
|
||||
float mask = smoothstep(threshold - 0.06, threshold + 0.06, noise.x);
|
||||
float depth = clamp((noise.x - threshold) / max(coverage, 0.05), 0.0, 1.0);
|
||||
return vec2(mask, depth);
|
||||
}
|
||||
|
||||
vec2 cloud_coverage(cloud_layer_s layer, vec2 offset)
|
||||
{
|
||||
vec2 shift;
|
||||
return cloud_coverage(layer, offset, shift);
|
||||
}
|
||||
|
||||
// extinction (1/m) of a volumetric layer at a point; Detail eats the edges with the small noise
|
||||
float cloud_layer_density(int index, vec3 position, float height, bool detail)
|
||||
{
|
||||
cloud_layer_s layer = cloud_layers[index];
|
||||
vec2 bounds = cloud_layer_bounds(layer);
|
||||
if (height <= bounds.x || height >= bounds.y)
|
||||
return 0.0;
|
||||
vec2 shift;
|
||||
vec2 coverage = cloud_coverage(layer, position.xz, shift);
|
||||
float bottom = layer.heights.x + shift.x;
|
||||
float h = (height - bottom) / max(layer.heights.y + shift.y - bottom, 1.0);
|
||||
if (h <= 0.0 || h >= 1.0)
|
||||
return 0.0;
|
||||
// the anvil spreads the top of the storm clouds out of their area
|
||||
if (layer.profile.w > 0.0)
|
||||
{
|
||||
float anvil = layer.profile.w * smoothstep(0.7, 0.92, h) * (1.0 - smoothstep(0.97, 1.0, h));
|
||||
coverage.x = max(coverage.x, anvil * smoothstep(0.0, 0.5, coverage.y + 0.35));
|
||||
coverage.y = max(coverage.y, anvil * 0.6);
|
||||
}
|
||||
if (coverage.x <= 0.0)
|
||||
return 0.0;
|
||||
// the tops of the single clouds are of different heights, the larger ones (deeper in their area) the taller
|
||||
float top = mix(1.0 - layer.profile.z, 1.0, coverage.y);
|
||||
float fall = 1.0 - smoothstep(top * layer.profile.y, top, h);
|
||||
if (fall <= 0.0)
|
||||
return 0.0;
|
||||
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;
|
||||
vec4 shape = textureLod(cloud_shape_noise, coords, 0.0);
|
||||
// the smaller cells of the worley octaves bite billows into the heaps of the perlin-worley noise; the octaves keep to a narrow
|
||||
// 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);
|
||||
// the base is not cut flat: it curves up towards the edge of the cloud into its sides, and every billow starts a little higher or
|
||||
// lower; both as shares of the height of the cloud, so the small ones are rounded too and not cut away. the sheets (little
|
||||
// erosion) keep a flatter base
|
||||
float lift = (CLOUD_BASE_ROUND * (1.0 - smoothstep(0.0, CLOUD_BASE_ROUND_DEPTH, coverage.y)) + CLOUD_BASE_LUMPS * (1.0 - worley)) * top * clamp(layer.shape.w * 2.5, 0.0, 1.0);
|
||||
float gradient = smoothstep(lift, lift + layer.profile.x, h) * fall;
|
||||
if (gradient <= 0.0)
|
||||
return 0.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
|
||||
base = mix(1.0, base, clamp(layer.shape.w * 2.5, 0.25, 1.0));
|
||||
// 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)
|
||||
{
|
||||
vec3 detailcoords = coords * CLOUD_DETAIL_SCALE;
|
||||
vec4 small = textureLod(cloud_detail_noise, detailcoords, 0.0);
|
||||
// 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);
|
||||
// the thin outer part of a cloud gets a finer octave too, its lookup pushed about by the coarser one, so the edges are frayed
|
||||
// into small billows and torn bits rather than evenly rounded. the dense inside is never eaten into, so it is skipped there
|
||||
float fineshare = 1.0 - smoothstep(CLOUD_FINE_BELOW * 0.5, CLOUD_FINE_BELOW, density);
|
||||
if (fineshare > 0.0)
|
||||
{
|
||||
vec3 push = (small.gbr - 0.5) * CLOUD_FINE_PUSH;
|
||||
vec4 fine = textureLod(cloud_detail_noise, detailcoords * CLOUD_FINE_SCALE + push, 0.0);
|
||||
float finefbm = clamp((fine.r * 0.625 + fine.g * 0.25 + fine.b * 0.125 - 0.30) / 0.35, 0.0, 1.0);
|
||||
fbm = clamp(mix(fbm, fbm * 0.65 + finefbm * 0.35, fineshare), 0.0, 1.0);
|
||||
}
|
||||
// wispy at the base, billowing at the top
|
||||
float erosion = mix(fbm, 1.0 - fbm, clamp(h * 4.0, 0.0, 1.0));
|
||||
density = clamp(cloud_remap(density, erosion * layer.shape.w * CLOUD_EROSION * cloud_wind.w, 1.0, 0.0, 1.0), 0.0, 1.0);
|
||||
}
|
||||
return min(density * CLOUD_EDGE_SHARPNESS, 1.0) * layer.shape.y;
|
||||
}
|
||||
|
||||
// the same without the detail, cheaper, for the shadows and the fog
|
||||
float cloud_density_coarse(vec3 position)
|
||||
{
|
||||
float height = cloud_height(position);
|
||||
float density = 0.0;
|
||||
for (int i = 0; i < int(weather_camera.w); i++)
|
||||
{
|
||||
if (cloud_layers[i].extra.x > 0.5)
|
||||
continue;
|
||||
density += cloud_layer_density(i, position, height, false);
|
||||
}
|
||||
return density;
|
||||
}
|
||||
|
||||
// optical depth of a flat layer (cirrus and the like) where a ray crosses its middle, Up the vertical part of the ray there
|
||||
float cloud_flat_depth(int index, vec3 position)
|
||||
{
|
||||
cloud_layer_s layer = cloud_layers[index];
|
||||
vec2 coverage = cloud_coverage(layer, position.xz);
|
||||
if (coverage.x <= 0.0)
|
||||
return 0.0;
|
||||
vec2 plane = layer.motion.xy + cloud_noise_plane(layer, position.xz);
|
||||
vec2 coords = plane * layer.shape.z;
|
||||
// strands and ripples out of slices of the noise
|
||||
vec4 shape = textureLod(cloud_shape_noise, vec3(coords, 0.37 + layer.extra.y), 0.0);
|
||||
vec4 small = textureLod(cloud_detail_noise, vec3(coords * 4.0, 0.61), 0.0);
|
||||
float strands = clamp(cloud_remap(shape.r * 0.7 + small.r * 0.3, 0.35 * layer.shape.w + (1.0 - coverage.x) * 0.5, 1.0, 0.0, 1.0), 0.0, 1.0);
|
||||
return strands * coverage.x * layer.shape.y;
|
||||
}
|
||||
|
||||
// the shadow of the clouds at a point: what they let through towards the key light. Steps per layer
|
||||
float cloud_transmittance_to_sun(vec3 position, int steps)
|
||||
{
|
||||
vec3 sun = weather_sun_direction.xyz;
|
||||
if (sun.y <= 0.01)
|
||||
return fog_light.y;
|
||||
float height = cloud_height(position);
|
||||
float depth = 0.0;
|
||||
for (int i = 0; i < int(weather_camera.w); i++)
|
||||
{
|
||||
cloud_layer_s layer = cloud_layers[i];
|
||||
if (layer.extra.x > 0.5)
|
||||
{
|
||||
float middle = 0.5 * (layer.heights.x + layer.heights.y);
|
||||
if (middle > height)
|
||||
depth += cloud_flat_depth(i, position + sun * ((middle - height) / sun.y)) / sun.y;
|
||||
continue;
|
||||
}
|
||||
vec2 bounds = cloud_layer_bounds(layer);
|
||||
float enter = max(bounds.x - height, 0.0) / sun.y;
|
||||
float leave = (bounds.y - height) / sun.y;
|
||||
if (leave <= 0.0)
|
||||
continue;
|
||||
float step = (leave - enter) / float(steps);
|
||||
for (int s = 0; s < steps; s++)
|
||||
{
|
||||
vec3 point = position + sun * (enter + step * (float(s) + 0.5));
|
||||
depth += cloud_layer_density(i, point, cloud_height(point), false) * step;
|
||||
}
|
||||
}
|
||||
return exp(-depth);
|
||||
}
|
||||
290
shaders/ogl46/clouds_march.comp
Normal file
290
shaders/ogl46/clouds_march.comp
Normal file
@@ -0,0 +1,290 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// clouds of the weather ray marched through their layers (clouds_common.glsl), into an image the sky pass lays over the sky
|
||||
// (sky_weather.frag). mode 0: the view of the main camera at a part of the resolution, smoothed over frames by
|
||||
// clouds_temporal.comp; mode 1: a panorama around the camera (azimuth across, elevation up), a band of rows per frame, for the
|
||||
// environment map and the other views. rgb: the light of the clouds with the fog in front of them, a: what they let through
|
||||
// of the sky behind; the depth image gets the distance of the clouds, for the reprojection
|
||||
|
||||
#include "common.glsl"
|
||||
#include "clouds_common.glsl"
|
||||
|
||||
layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in;
|
||||
|
||||
layout(rgba16f, binding = 0) uniform writeonly image2D clouds_color;
|
||||
layout(r32f, binding = 1) uniform writeonly image2D clouds_depth;
|
||||
|
||||
layout(location = 0) uniform int mode;
|
||||
layout(location = 1) uniform ivec2 size;
|
||||
// mode 0: clip space to view space of the camera, and view space to world axes
|
||||
layout(location = 2) uniform mat4 inverse_projection;
|
||||
layout(location = 3) uniform mat4 view_to_world;
|
||||
layout(location = 4) uniform int frame;
|
||||
// x: samples through a layer at most, y: samples towards the sun, z: farthest distance (m), w: 1 with the detail noise
|
||||
layout(location = 5) uniform vec4 quality;
|
||||
// mode 1: first row of the band and the rows in it
|
||||
layout(location = 6) uniform ivec2 rows;
|
||||
|
||||
// lowest elevation of the panorama (radians); its rows go with the square of the height, denser at the horizon
|
||||
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),
|
||||
// times the tuning of the debug window (weather_sun_color.w, weather_ambient.w). the sunlight three times that of the sunlit faces,
|
||||
// set by eye in the simulator: the sunlit sides of real cumulus are several times brighter than the sky around them. the sky light
|
||||
// about as the shaded faces get it: more of it greys the shaded sides of the clouds into a flat pastel
|
||||
const float CLOUD_SUN_SCALE = 4.5;
|
||||
const float CLOUD_AMBIENT_SCALE = 0.6;
|
||||
// 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 below a point, or above it past CLOUD_SKY_REACH
|
||||
const float CLOUD_AMBIENT_FADE = 0.05;
|
||||
// the part of the cloud over a point looked at for the sky light held back (cloud_sky_depth), metres, how fast the light fades
|
||||
// per optical depth there (less than the extinction: the light scattered many times gets through further), and the share left
|
||||
// in the darkest places
|
||||
const float CLOUD_SKY_REACH = 400.0;
|
||||
const float CLOUD_SKY_FADE = 0.25;
|
||||
const float CLOUD_SKY_FLOOR = 0.15;
|
||||
// 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;
|
||||
// how many of the nearest steps towards the sun see the detail noise
|
||||
const int CLOUD_LIGHT_DETAIL_STEPS = 1;
|
||||
// orders of the scattering the sunlight is followed through (cloud_sunlight): the higher ones reach deep into a thick cloud, so its
|
||||
// shaded sides and its inside are lit through the cloud too, not only by the sky, and a big cloud close by is not a flat grey
|
||||
const int CLOUD_SCATTER_OCTAVES = 6;
|
||||
// how much longer every next step towards the sun is than the one before: 1 + this / steps², so the first step is short where the
|
||||
// light falls off fastest (about a 30th of the reach at 4 steps) and the steps get close to even when there are many of them
|
||||
const float CLOUD_LIGHT_GROWTH = 24.0;
|
||||
|
||||
float henyey_greenstein(float cosangle, float g)
|
||||
{
|
||||
// times 4 pi: 1 for an even scattering
|
||||
float g2 = g * g;
|
||||
return (1.0 - g2) / pow(max(1.0 + g2 - 2.0 * g * cosangle, 1e-4), 1.5);
|
||||
}
|
||||
|
||||
float cloud_phase(float cosangle, float scale)
|
||||
{
|
||||
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 * weather_ambient.w);
|
||||
}
|
||||
|
||||
// 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
|
||||
float cloud_sunlight(int index, vec3 position, float cosangle, int steps, float powder, bool detail)
|
||||
{
|
||||
vec3 sun = weather_sun_direction.xyz;
|
||||
cloud_layer_s layer = cloud_layers[index];
|
||||
float thickness = layer.heights.y - layer.heights.x;
|
||||
float reach = min(thickness, 1500.0);
|
||||
float depth = 0.0;
|
||||
float travelled = 0.0;
|
||||
// steps growing geometrically: near the point the shape matters, far only the bulk. with steps growing evenly the first one was a
|
||||
// tenth of the reach at 4 steps, and the light fell off the smooth sides of the clouds in visible bands
|
||||
float growth = 1.0 + CLOUD_LIGHT_GROWTH / float(steps * steps);
|
||||
float step = reach * (growth - 1.0) / (pow(growth, float(steps)) - 1.0);
|
||||
for (int s = 0; s < steps; s++)
|
||||
{
|
||||
vec3 point = position + sun * (travelled + 0.5 * step);
|
||||
travelled += step;
|
||||
// the nearest steps with the detail noise: the small billows shade one another, lit on top and grey beneath
|
||||
depth += cloud_layer_density(index, point, cloud_height(point), detail && (s < CLOUD_LIGHT_DETAIL_STEPS)) * step;
|
||||
step *= growth;
|
||||
}
|
||||
float light = 0.0;
|
||||
float amount = 1.0;
|
||||
float fade = 1.0;
|
||||
float spread = 1.0;
|
||||
for (int octave = 0; octave < CLOUD_SCATTER_OCTAVES; octave++)
|
||||
{
|
||||
light += amount * exp(-depth * fade) * cloud_phase(cosangle, spread) * (octave == 0 ? powder : 1.0);
|
||||
amount *= 0.5;
|
||||
fade *= 0.5;
|
||||
spread *= 0.5;
|
||||
}
|
||||
return light;
|
||||
}
|
||||
|
||||
// optical depth of the cloud over a point up to Reach above it, from Samples of the coarse density: under a lump and deep in a
|
||||
// thick cloud the sky light is held back, at the tops and the thin edges it is not, so the shaded sides take the form of the cloud
|
||||
float cloud_sky_depth(int index, vec3 position, float height, float reach, int samples)
|
||||
{
|
||||
float depth = 0.0;
|
||||
for (int s = 0; s < samples; s++)
|
||||
{
|
||||
// up along the vertical: the height grows by the same distance (cloud_height bends only with the horizontal distance)
|
||||
float up = reach * (float(s) + 0.5) / float(samples);
|
||||
depth += cloud_layer_density(index, position + vec3(0.0, up, 0.0), height + up, false);
|
||||
}
|
||||
return depth * reach / float(samples);
|
||||
}
|
||||
|
||||
float interleaved_noise(vec2 pixel)
|
||||
{
|
||||
return fract(52.9829189 * fract(dot(pixel, vec2(0.06711056, 0.00583715))));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec3 direction;
|
||||
if (mode == 0)
|
||||
{
|
||||
if (any(greaterThanEqual(texel, size)))
|
||||
return;
|
||||
vec2 ndc = (vec2(texel) + 0.5) / vec2(size) * 2.0 - 1.0;
|
||||
// reverse z: the near plane at 1
|
||||
vec4 near = inverse_projection * vec4(ndc, 1.0, 1.0);
|
||||
direction = normalize(mat3(view_to_world) * normalize(near.xyz / near.w));
|
||||
}
|
||||
else
|
||||
{
|
||||
texel.y += rows.x;
|
||||
if (texel.x >= size.x || texel.y >= min(size.y, rows.x + rows.y))
|
||||
return;
|
||||
vec2 coords = (vec2(texel) + 0.5) / vec2(size);
|
||||
float elevation = PANORAMA_LOW + (0.5 * WEATHER_PI - PANORAMA_LOW) * coords.y * coords.y;
|
||||
float azimuth = coords.x * 2.0 * WEATHER_PI;
|
||||
direction = vec3(cos(elevation) * sin(azimuth), sin(elevation), cos(elevation) * cos(azimuth));
|
||||
}
|
||||
|
||||
vec3 sun = weather_sun_direction.xyz;
|
||||
float cosangle = dot(direction, sun);
|
||||
float sunup = weather_sun_direction.w;
|
||||
float maxdistance = quality.z;
|
||||
int maxsteps = int(quality.x);
|
||||
int lightsteps = int(quality.y);
|
||||
int skysteps = (lightsteps >= 4) ? 2 : 1;
|
||||
bool detail = quality.w > 0.5;
|
||||
float jitter = fract(interleaved_noise(vec2(texel)) + float(frame) * 0.618034);
|
||||
|
||||
vec3 scattered = vec3(0.0);
|
||||
float transmittance = 1.0;
|
||||
float depthsum = 0.0;
|
||||
float weightsum = 0.0;
|
||||
int layers = int(weather_camera.w);
|
||||
|
||||
// the stretches of the volumetric layers along the ray, nearest first
|
||||
vec2 segments[MAX_CLOUD_LAYERS];
|
||||
int order[MAX_CLOUD_LAYERS];
|
||||
int count = 0;
|
||||
for (int i = 0; i < layers; i++)
|
||||
{
|
||||
if (cloud_layers[i].extra.x > 0.5)
|
||||
continue;
|
||||
vec2 bounds = cloud_layer_bounds(cloud_layers[i]);
|
||||
vec2 segment = cloud_layer_segment(direction.y, bounds.x, bounds.y);
|
||||
segment.y = min(segment.y, maxdistance);
|
||||
if (segment.y <= segment.x)
|
||||
continue;
|
||||
int at = count++;
|
||||
segments[at] = segment;
|
||||
order[at] = i;
|
||||
for (int j = at; j > 0 && segments[j - 1].x > segments[j].x; j--)
|
||||
{
|
||||
vec2 swapsegment = segments[j];
|
||||
segments[j] = segments[j - 1];
|
||||
segments[j - 1] = swapsegment;
|
||||
int swaporder = order[j];
|
||||
order[j] = order[j - 1];
|
||||
order[j - 1] = swaporder;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 ambienttop = cloud_sky_light(1.0);
|
||||
vec3 sunlight = weather_sun_color.rgb * (CLOUD_SUN_SCALE * weather_sun_color.w) * 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++)
|
||||
{
|
||||
int index = order[k];
|
||||
cloud_layer_s layer = cloud_layers[index];
|
||||
vec2 segment = segments[k];
|
||||
float span = segment.y - segment.x;
|
||||
float thickness = layer.heights.y - layer.heights.x;
|
||||
int steps = clamp(int(float(maxsteps) * span / (thickness * 4.0)), 8, maxsteps);
|
||||
float step = span / float(steps);
|
||||
for (int s = 0; s < steps; s++)
|
||||
{
|
||||
float t = segment.x + (float(s) + jitter) * step;
|
||||
vec3 position = direction * t;
|
||||
float height = cloud_height(position);
|
||||
float density = cloud_layer_density(index, position, height, detail);
|
||||
if (density <= 0.0)
|
||||
continue;
|
||||
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, powder, detail) : 0.0;
|
||||
// the light of the sky reaches inside through the cloud above, that of the ground through the cloud below: the base of a
|
||||
// thick cloud is darker than its top, and greyer, lit only by the ground and the sky at its sides
|
||||
float column = layer.shape.y * layer.shape.x;
|
||||
float top = cloud_layer_bounds(layer).y;
|
||||
float reach = clamp(top - height, 0.0, CLOUD_SKY_REACH);
|
||||
float sky = exp(-CLOUD_SKY_FADE * cloud_sky_depth(index, position, height, reach, skysteps) - CLOUD_AMBIENT_FADE * column * max(top - height - reach, 0.0));
|
||||
float below = column * max(height - layer.heights.x, 0.0);
|
||||
vec3 light = sunlight * sunshare + (ambienttop * mix(CLOUD_SKY_FLOOR, 1.0, sky) + ambientbottom * exp(-CLOUD_AMBIENT_FADE * below));
|
||||
float through = exp(-density * step);
|
||||
vec3 inscattered = light * (1.0 - through);
|
||||
scattered += transmittance * inscattered;
|
||||
float weight = transmittance * (1.0 - through);
|
||||
depthsum += t * weight;
|
||||
weightsum += weight;
|
||||
transmittance *= through;
|
||||
if (transmittance < 0.01)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// the thin high layers, past the others
|
||||
for (int i = 0; i < layers && transmittance > 0.01; i++)
|
||||
{
|
||||
cloud_layer_s layer = cloud_layers[i];
|
||||
if (layer.extra.x < 0.5)
|
||||
continue;
|
||||
vec2 crossing;
|
||||
if (cloud_ray_height(direction.y, 0.5 * (layer.heights.x + layer.heights.y), crossing) == 0)
|
||||
continue;
|
||||
float t = crossing.x;
|
||||
if (t > maxdistance * 2.0)
|
||||
continue;
|
||||
vec3 position = direction * t;
|
||||
// the slant of the ray through the sheet where it crosses it, the earth curving away
|
||||
float slope = max(abs(direction.y - (1.0 - direction.y * direction.y) * t / CLOUD_EARTH_RADIUS), 0.03);
|
||||
float depth = cloud_flat_depth(i, position) / slope;
|
||||
if (depth <= 0.0)
|
||||
continue;
|
||||
float through = exp(-depth);
|
||||
// ice crystals scatter forwards strongly
|
||||
vec3 light = sunlight * henyey_greenstein(cosangle, 0.6) * 0.8 + ambienttop;
|
||||
scattered += transmittance * light * (1.0 - through);
|
||||
float weight = transmittance * (1.0 - through);
|
||||
depthsum += t * weight;
|
||||
weightsum += weight;
|
||||
transmittance *= through;
|
||||
}
|
||||
|
||||
// the haze the atmosphere has in the sky already lies between the camera and the clouds too; the rest of the fog goes over
|
||||
// the clouds and the sky alike when they are laid over it (sky_weather.frag)
|
||||
float span_distance = (weightsum > 1e-5) ? depthsum / weightsum : maxdistance;
|
||||
if (weightsum > 1e-5)
|
||||
{
|
||||
vec4 fog = fog_analytic_with(direction, 0.0, span_distance, min(fog_haze.x, fog_haze.z), 0.0);
|
||||
scattered = scattered * fog.a + fog.rgb * (1.0 - transmittance);
|
||||
}
|
||||
if (any(isnan(scattered)) || isnan(transmittance))
|
||||
{
|
||||
scattered = vec3(0.0);
|
||||
transmittance = 1.0;
|
||||
}
|
||||
imageStore(clouds_color, texel, vec4(scattered, transmittance));
|
||||
if (mode == 0)
|
||||
imageStore(clouds_depth, texel, vec4(span_distance));
|
||||
}
|
||||
136
shaders/ogl46/clouds_noise.comp
Normal file
136
shaders/ogl46/clouds_noise.comp
Normal file
@@ -0,0 +1,136 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// the tiling noises the clouds and the fog are made of, made once at the start (opengl46_weather::init).
|
||||
// kind 0, the shape (3d): r perlin-worley (perlin fbm with the worley cells carved in), g b a worley fbm of rising frequency;
|
||||
// kind 1, the detail (3d): r g b worley fbm of rising frequency;
|
||||
// kind 2, the coverage (2d): r g b three perlin fbm, spread evenly over 0-1 so a threshold of 1 - c leaves a share c above it
|
||||
|
||||
#include "common.glsl"
|
||||
#include "random.glsl"
|
||||
|
||||
layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in;
|
||||
|
||||
layout(rgba8, binding = 0) uniform writeonly image3D noise_volume;
|
||||
layout(rgba8, binding = 1) uniform writeonly image2D noise_plane;
|
||||
|
||||
layout(location = 0) uniform int kind;
|
||||
layout(location = 1) uniform int size;
|
||||
|
||||
vec3 hash3(ivec3 cell, int period, uint seed)
|
||||
{
|
||||
uvec3 wrapped = uvec3((cell % period + period) % period);
|
||||
uint h = Hash(uvec3(wrapped.x + seed * 977U, wrapped.y, wrapped.z));
|
||||
uint h2 = Hash(h + 0x68bc21ebU);
|
||||
uint h3 = Hash(h2 + 0x02e5be93U);
|
||||
return vec3(float(h & 0xffffU), float(h2 & 0xffffU), float(h3 & 0xffffU)) / 65535.0;
|
||||
}
|
||||
|
||||
// gradient noise repeating every Period cells, -1..1 about
|
||||
float perlin(vec3 position, int period, uint seed)
|
||||
{
|
||||
ivec3 cell = ivec3(floor(position));
|
||||
vec3 local = fract(position);
|
||||
vec3 fade = local * local * local * (local * (local * 6.0 - 15.0) + 10.0);
|
||||
float corners[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
ivec3 offset = ivec3(i & 1, (i >> 1) & 1, (i >> 2) & 1);
|
||||
vec3 gradient = normalize(hash3(cell + offset, period, seed) * 2.0 - 1.0 + 1e-4);
|
||||
corners[i] = dot(gradient, local - vec3(offset));
|
||||
}
|
||||
float x0 = mix(corners[0], corners[1], fade.x);
|
||||
float x1 = mix(corners[2], corners[3], fade.x);
|
||||
float x2 = mix(corners[4], corners[5], fade.x);
|
||||
float x3 = mix(corners[6], corners[7], fade.x);
|
||||
return mix(mix(x0, x1, fade.y), mix(x2, x3, fade.y), fade.z) * 1.6;
|
||||
}
|
||||
|
||||
// inverted distance to the nearest of the points scattered one per cell, 0-1, repeating every Period cells
|
||||
float worley(vec3 position, int period, uint seed)
|
||||
{
|
||||
ivec3 cell = ivec3(floor(position));
|
||||
vec3 local = fract(position);
|
||||
float nearest = 1.0;
|
||||
for (int z = -1; z <= 1; z++)
|
||||
for (int y = -1; y <= 1; y++)
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
ivec3 offset = ivec3(x, y, z);
|
||||
vec3 point = vec3(offset) + hash3(cell + offset, period, seed);
|
||||
nearest = min(nearest, length(point - local));
|
||||
}
|
||||
return 1.0 - clamp(nearest, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float perlin_fbm(vec3 position, int frequency, int octaves, uint seed)
|
||||
{
|
||||
float sum = 0.0;
|
||||
float amplitude = 0.5;
|
||||
float total = 0.0;
|
||||
for (int i = 0; i < octaves; i++)
|
||||
{
|
||||
sum += perlin(position * float(frequency), frequency, seed + uint(i)) * amplitude;
|
||||
total += amplitude;
|
||||
amplitude *= 0.5;
|
||||
frequency *= 2;
|
||||
}
|
||||
return sum / total;
|
||||
}
|
||||
|
||||
float worley_fbm(vec3 position, int frequency, uint seed)
|
||||
{
|
||||
return worley(position * float(frequency), frequency, seed) * 0.625
|
||||
+ worley(position * float(frequency * 2), frequency * 2, seed + 1U) * 0.25
|
||||
+ worley(position * float(frequency * 4), frequency * 4, seed + 2U) * 0.125;
|
||||
}
|
||||
|
||||
// octaves of the coverage noise: it only places the clouds and gives their outline. it is the same at every height, so its finer
|
||||
// octaves would stand as upright grooves down the sides of the clouds; their small shapes come from the 3d noises
|
||||
const int COVERAGE_OCTAVES = 4;
|
||||
|
||||
// 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)
|
||||
{
|
||||
return newlow + (value - low) / (high - low) * (newhigh - newlow);
|
||||
}
|
||||
|
||||
// a sum of noises is about normally distributed; this spreads it about evenly over 0-1
|
||||
float even(float value, float spread)
|
||||
{
|
||||
return clamp(0.5 + 0.5 * tanh(value / spread * 1.1), 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec3 texel = ivec3(gl_GlobalInvocationID);
|
||||
if (any(greaterThanEqual(texel.xy, ivec2(size))))
|
||||
return;
|
||||
if (kind == 2)
|
||||
{
|
||||
vec3 position = vec3((vec2(texel.xy) + 0.5) / float(size), 0.0);
|
||||
float first = perlin_fbm(position, 4, COVERAGE_OCTAVES, 11U);
|
||||
float second = perlin_fbm(position + vec3(0.0, 0.0, 0.5), 8, 5, 23U);
|
||||
float third = perlin_fbm(position + vec3(0.0, 0.0, 0.25), 8, 5, 29U);
|
||||
imageStore(noise_plane, texel.xy, vec4(even(first, 0.22), even(second, 0.22), even(third, 0.22), 1.0));
|
||||
return;
|
||||
}
|
||||
if (texel.z >= size)
|
||||
return;
|
||||
vec3 position = (vec3(texel) + 0.5) / float(size);
|
||||
if (kind == 0)
|
||||
{
|
||||
// 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 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)));
|
||||
}
|
||||
else
|
||||
imageStore(noise_volume, texel, vec4(worley_fbm(position, 4, 13U), worley_fbm(position, 8, 15U), worley_fbm(position, 16, 17U), 1.0));
|
||||
}
|
||||
29
shaders/ogl46/clouds_shadow.comp
Normal file
29
shaders/ogl46/clouds_shadow.comp
Normal file
@@ -0,0 +1,29 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// the shadow the clouds cast on the ground: for points of the ground reference around the camera, what the clouds let through
|
||||
// towards the key light. the shading (light_common.glsl) and the fog (fog_inject.comp) look it up along the ray of the sun,
|
||||
// see cloud_shadow() in weather_common.glsl
|
||||
|
||||
#include "common.glsl"
|
||||
#include "clouds_common.glsl"
|
||||
|
||||
layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in;
|
||||
|
||||
layout(r16f, binding = 0) uniform writeonly image2D shadow;
|
||||
|
||||
layout(location = 1) uniform int size;
|
||||
// world x and z of the centre of the map relative to the camera, size of the map (metres)
|
||||
layout(location = 2) uniform vec3 placement;
|
||||
// samples through a layer
|
||||
layout(location = 3) uniform int steps;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(texel, ivec2(size))))
|
||||
return;
|
||||
vec2 offset = placement.xy + ((vec2(texel) + 0.5) / float(size) - 0.5) * placement.z;
|
||||
// the ground reference, the earth curving away
|
||||
vec3 ground = vec3(offset.x, -weather_camera.x + dot(offset, offset) * (0.5 / CLOUD_EARTH_RADIUS), offset.y);
|
||||
imageStore(shadow, texel, vec4(cloud_transmittance_to_sun(ground, steps)));
|
||||
}
|
||||
67
shaders/ogl46/clouds_temporal.comp
Normal file
67
shaders/ogl46/clouds_temporal.comp
Normal file
@@ -0,0 +1,67 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// the clouds of the main view smoothed over frames: the image of this frame (clouds_march.comp, ray started at another
|
||||
// distance every frame) blended into the one of the previous frames, found where the clouds were by the distance of the
|
||||
// clouds, the rotation and the move of the camera. the history is held to the range of the neighbourhood of this frame,
|
||||
// so moving clouds and a turning camera don't leave trails
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in;
|
||||
|
||||
layout(binding = CLOUD_TEX) uniform sampler2D current_color;
|
||||
layout(binding = CLOUD_SHAPE_TEX) uniform sampler2D current_depth;
|
||||
layout(binding = CLOUD_HISTORY_TEX) uniform sampler2D history_color;
|
||||
layout(rgba16f, binding = 0) uniform writeonly image2D result;
|
||||
|
||||
layout(location = 1) uniform ivec2 size;
|
||||
layout(location = 2) uniform mat4 inverse_projection;
|
||||
layout(location = 3) uniform mat4 view_to_world;
|
||||
// world axes to the clip space of the previous frame, the camera of the previous frame at the origin
|
||||
layout(location = 4) uniform mat4 previous_projection_view;
|
||||
// where the camera was in the previous frame, relative to where it is (world, metres)
|
||||
layout(location = 5) uniform vec3 previous_camera;
|
||||
// share of this frame in the result; 1 when there is no history
|
||||
layout(location = 6) uniform float blend;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(texel, size)))
|
||||
return;
|
||||
vec4 current = texelFetch(current_color, texel, 0);
|
||||
if (blend >= 1.0)
|
||||
{
|
||||
imageStore(result, texel, current);
|
||||
return;
|
||||
}
|
||||
// the range of the neighbourhood
|
||||
vec4 low = current;
|
||||
vec4 high = current;
|
||||
for (int y = -1; y <= 1; y++)
|
||||
for (int x = -1; x <= 1; x++)
|
||||
{
|
||||
vec4 neighbour = texelFetch(current_color, clamp(texel + ivec2(x, y), ivec2(0), size - 1), 0);
|
||||
low = min(low, neighbour);
|
||||
high = max(high, neighbour);
|
||||
}
|
||||
vec2 ndc = (vec2(texel) + 0.5) / vec2(size) * 2.0 - 1.0;
|
||||
vec4 near = inverse_projection * vec4(ndc, 1.0, 1.0);
|
||||
vec3 direction = normalize(mat3(view_to_world) * normalize(near.xyz / near.w));
|
||||
float span_distance = texelFetch(current_depth, texel, 0).r;
|
||||
vec3 position = direction * span_distance - previous_camera;
|
||||
vec4 clip = previous_projection_view * vec4(position, 1.0);
|
||||
float share = blend;
|
||||
vec4 history = current;
|
||||
if (clip.w > 0.0)
|
||||
{
|
||||
vec2 coords = clip.xy / clip.w * 0.5 + 0.5;
|
||||
if (all(greaterThanEqual(coords, vec2(0.0))) && all(lessThanEqual(coords, vec2(1.0))))
|
||||
history = clamp(textureLod(history_color, coords, 0.0), low, high);
|
||||
else
|
||||
share = 1.0;
|
||||
}
|
||||
else
|
||||
share = 1.0;
|
||||
imageStore(result, texel, mix(history, current, share));
|
||||
}
|
||||
59
shaders/ogl46/cluster_build.comp
Normal file
59
shaders/ogl46/cluster_build.comp
Normal file
@@ -0,0 +1,59 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// bounding boxes of the light clusters in view space, see gl/cluster_grid.h
|
||||
// one invocation per cluster, run when the projection or the depth range of the pass changes
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = CLUSTER_BUILD_GROUP_SIZE) in;
|
||||
|
||||
// clip space w at the lower boundary of slice k: the inverse of the slice formula used when shading
|
||||
float slice_depth(uint k)
|
||||
{
|
||||
return exp((float(k) - cluster_slice_bias) / cluster_slice_scale);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
if (index >= uint(CLUSTER_COUNT))
|
||||
return;
|
||||
|
||||
uvec3 cell = uvec3(
|
||||
index % uint(CLUSTER_GRID_X),
|
||||
(index / uint(CLUSTER_GRID_X)) % uint(CLUSTER_GRID_Y),
|
||||
index / uint(CLUSTER_GRID_X * CLUSTER_GRID_Y));
|
||||
|
||||
// clip x, y and w are affine functions of the view space position, while clip z is constant (reverse z with
|
||||
// the far plane at infinity), which makes the projection matrix singular. the position is recovered from
|
||||
// those three alone, so off-axis and rotated screens of custom viewports and vr eyes work too
|
||||
mat3 linear = mat3(
|
||||
vec3(cluster_projection[0][0], cluster_projection[0][1], cluster_projection[0][3]),
|
||||
vec3(cluster_projection[1][0], cluster_projection[1][1], cluster_projection[1][3]),
|
||||
vec3(cluster_projection[2][0], cluster_projection[2][1], cluster_projection[2][3]));
|
||||
vec3 translation = vec3(cluster_projection[3][0], cluster_projection[3][1], cluster_projection[3][3]);
|
||||
mat3 unproject = inverse(linear);
|
||||
|
||||
// the tile in clip space xy, the slice in clip space w
|
||||
vec2 tiles = vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y);
|
||||
vec2 ndc_min = vec2(cell.xy) / tiles * 2.0 - 1.0;
|
||||
vec2 ndc_max = vec2(cell.xy + 1U) / tiles * 2.0 - 1.0;
|
||||
float w_near = slice_depth(cell.z);
|
||||
float w_far = slice_depth(cell.z + 1U);
|
||||
|
||||
vec3 lower = vec3(3.402823e38);
|
||||
vec3 upper = vec3(-3.402823e38);
|
||||
for (int corner = 0; corner < 8; ++corner)
|
||||
{
|
||||
vec2 ndc = vec2(
|
||||
(corner & 1) != 0 ? ndc_max.x : ndc_min.x,
|
||||
(corner & 2) != 0 ? ndc_max.y : ndc_min.y);
|
||||
float w = (corner & 4) != 0 ? w_far : w_near;
|
||||
vec3 position = unproject * (vec3(ndc * w, w) - translation);
|
||||
lower = min(lower, position);
|
||||
upper = max(upper, position);
|
||||
}
|
||||
|
||||
cluster_aabbs[index].lower = vec4(lower, 0.0);
|
||||
cluster_aabbs[index].upper = vec4(upper, 0.0);
|
||||
}
|
||||
157
shaders/ogl46/cluster_cull.comp
Normal file
157
shaders/ogl46/cluster_cull.comp
Normal file
@@ -0,0 +1,157 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// assigns the lights to the light clusters, see gl/cluster_grid.h
|
||||
// one invocation per cluster: tests every light of light_ssbo (the sun, slot 0, is handled apart from the clusters)
|
||||
// against the bounding box of the cluster in view space, and writes the indices of the lights which reach it.
|
||||
// run every frame after the lights were updated, for the grid of the pass about to be drawn
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = CLUSTER_CULL_GROUP_SIZE) in;
|
||||
|
||||
// a light is taken to end where its brightest possible contribution drops under this, a fraction of
|
||||
// what the display can tell apart. there is no other limit: the 1 km filter on the cpu side is about the distance of a light
|
||||
// from the camera, not about how far it reaches, and the shading lights a surface at any distance
|
||||
const float MIN_CONTRIBUTION = 1.0 / 1024.0;
|
||||
// reach of a light whose contribution does not fall off with the distance; its square still fits in a float
|
||||
const float UNLIMITED_RANGE = 1.0e18;
|
||||
// the boxes and the light data are floats: planes of the headlight projection let this much (in meters) of a box through
|
||||
const float PLANE_MARGIN = 0.05;
|
||||
|
||||
// brightest value the light adds to a surface it hits, as the shading computes it
|
||||
float light_peak(light_s light)
|
||||
{
|
||||
float peak = max(light.color.r, max(light.color.g, light.color.b)) * light.intensity;
|
||||
if (light.type == LIGHT_HEADLIGHTS)
|
||||
// weights are the state of the individual lamps and the beam texture, which is not brighter than 1, picks from them
|
||||
return peak * max(light.headlight_weights.r, max(light.headlight_weights.g, light.headlight_weights.b));
|
||||
return peak * (1.0 + light.ambient);
|
||||
}
|
||||
|
||||
// distance from the light at which its contribution falls under MIN_CONTRIBUTION, 0 for a light which has none
|
||||
float light_range(light_s light)
|
||||
{
|
||||
float peak = light_peak(light);
|
||||
if (!(peak > MIN_CONTRIBUTION))
|
||||
return 0.0;
|
||||
// attenuation which the contribution can drop to without being lost: 1 / k
|
||||
float k = peak / MIN_CONTRIBUTION;
|
||||
|
||||
if (light.type == LIGHT_HEADLIGHTS)
|
||||
{
|
||||
// 1 + linear * d + quadratic * d^2 <= k, the positive root written the way which has no cancellation for a small quadratic
|
||||
float a = light.quadratic;
|
||||
float b = light.linear;
|
||||
float c = 1.0 - k;
|
||||
float denominator = b + sqrt(b * b - 4.0 * a * c);
|
||||
return (denominator > 1e-12) ? min(-2.0 * c / denominator, UNLIMITED_RANGE) : UNLIMITED_RANGE;
|
||||
}
|
||||
// point and spot lights fall off with the square of the distance; the shading ends them at 1 / sqrt(quadratic) where it is set
|
||||
float range = sqrt(k);
|
||||
return (light.quadratic > 0.0) ? min(range, inversesqrt(light.quadratic)) : range;
|
||||
}
|
||||
|
||||
bool sphere_touches_box(vec3 center, float radius, vec3 lower, vec3 upper)
|
||||
{
|
||||
vec3 offset = center - clamp(center, lower, upper);
|
||||
return dot(offset, offset) <= radius * radius;
|
||||
}
|
||||
|
||||
// whether any part of the box, given by its center and half extents, is on the positive side of the plane
|
||||
bool box_reaches_plane(vec4 plane, vec3 center, vec3 extent)
|
||||
{
|
||||
return dot(plane.xyz, center) + plane.w + dot(abs(plane.xyz), extent) + PLANE_MARGIN * length(plane.xyz) >= 0.0;
|
||||
}
|
||||
|
||||
// planes bounding the beam of a headlight: the projection takes a view space position to the texture coordinates
|
||||
// of the beam, and the beam is where those fall in 0..1 and the position is in front of the projector. clip z of that
|
||||
// projection is a constant (reverse z, no far plane), so only the four sides and the near plane are real planes
|
||||
bool box_in_headlight_beam(mat4 projection, vec3 center, vec3 extent)
|
||||
{
|
||||
vec4 row_x = vec4(projection[0][0], projection[1][0], projection[2][0], projection[3][0]);
|
||||
vec4 row_y = vec4(projection[0][1], projection[1][1], projection[2][1], projection[3][1]);
|
||||
vec4 row_z = vec4(projection[0][2], projection[1][2], projection[2][2], projection[3][2]);
|
||||
vec4 row_w = vec4(projection[0][3], projection[1][3], projection[2][3], projection[3][3]);
|
||||
|
||||
return box_reaches_plane(row_x, center, extent)
|
||||
&& box_reaches_plane(row_w - row_x, center, extent)
|
||||
&& box_reaches_plane(row_y, center, extent)
|
||||
&& box_reaches_plane(row_w - row_y, center, extent)
|
||||
&& box_reaches_plane(row_z, center, extent)
|
||||
&& box_reaches_plane(row_w - row_z, center, extent);
|
||||
}
|
||||
|
||||
// whether the sphere touches the infinite cone with the apex at the origin, the unit axis and the cosine of the half angle
|
||||
bool sphere_touches_cone(vec3 center, float radius, vec3 axis, float cosine)
|
||||
{
|
||||
float sine = max(sqrt(max(1.0 - cosine * cosine, 0.0)), 1e-4);
|
||||
float along = dot(center, axis);
|
||||
float across = sqrt(max(dot(center, center) - along * along, 0.0));
|
||||
// distance to the boundary ray if the closest point of the cone is on it, and to the apex otherwise
|
||||
float distance_to_cone = (along * cosine + across * sine > 0.0) ? across * cosine - along * sine : length(center);
|
||||
return distance_to_cone <= radius;
|
||||
}
|
||||
|
||||
bool light_reaches_box(light_s light, float range, vec3 lower, vec3 upper)
|
||||
{
|
||||
vec3 center = 0.5 * (lower + upper);
|
||||
vec3 extent = 0.5 * (upper - lower);
|
||||
|
||||
if (light.type == LIGHT_DIR)
|
||||
return true;
|
||||
|
||||
if (!sphere_touches_box(light.pos, range, lower, upper))
|
||||
return false;
|
||||
|
||||
if (light.type == LIGHT_HEADLIGHTS)
|
||||
return box_in_headlight_beam(light.headlight_projection, center, extent);
|
||||
|
||||
if ((light.type == LIGHT_SPOT) && (light.out_cutoff > 0.0))
|
||||
// the cone is tested against the sphere around the box, which lets some of the corners through. a cone wider than
|
||||
// a half of the space is taken as the whole sphere
|
||||
return sphere_touches_cone(center - light.pos, length(extent), normalize(light.dir), light.out_cutoff);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
if (index >= uint(CLUSTER_COUNT))
|
||||
return;
|
||||
|
||||
vec3 lower = cluster_aabbs[index].lower.xyz;
|
||||
vec3 upper = cluster_aabbs[index].upper.xyz;
|
||||
|
||||
// the lights are in the order of their importance, so when a cluster is full the least important are the ones left out
|
||||
uint found[MAX_LIGHTS_PER_CLUSTER];
|
||||
uint found_count = 0U;
|
||||
uint dropped = 0U;
|
||||
|
||||
uint lights_end = min(lights_total, uint(lights.length()));
|
||||
for (uint i = 1U; i < lights_end; ++i)
|
||||
{
|
||||
float range = light_range(lights[i]);
|
||||
if (range <= 0.0 && lights[i].type != LIGHT_DIR)
|
||||
continue;
|
||||
if (!light_reaches_box(lights[i], range, lower, upper))
|
||||
continue;
|
||||
|
||||
if (found_count < uint(MAX_LIGHTS_PER_CLUSTER))
|
||||
found[found_count++] = i;
|
||||
else
|
||||
++dropped;
|
||||
}
|
||||
|
||||
uint offset = 0U;
|
||||
if (found_count > 0U)
|
||||
{
|
||||
offset = atomicAdd(cluster_index_count, found_count);
|
||||
for (uint k = 0U; k < found_count; ++k)
|
||||
cluster_light_indices[offset + k] = found[k];
|
||||
}
|
||||
if (dropped > 0U)
|
||||
atomicAdd(cluster_overflow_count, dropped);
|
||||
|
||||
cluster_ranges[index] = cluster_range_s(offset, found_count);
|
||||
}
|
||||
46
shaders/ogl46/cluster_heatmap.frag
Normal file
46
shaders/ogl46/cluster_heatmap.frag
Normal file
@@ -0,0 +1,46 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// debug view of the light clusters: tints every pixel of the scene with the colour of the number of lights which
|
||||
// were assigned to its cluster (see gl/cluster_grid.h), pixels of clusters without lights stay as they are.
|
||||
// drawn over the resolved image
|
||||
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma texture(depth_tex, 0, R)
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
const float TINT = 0.55;
|
||||
|
||||
// blue (few) through green to red (many)
|
||||
vec3 ramp(float t)
|
||||
{
|
||||
return clamp(vec3(1.5) - abs(4.0 * t - vec3(3.0, 2.0, 1.0)), 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
// reverse z: 0 is the far plane at infinity, i.e. the sky, which has no cluster
|
||||
float depth = texelFetch(depth_tex, ivec2(f_coords * vec2(textureSize(depth_tex, 0))), 0).r;
|
||||
if (depth <= 0.0)
|
||||
discard;
|
||||
|
||||
// depth is znear / w
|
||||
float w = cluster_znear / depth;
|
||||
int slice = clamp(int(floor(log(w) * cluster_slice_scale + cluster_slice_bias)), 0, CLUSTER_GRID_Z - 1);
|
||||
ivec2 tile = clamp(ivec2(floor(f_coords * vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y))), ivec2(0), ivec2(CLUSTER_GRID_X - 1, CLUSTER_GRID_Y - 1));
|
||||
|
||||
uint count = cluster_ranges[tile.x + CLUSTER_GRID_X * (tile.y + CLUSTER_GRID_Y * slice)].count;
|
||||
if (count == 0U)
|
||||
discard;
|
||||
|
||||
// a full cluster (lights beyond its capacity are dropped) is white; the rest goes by the logarithm of the count
|
||||
vec3 color = (count >= uint(MAX_LIGHTS_PER_CLUSTER))
|
||||
? vec3(1.0)
|
||||
: ramp(log2(1.0 + float(count)) / log2(1.0 + float(MAX_LIGHTS_PER_CLUSTER)));
|
||||
|
||||
out_color = vec4(color, TINT);
|
||||
}
|
||||
30
shaders/ogl46/color.frag
Normal file
30
shaders/ogl46/color.frag
Normal file
@@ -0,0 +1,30 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 13) in vec3 f_color;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#include "sky_shoulder.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 col = sky_shoulder(pow(max(f_color.rgb, vec3(0.0)), vec3(2.2)));
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = vec4(apply_fog(col), 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(vec4(apply_fog(col), 1.0f));
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
322
shaders/ogl46/common.glsl
Normal file
322
shaders/ogl46/common.glsl
Normal file
@@ -0,0 +1,322 @@
|
||||
// what the shaders of the opengl 4.6 renderer share: the settings, the constants of the game, the lights, the clusters, the
|
||||
// blocks of the scene and of the model being drawn. the constants below are the values of the game (gl/ubo.h and others):
|
||||
// compiled from the source, the game puts its own values before the source (gl::glsl_common) and they are taken instead;
|
||||
// the SPIR-V binaries (shaders/ogl46bin) are compiled with the values written here, so they have to stay the same as in the
|
||||
// game, which checks them when it starts (and doesn't load the binaries when they differ).
|
||||
//
|
||||
// the locations of the inputs and outputs between the stages, the same for a name in every shader (SPIR-V links the stages by
|
||||
// the locations, not by the names): 0 f_normal, 1 f_normal_raw, 2 f_coord, 3 f_pos, 4 f_clip_pos, 5 f_clip_future_pos,
|
||||
// 6 TangentFragPos, 7 UserData, 8-10 f_tbn, 11 f_model_draw, 12 f_albedo, 13 f_color, 14 f_coords, 15 f_local, 16 f_soft,
|
||||
// 17 f_streak, 18 g_coord, 19 g_coords, 20 g_pos
|
||||
#ifndef COMMON_GLSL
|
||||
#define COMMON_GLSL
|
||||
|
||||
#if !defined(STAGE_VERTEX) && !defined(STAGE_FRAGMENT) && !defined(STAGE_GEOMETRY) && !defined(STAGE_COMPUTE)
|
||||
#error "the stage of the shader isn't defined: #define STAGE_VERTEX 1 (STAGE_FRAGMENT, STAGE_GEOMETRY, STAGE_COMPUTE) before the include of common.glsl"
|
||||
#endif
|
||||
|
||||
#include "settings.glsl"
|
||||
|
||||
#ifndef MASZYNA_CONSTANTS
|
||||
#define MASZYNA_CONSTANTS 1
|
||||
#define MAX_LIGHTS 1024U
|
||||
#define MAX_CASCADES 3U
|
||||
#define MAX_PARAMS 3U
|
||||
#define SHADOW_TEX 8
|
||||
#define ENV_TEX 9
|
||||
#define HEADLIGHT_TEX 10
|
||||
#define ENV_GGX_TEX 11
|
||||
#define SCENE_UBO_BINDING 0
|
||||
#define MODEL_UBO_BINDING 1
|
||||
#define INSTANCE_SSBO_BINDING 8
|
||||
#define INSTANCE_RECORD_SSBO_BINDING 9
|
||||
#define INSTANCE_JOB_SSBO_BINDING 10
|
||||
#define INSTANCE_GROUP_SSBO_BINDING 11
|
||||
#define INSTANCE_LOD_SSBO_BINDING 12
|
||||
#define INSTANCE_COUNTER_SSBO_BINDING 13
|
||||
#define INSTANCE_OUTPUT_SSBO_BINDING 14
|
||||
#define INSTANCE_COMMAND_SSBO_BINDING 15
|
||||
#define MODEL_DRAW_SSBO_BINDING 10
|
||||
#define INSTANCE_CULL_GROUP_SIZE 64
|
||||
#define INSTANCE_OFFSETS_GROUP_SIZE 1024
|
||||
#define INSTANCE_COMMAND_GROUP_SIZE 64
|
||||
#define HIZ_GROUP_SIZE 8
|
||||
#define SHADOW_FIT_GROUP_SIZE 16
|
||||
#define SHADOW_FIT_SSBO_BINDING 9
|
||||
#define PARTICLE_GROUP_SIZE 64
|
||||
#define PARTICLE_SSBO_BINDING 9
|
||||
#define PARTICLE_SOURCE_SSBO_BINDING 10
|
||||
#define PARTICLE_OWNER_SSBO_BINDING 11
|
||||
#define PARTICLE_OUTPUT_SSBO_BINDING 12
|
||||
#define PARTICLE_COMMAND_SSBO_BINDING 13
|
||||
#define INSTANCE_COUNTER_HEADER 8
|
||||
#define INSTANCE_JOB_SLEEPERS 1U
|
||||
#define LIGHT_SSBO_BINDING 0
|
||||
#define CLUSTER_GRID_X 16
|
||||
#define CLUSTER_GRID_Y 9
|
||||
#define CLUSTER_GRID_Z 24
|
||||
#define CLUSTER_COUNT 3456
|
||||
#define MAX_LIGHTS_PER_CLUSTER 64
|
||||
#define CLUSTER_BUILD_GROUP_SIZE 64
|
||||
#define CLUSTER_CULL_GROUP_SIZE 64
|
||||
#define CLUSTER_UBO_BINDING 2
|
||||
#define CLUSTER_AABB_SSBO_BINDING 1
|
||||
#define CLUSTER_RANGE_SSBO_BINDING 2
|
||||
#define CLUSTER_INDEX_SSBO_BINDING 3
|
||||
#define CLUSTER_COUNTER_SSBO_BINDING 4
|
||||
#define ENVMAP_GGX_LEVELS 7
|
||||
#define ENVMAP_GGX_ROUGHNESS_MIN 0.200000
|
||||
#define ENVMAP_GGX_GROUP_SIZE 8
|
||||
#define SKY_SSBO_BINDING 5
|
||||
#define EXPOSURE_SSBO_BINDING 6
|
||||
#define EXPOSURE_HISTOGRAM_BINS 256
|
||||
#define EXPOSURE_HISTOGRAM_GROUP_SIZE 16
|
||||
#define ATMOSPHERE_UBO_BINDING 4
|
||||
#define ATMOSPHERE_RESULT_SSBO_BINDING 7
|
||||
#define ATMOSPHERE_TRANSMITTANCE_WIDTH 256
|
||||
#define ATMOSPHERE_TRANSMITTANCE_HEIGHT 64
|
||||
#define ATMOSPHERE_MULTISCATTERING_SIZE 32
|
||||
#define ATMOSPHERE_SKYVIEW_WIDTH 192
|
||||
#define ATMOSPHERE_SKYVIEW_HEIGHT 108
|
||||
#define ATMOSPHERE_AMBIENT_GROUP_SIZE 256
|
||||
#define ATMOSPHERE_TRANSMITTANCE_UNIT 0
|
||||
#define ATMOSPHERE_MULTISCATTERING_UNIT 1
|
||||
#define ATMOSPHERE_SKYVIEW_UNIT 2
|
||||
#define SKY_VIEW_TEX 12
|
||||
#define LOCAL_SHADOW_TEX 13
|
||||
#define LOCAL_SHADOW_LAMPS_TEX 14
|
||||
#define HEADLIGHT_SHADOW_TEX 15
|
||||
#define MAX_HEADLIGHT_SHADOWS 4U
|
||||
#define FOG_VOLUME_TEX 16
|
||||
#define CLOUD_SHADOW_TEX 17
|
||||
#define CLOUD_TEX 18
|
||||
#define CLOUD_SHAPE_TEX 19
|
||||
#define CLOUD_DETAIL_TEX 20
|
||||
#define CLOUD_COVERAGE_TEX 21
|
||||
#define CLOUD_HISTORY_TEX 22
|
||||
#define SCENE_DEPTH_TEX 23
|
||||
#define WEATHER_UBO_BINDING 5
|
||||
#define WEATHER_SCRATCH_SSBO_BINDING 9
|
||||
#define MAX_CLOUD_LAYERS 4
|
||||
#define FOG_HORIZON_SAMPLES 32
|
||||
#define WEATHER_GROUP_SIZE 8
|
||||
#define FOG_VOLUME_BIAS 10.000000
|
||||
#endif
|
||||
|
||||
const uint LIGHT_SPOT = 0U;
|
||||
const uint LIGHT_POINT = 1U;
|
||||
const uint LIGHT_DIR = 2U;
|
||||
const uint LIGHT_HEADLIGHTS = 3U;
|
||||
|
||||
struct light_s
|
||||
{
|
||||
vec3 pos;
|
||||
uint type;
|
||||
|
||||
vec3 dir;
|
||||
float in_cutoff;
|
||||
|
||||
vec3 color;
|
||||
float out_cutoff;
|
||||
|
||||
float linear;
|
||||
float quadratic;
|
||||
|
||||
float intensity;
|
||||
float ambient;
|
||||
|
||||
mat4 headlight_projection;
|
||||
vec4 headlight_weights;
|
||||
};
|
||||
|
||||
// storage blocks are guaranteed only in fragment and compute shaders (GL 4.6 minimum for the other stages is 0)
|
||||
#if defined(STAGE_FRAGMENT) || defined(STAGE_COMPUTE)
|
||||
layout(std430, binding = LIGHT_SSBO_BINDING) readonly buffer light_ssbo
|
||||
{
|
||||
vec3 ambient;
|
||||
// lights stored in lights[], what the cluster culling goes through
|
||||
uint lights_total;
|
||||
|
||||
vec3 fog_color;
|
||||
// lights the shading loop goes through when the lights are not clustered
|
||||
uint lights_count;
|
||||
// 1 if the shading takes the lights from the list of the cluster of a fragment, see cluster_ubo
|
||||
uint lights_clustered;
|
||||
// scale of the directional part of the sky ambient light, see sky_ssbo
|
||||
float sky_ambient_factor;
|
||||
// 1 if rough reflections come from the prefiltered environment map
|
||||
uint env_ggx;
|
||||
|
||||
light_s lights[];
|
||||
};
|
||||
|
||||
// directional part of the sky ambient light: real spherical harmonics (bands 1 and 2, band 0 is ambient) of the light
|
||||
// a surface facing a world space direction gets, in units of ambient. see gl::sky_data
|
||||
layout(std430, binding = SKY_SSBO_BINDING) readonly buffer sky_ssbo
|
||||
{
|
||||
vec4 sky_ambient_sh[9];
|
||||
};
|
||||
#endif
|
||||
|
||||
// light clusters, see gl/cluster_grid.h
|
||||
struct cluster_aabb_s
|
||||
{
|
||||
vec4 lower;
|
||||
vec4 upper;
|
||||
};
|
||||
|
||||
struct cluster_range_s
|
||||
{
|
||||
uint offset;
|
||||
uint count;
|
||||
};
|
||||
|
||||
#if defined(STAGE_FRAGMENT) || defined(STAGE_COMPUTE)
|
||||
// parameters of the grid for the pass being drawn
|
||||
layout(std140, binding = CLUSTER_UBO_BINDING) uniform cluster_ubo
|
||||
{
|
||||
mat4 cluster_projection;
|
||||
float cluster_znear;
|
||||
float cluster_zfar;
|
||||
float cluster_slice_scale;
|
||||
float cluster_slice_bias;
|
||||
vec2 cluster_viewport_size;
|
||||
uint cluster_valid;
|
||||
};
|
||||
|
||||
// the compute passes fill the cluster buffers, shading only reads them
|
||||
#ifdef STAGE_COMPUTE
|
||||
#define CLUSTER_SSBO_ACCESS
|
||||
layout(std430, binding = CLUSTER_AABB_SSBO_BINDING) buffer cluster_aabb_ssbo
|
||||
{
|
||||
cluster_aabb_s cluster_aabbs[];
|
||||
};
|
||||
|
||||
layout(std430, binding = CLUSTER_COUNTER_SSBO_BINDING) buffer cluster_counter_ssbo
|
||||
{
|
||||
uint cluster_index_count;
|
||||
uint cluster_overflow_count;
|
||||
};
|
||||
#else
|
||||
#define CLUSTER_SSBO_ACCESS readonly
|
||||
#endif
|
||||
|
||||
layout(std430, binding = CLUSTER_RANGE_SSBO_BINDING) CLUSTER_SSBO_ACCESS buffer cluster_range_ssbo
|
||||
{
|
||||
cluster_range_s cluster_ranges[];
|
||||
};
|
||||
|
||||
layout(std430, binding = CLUSTER_INDEX_SSBO_BINDING) CLUSTER_SSBO_ACCESS buffer cluster_index_ssbo
|
||||
{
|
||||
uint cluster_light_indices[];
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MODEL_DRAWS
|
||||
// programs of the multi draws of the instances drawn from the gpu culling: the model block of every draw comes from a
|
||||
// storage buffer, picked by the number of the draw in the multi draw. the fragment stage gets the number from the vertex
|
||||
// stage (which writes f_model_draw), and the names of the uniform block below stand for the fields of the draw
|
||||
struct model_draw_s
|
||||
{
|
||||
mat4 modelview;
|
||||
mat3 modelviewnormal;
|
||||
vec4 param[MAX_PARAMS];
|
||||
|
||||
mat4 future;
|
||||
float opacity;
|
||||
float emission;
|
||||
float fog_density;
|
||||
float alpha_mult;
|
||||
float shadow_tone;
|
||||
};
|
||||
|
||||
layout(std430, binding = MODEL_DRAW_SSBO_BINDING) readonly buffer model_draw_ssbo
|
||||
{
|
||||
model_draw_s model_draws[];
|
||||
};
|
||||
|
||||
#if defined(STAGE_VERTEX)
|
||||
layout(location = 11) flat out uint f_model_draw;
|
||||
#define MODEL_DRAW gl_DrawID
|
||||
#elif defined(STAGE_FRAGMENT)
|
||||
layout(location = 11) flat in uint f_model_draw;
|
||||
#define MODEL_DRAW f_model_draw
|
||||
#else
|
||||
#define MODEL_DRAW 0
|
||||
#endif
|
||||
|
||||
#define modelview model_draws[MODEL_DRAW].modelview
|
||||
#define modelviewnormal model_draws[MODEL_DRAW].modelviewnormal
|
||||
#define param model_draws[MODEL_DRAW].param
|
||||
#define future model_draws[MODEL_DRAW].future
|
||||
#define opacity model_draws[MODEL_DRAW].opacity
|
||||
#define emission model_draws[MODEL_DRAW].emission
|
||||
#define fog_density model_draws[MODEL_DRAW].fog_density
|
||||
#define alpha_mult model_draws[MODEL_DRAW].alpha_mult
|
||||
#define shadow_tone model_draws[MODEL_DRAW].shadow_tone
|
||||
#else
|
||||
layout(std140, binding = MODEL_UBO_BINDING) uniform model_ubo
|
||||
{
|
||||
mat4 modelview;
|
||||
mat3 modelviewnormal;
|
||||
vec4 param[MAX_PARAMS];
|
||||
|
||||
mat4 future;
|
||||
float opacity;
|
||||
float emission;
|
||||
float fog_density;
|
||||
float alpha_mult;
|
||||
float shadow_tone;
|
||||
};
|
||||
#endif
|
||||
|
||||
layout(std140, binding = SCENE_UBO_BINDING) uniform scene_ubo
|
||||
{
|
||||
mat4 projection;
|
||||
mat4 inv_view;
|
||||
mat4 lightview[MAX_CASCADES];
|
||||
vec3 cascade_end;
|
||||
float time;
|
||||
vec4 rain_params;
|
||||
vec4 wiper_pos;
|
||||
vec4 wiper_timer_out;
|
||||
vec4 wiper_timer_return;
|
||||
// shadow maps of the headlights, see gl::scene_ubs
|
||||
mat4 headlight_shadow[MAX_HEADLIGHT_SHADOWS];
|
||||
vec4 headlight_shadow_params[MAX_HEADLIGHT_SHADOWS];
|
||||
// contact shadows, see gl::scene_ubs
|
||||
vec4 contact_shadow_params;
|
||||
vec4 contact_shadow_lamps;
|
||||
// cascades of the sun shadows fitted to the depth of the view, see gl::scene_ubs
|
||||
vec4 shadow_fit;
|
||||
};
|
||||
|
||||
#ifdef STAGE_VERTEX
|
||||
// Per-instance modelview matrices for GPU-instanced draws, element 0 is identity.
|
||||
// Draws which aren't instanced have gl_BaseInstance == 0 and gl_InstanceID == 0,
|
||||
// so effective_modelview() reduces to model_ubo.modelview. For instanced draws
|
||||
// the C++ side uploads camera-space root transforms of the instances and draws
|
||||
// them from base instance onwards, with model_ubo.modelview set to the submodel-local
|
||||
// chain (computed from identity, NOT from the camera/instance), so the product
|
||||
// gives the correct final transform per instance per submodel.
|
||||
layout(std430, binding = INSTANCE_SSBO_BINDING) readonly buffer instance_ssbo
|
||||
{
|
||||
mat4 instance_modelview[];
|
||||
};
|
||||
|
||||
mat4 effective_modelview() {
|
||||
return instance_modelview[gl_BaseInstance + gl_InstanceID] * modelview;
|
||||
}
|
||||
mat3 effective_modelviewnormal() {
|
||||
// instance_modelview can include per-instance scale (uniform or per-axis),
|
||||
// so its upper-3x3 is NOT its own inverse-transpose in the non-uniform
|
||||
// case. Compute the correct normal matrix via transpose(inverse(...)).
|
||||
// For pure rotation+translation this still produces the rotation matrix
|
||||
// (one extra mat3 inverse per vertex — modern GPUs handle it cheaply).
|
||||
mat3 instance_mv3 = mat3(instance_modelview[gl_BaseInstance + gl_InstanceID]);
|
||||
mat3 instance_normal = transpose(inverse(instance_mv3));
|
||||
return instance_normal * modelviewnormal;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
108
shaders/ogl46/conversion.glsl
Normal file
108
shaders/ogl46/conversion.glsl
Normal file
@@ -0,0 +1,108 @@
|
||||
vec3 rgb2yuv(vec3 val)
|
||||
{
|
||||
return
|
||||
clamp(
|
||||
vec3(
|
||||
(0.257 * val.r) + (0.504 * val.g) + (0.098 * val.b) + 0.0625,
|
||||
-(0.148 * val.r) - (0.291 * val.g) + (0.439 * val.b) + 0.5,
|
||||
(0.439 * val.r) - (0.368 * val.g) - (0.071 * val.b) + 0.5 ),
|
||||
0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 yuv2rgb(vec3 val)
|
||||
{
|
||||
val -= vec3(0.0625, 0.5, 0.5);
|
||||
return
|
||||
clamp(
|
||||
vec3(
|
||||
(1.164 * val.r) + (1.596 * val.b),
|
||||
(1.164 * val.r) - (0.391 * val.g) - (0.813 * val.b),
|
||||
(1.164 * val.r) + (2.018 * val.g) ),
|
||||
0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 rgb2hsl( vec3 col )
|
||||
{
|
||||
float red = col.r;
|
||||
float green = col.g;
|
||||
float blue = col.b;
|
||||
|
||||
float minc = min( col.r, min( col.g, col.b ));
|
||||
float maxc = max( col.r, max( col.g, col.b ));
|
||||
float delta = maxc - minc;
|
||||
|
||||
float lum = (minc + maxc) * 0.5;
|
||||
float sat = 0.0;
|
||||
float hue = 0.0;
|
||||
|
||||
if (lum > 0.0 && lum < 1.0) {
|
||||
float mul = (lum < 0.5) ? (lum) : (1.0-lum);
|
||||
sat = delta / (mul * 2.0);
|
||||
}
|
||||
|
||||
vec3 masks = vec3(
|
||||
(maxc == red && maxc != green) ? 1.0 : 0.0,
|
||||
(maxc == green && maxc != blue) ? 1.0 : 0.0,
|
||||
(maxc == blue && maxc != red) ? 1.0 : 0.0
|
||||
);
|
||||
|
||||
vec3 adds = vec3(
|
||||
((green - blue ) / delta),
|
||||
2.0 + ((blue - red ) / delta),
|
||||
4.0 + ((red - green) / delta)
|
||||
);
|
||||
|
||||
float deltaGtz = (delta > 0.0) ? 1.0 : 0.0;
|
||||
|
||||
hue += dot( adds, masks );
|
||||
hue *= deltaGtz;
|
||||
hue /= 6.0;
|
||||
|
||||
if (hue < 0.0)
|
||||
hue += 1.0;
|
||||
|
||||
return vec3( hue, sat, lum );
|
||||
}
|
||||
|
||||
vec3 hsl2rgb( vec3 col )
|
||||
{
|
||||
const float onethird = 1.0 / 3.0;
|
||||
const float twothird = 2.0 / 3.0;
|
||||
const float rcpsixth = 6.0;
|
||||
|
||||
float hue = col.x;
|
||||
float sat = col.y;
|
||||
float lum = col.z;
|
||||
|
||||
vec3 xt = vec3(
|
||||
rcpsixth * (hue - twothird),
|
||||
0.0,
|
||||
rcpsixth * (1.0 - hue)
|
||||
);
|
||||
|
||||
if (hue < twothird) {
|
||||
xt.r = 0.0;
|
||||
xt.g = rcpsixth * (twothird - hue);
|
||||
xt.b = rcpsixth * (hue - onethird);
|
||||
}
|
||||
|
||||
if (hue < onethird) {
|
||||
xt.r = rcpsixth * (onethird - hue);
|
||||
xt.g = rcpsixth * hue;
|
||||
xt.b = 0.0;
|
||||
}
|
||||
|
||||
xt = min( xt, 1.0 );
|
||||
|
||||
float sat2 = 2.0 * sat;
|
||||
float satinv = 1.0 - sat;
|
||||
float luminv = 1.0 - lum;
|
||||
float lum2m1 = (2.0 * lum) - 1.0;
|
||||
vec3 ct = (sat2 * xt) + satinv;
|
||||
|
||||
vec3 rgb;
|
||||
if (lum >= 0.5) { rgb = (luminv * ct) + lum2m1; }
|
||||
else { rgb = lum * ct; }
|
||||
|
||||
return rgb;
|
||||
}
|
||||
13
shaders/ogl46/copy.frag
Normal file
13
shaders/ogl46/copy.frag
Normal file
@@ -0,0 +1,13 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB)
|
||||
layout(binding = 0) uniform sampler2D tex1;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = texture(tex1, f_coords);
|
||||
}
|
||||
9
shaders/ogl46/depthprepass.frag
Normal file
9
shaders/ogl46/depthprepass.frag
Normal file
@@ -0,0 +1,9 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// depth prepass of materials which never discard a fragment (no alpha test)
|
||||
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
}
|
||||
25
shaders/ogl46/depthprepass.vert
Normal file
25
shaders/ogl46/depthprepass.vert
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
// depth prepass of the instances drawn from the gpu culling (opengl46_renderer::Render_Instances_Indirect()).
|
||||
// the position is worked out the way vertex.vert of the materials does it, and declared invariant in both, so the colour
|
||||
// pass which follows gets the same depth for the same surface
|
||||
|
||||
layout(location = 0) in vec3 v_vert;
|
||||
layout(location = 2) in vec2 v_coord;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
invariant gl_Position;
|
||||
|
||||
layout(location = 2) out vec2 f_coord;
|
||||
|
||||
void main()
|
||||
{
|
||||
mat4 mv = effective_modelview();
|
||||
vec4 position = mv * vec4(v_vert, 1.0);
|
||||
gl_Position = projection * position;
|
||||
f_coord = v_coord;
|
||||
#ifdef MODEL_DRAWS
|
||||
f_model_draw = uint(gl_DrawID);
|
||||
#endif
|
||||
}
|
||||
18
shaders/ogl46/depthprepass_alpha.frag
Normal file
18
shaders/ogl46/depthprepass_alpha.frag
Normal file
@@ -0,0 +1,18 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// depth prepass of materials whose colour shader tests the alpha of the diffuse texture against the opacity of the
|
||||
// material (mat_default.frag and the like). the test here is stricter by one step of 8-bit alpha: a fragment which writes
|
||||
// depth here is never discarded by the colour pass, which would leave a hole with nothing drawn behind it
|
||||
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (texture(diffuse, f_coord).a < opacity + 1.0 / 255.0)
|
||||
discard;
|
||||
}
|
||||
26
shaders/ogl46/dither.glsl
Normal file
26
shaders/ogl46/dither.glsl
Normal file
@@ -0,0 +1,26 @@
|
||||
// noise of half a step of the 8-bit output, added to the colour about to be written to the window: smooth dark gradients (the
|
||||
// fog at night) show no bands, which otherwise crawl over the image with the smallest change of the light. interleaved gradient
|
||||
// noise (Jimenez 2014) of the pixel, the same in every frame. the colour is the one written to the window: encoded already with
|
||||
// OUTPUT_GAMMA, linear otherwise (the sRGB framebuffer encodes it, so the step is taken in the encoded values)
|
||||
#ifndef DITHER_GLSL
|
||||
#define DITHER_GLSL
|
||||
|
||||
#include "settings.glsl"
|
||||
|
||||
vec3 dither_output(vec3 color)
|
||||
{
|
||||
float noise = (fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715)))) - 0.5) / 255.0;
|
||||
if (OUTPUT_GAMMA)
|
||||
{
|
||||
return max(color + noise, vec3(0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3 linear = max(color, vec3(0.0));
|
||||
vec3 encoded = mix(linear * 12.92, 1.055 * pow(linear, vec3(1.0 / 2.4)) - 0.055, step(vec3(0.0031308), linear));
|
||||
encoded = max(encoded + noise, vec3(0.0));
|
||||
return mix(encoded / 12.92, pow((encoded + 0.055) / 1.055, vec3(2.4)), step(vec3(0.04045), encoded));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
102
shaders/ogl46/envmap_prefilter.comp
Normal file
102
shaders/ogl46/envmap_prefilter.comp
Normal file
@@ -0,0 +1,102 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// prefilters the environment map with the GGX lobe for rough reflections (split sum approximation, Karis 2013), one level
|
||||
// of the prefiltered map per dispatch. filtered importance sampling (Krivanek, Colbert 2008): each sample reads the mipmap
|
||||
// of the source whose texels are about as large as the solid angle the sample stands for, so a few dozen samples give a
|
||||
// smooth result. run when an update of the environment map is complete
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = ENVMAP_GGX_GROUP_SIZE, local_size_y = ENVMAP_GGX_GROUP_SIZE) in;
|
||||
|
||||
layout(binding = 0, rgba16f) writeonly uniform imageCube prefilter_target;
|
||||
// the plain map with its box filtered mipmaps, on the unit gl::program binds envmap to
|
||||
layout(binding = ENV_TEX) uniform samplerCube envmap;
|
||||
|
||||
// perceptual roughness of the level being written
|
||||
layout(location = 0) uniform float roughness;
|
||||
// size of the level being written, and of the first level of the source
|
||||
layout(location = 1) uniform int target_size;
|
||||
layout(location = 2) uniform float source_size;
|
||||
layout(location = 3) uniform int sample_count;
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
// direction through the centre of a texel of a cube map face, in the order and orientation of GL_TEXTURE_CUBE_MAP_POSITIVE_X..
|
||||
vec3 cube_direction(ivec3 texel, int size)
|
||||
{
|
||||
vec2 uv = (vec2(texel.xy) + 0.5) / float(size) * 2.0 - 1.0;
|
||||
switch (texel.z)
|
||||
{
|
||||
case 0: return vec3(1.0, -uv.y, -uv.x);
|
||||
case 1: return vec3(-1.0, -uv.y, uv.x);
|
||||
case 2: return vec3(uv.x, 1.0, uv.y);
|
||||
case 3: return vec3(uv.x, -1.0, -uv.y);
|
||||
case 4: return vec3(uv.x, -uv.y, 1.0);
|
||||
default: return vec3(-uv.x, -uv.y, -1.0);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 hammersley(uint i, uint n)
|
||||
{
|
||||
uint bits = bitfieldReverse(i);
|
||||
return vec2(float(i) / float(n), float(bits) * 2.3283064365386963e-10);
|
||||
}
|
||||
|
||||
// half vector around N for the GGX distribution of linear roughness a
|
||||
vec3 importance_sample_ggx(vec2 xi, float a, vec3 N)
|
||||
{
|
||||
float phi = 2.0 * PI * xi.x;
|
||||
float costheta = sqrt((1.0 - xi.y) / (1.0 + (a * a - 1.0) * xi.y));
|
||||
float sintheta = sqrt(max(1.0 - costheta * costheta, 0.0));
|
||||
vec3 H = vec3(sintheta * cos(phi), sintheta * sin(phi), costheta);
|
||||
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
vec3 tangentx = normalize(cross(up, N));
|
||||
vec3 tangenty = cross(N, tangentx);
|
||||
return tangentx * H.x + tangenty * H.y + N * H.z;
|
||||
}
|
||||
|
||||
float distribution_ggx(float NdotH, float a)
|
||||
{
|
||||
float a2 = a * a;
|
||||
float d = NdotH * NdotH * (a2 - 1.0) + 1.0;
|
||||
return a2 / (PI * d * d);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec3 texel = ivec3(gl_GlobalInvocationID);
|
||||
if (texel.x >= target_size || texel.y >= target_size)
|
||||
return;
|
||||
|
||||
vec3 N = normalize(cube_direction(texel, target_size));
|
||||
// the view is taken along the normal, which is what the split sum approximation assumes
|
||||
vec3 V = N;
|
||||
float a = max(roughness * roughness, 1e-3);
|
||||
float texelsolidangle = 4.0 * PI / (6.0 * source_size * source_size);
|
||||
|
||||
vec3 sum = vec3(0.0);
|
||||
float weight = 0.0;
|
||||
uint count = uint(sample_count);
|
||||
for (uint i = 0U; i < count; ++i)
|
||||
{
|
||||
vec3 H = importance_sample_ggx(hammersley(i, count), a, N);
|
||||
float VdotH = dot(V, H);
|
||||
vec3 L = 2.0 * VdotH * H - V;
|
||||
float NdotL = dot(N, L);
|
||||
if (NdotL <= 0.0)
|
||||
continue;
|
||||
float NdotH = max(dot(N, H), 0.0);
|
||||
// pdf of L: D * NdotH / (4 * VdotH)
|
||||
float pdf = distribution_ggx(NdotH, a) * NdotH / max(4.0 * VdotH, 1e-4);
|
||||
float samplesolidangle = 1.0 / (float(count) * max(pdf, 1e-6));
|
||||
float level = max(0.5 * log2(samplesolidangle / texelsolidangle) + 1.0, 0.0);
|
||||
vec3 color = textureLod(envmap, L, level).rgb;
|
||||
if (any(isnan(color)) || any(isinf(color)))
|
||||
color = vec3(0.0);
|
||||
sum += max(color, vec3(0.0)) * NdotL;
|
||||
weight += NdotL;
|
||||
}
|
||||
|
||||
imageStore(prefilter_target, texel, vec4(sum / max(weight, 1e-4), 1.0));
|
||||
}
|
||||
73
shaders/ogl46/envmapping.glsl
Normal file
73
shaders/ogl46/envmapping.glsl
Normal file
@@ -0,0 +1,73 @@
|
||||
layout(binding = ENV_TEX) uniform samplerCube envmap;
|
||||
// the same map prefiltered with the GGX lobe (envmap_prefilter.comp), used when env_ggx is 1
|
||||
layout(binding = ENV_GGX_TEX) uniform samplerCube envmap_ggx;
|
||||
|
||||
vec3 envmap_color( vec3 normal )
|
||||
{
|
||||
vec3 envcolor;
|
||||
if (ENVMAP_ENABLED)
|
||||
{
|
||||
vec3 refvec = reflect(f_pos.xyz, normal); // view space
|
||||
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space
|
||||
envcolor = texture(envmap, refvec).rgb;
|
||||
// Sanitize. The env cubemap can briefly contain NaN/Inf after a
|
||||
// reflection-pass regeneration (m_empty_cubemap was bound during the
|
||||
// face render and on NVIDIA can sample as undefined). Without this
|
||||
// guard, a single bad texel propagates through env_reflection in
|
||||
// apply_lights() and produces one-frame black flashes on glossy
|
||||
// (esp. specgloss) surfaces.
|
||||
if (any(isnan(envcolor)) || any(isinf(envcolor))) envcolor = vec3(0.0);
|
||||
envcolor = max(envcolor, vec3(0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
envcolor = vec3(0.5);
|
||||
}
|
||||
return envcolor;
|
||||
}
|
||||
|
||||
// Roughness-aware env map lookup — uses mip levels for blurry reflections.
|
||||
// lod 0.0 = mirror sharp, lod ~8.0 = fully diffuse blur.
|
||||
vec3 envmap_color_lod(vec3 fragnormal, float lod)
|
||||
{
|
||||
if (ENVMAP_ENABLED)
|
||||
{
|
||||
vec3 refvec = reflect(f_pos.xyz, fragnormal); // view space — matches envmap_color exactly
|
||||
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space — was missing
|
||||
vec3 envcolor = textureLod(envmap, refvec, lod).rgb;
|
||||
// See envmap_color() above — same NaN/Inf sanitize, also needed here
|
||||
// because mipmap generation propagates a single NaN texel across the
|
||||
// whole mip chain.
|
||||
if (any(isnan(envcolor)) || any(isinf(envcolor))) envcolor = vec3(0.0);
|
||||
return max(envcolor, vec3(0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
return vec3(0.5); // was vec3(0.0), match the non-LOD fallback
|
||||
}
|
||||
}
|
||||
|
||||
// reflection of the surroundings in a surface of the given perceptual roughness (0 mirror, 1 matte)
|
||||
vec3 envmap_color_rough(vec3 fragnormal, float roughness)
|
||||
{
|
||||
// the plain map, its mipmaps (box filtered) standing for the roughness
|
||||
const float MAX_REFLECTION_LOD = 8.0;
|
||||
vec3 boxcolor = envmap_color_lod(fragnormal, roughness * MAX_REFLECTION_LOD);
|
||||
if (ENVMAP_ENABLED)
|
||||
{
|
||||
if (env_ggx == 0U || roughness <= ENVMAP_GGX_ROUGHNESS_MIN)
|
||||
return boxcolor;
|
||||
vec3 refvec = reflect(f_pos.xyz, fragnormal);
|
||||
refvec = vec3(inv_view * vec4(refvec, 0.0));
|
||||
float level = (roughness - ENVMAP_GGX_ROUGHNESS_MIN) / (1.0 - ENVMAP_GGX_ROUGHNESS_MIN) * float(ENVMAP_GGX_LEVELS - 1U);
|
||||
vec3 ggxcolor = textureLod(envmap_ggx, refvec, level).rgb;
|
||||
if (any(isnan(ggxcolor)) || any(isinf(ggxcolor))) ggxcolor = vec3(0.0);
|
||||
ggxcolor = max(ggxcolor, vec3(0.0));
|
||||
// the prefiltered map takes over over the first tenth of the roughness above its lowest level
|
||||
return mix(boxcolor, ggxcolor, clamp((roughness - ENVMAP_GGX_ROUGHNESS_MIN) * 10.0, 0.0, 1.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
return boxcolor;
|
||||
}
|
||||
}
|
||||
81
shaders/ogl46/exposure_average.comp
Normal file
81
shaders/ogl46/exposure_average.comp
Normal file
@@ -0,0 +1,81 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// exposure from the luminance histogram (exposure_histogram.comp): the average log2 luminance of the pixels between the
|
||||
// low and high percentile (the darkest and brightest parts of the image do not move the exposure), adapted over time to,
|
||||
// faster towards the brighter. the exposure brings the adapted luminance to the key, within the limits. one work group,
|
||||
// one invocation per bin; the histogram is cleared for the next frame
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = EXPOSURE_HISTOGRAM_BINS) in;
|
||||
|
||||
layout(std430, binding = EXPOSURE_SSBO_BINDING) buffer exposure_ssbo
|
||||
{
|
||||
uint histogram[EXPOSURE_HISTOGRAM_BINS];
|
||||
float adapted_luminance;
|
||||
float exposure;
|
||||
float average_luminance;
|
||||
uint valid;
|
||||
};
|
||||
|
||||
// x: log2 of the lowest luminance binned, y: log2 luminance per bin
|
||||
layout(location = 0) uniform vec2 log_range;
|
||||
// x: time since the last frame (s), y: rate of adaptation to a darker image, z: to a brighter one (1/s), w: key
|
||||
layout(location = 1) uniform vec4 adaptation;
|
||||
// lowest and highest exposure (multipliers)
|
||||
layout(location = 2) uniform vec2 exposure_range;
|
||||
|
||||
const float LOW_PERCENTILE = 0.4;
|
||||
const float HIGH_PERCENTILE = 0.95;
|
||||
|
||||
shared float shared_counts[EXPOSURE_HISTOGRAM_BINS];
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_LocalInvocationIndex;
|
||||
shared_counts[index] = float(histogram[index]);
|
||||
histogram[index] = 0U;
|
||||
barrier();
|
||||
|
||||
if (index != 0U)
|
||||
return;
|
||||
|
||||
float total = 0.0;
|
||||
for (uint bin = 1U; bin < EXPOSURE_HISTOGRAM_BINS; ++bin)
|
||||
total += shared_counts[bin];
|
||||
// an all black image (or none) leaves the exposure as it was
|
||||
if (total < 1.0)
|
||||
return;
|
||||
|
||||
float low = total * LOW_PERCENTILE;
|
||||
float high = total * HIGH_PERCENTILE;
|
||||
float counted = 0.0;
|
||||
float logsum = 0.0;
|
||||
float weight = 0.0;
|
||||
for (uint bin = 1U; bin < EXPOSURE_HISTOGRAM_BINS; ++bin)
|
||||
{
|
||||
float count = shared_counts[bin];
|
||||
// the part of the bin between the percentiles
|
||||
float inside = max(min(counted + count, high) - max(counted, low), 0.0);
|
||||
counted += count;
|
||||
float binluminance = log_range.x + (float(bin - 1U) + 0.5) * log_range.y;
|
||||
logsum += inside * binluminance;
|
||||
weight += inside;
|
||||
}
|
||||
float averagelog = (weight > 0.0) ? logsum / weight : log_range.x;
|
||||
average_luminance = exp2(averagelog);
|
||||
|
||||
if (valid == 0U || !(adapted_luminance > 0.0))
|
||||
{
|
||||
adapted_luminance = average_luminance;
|
||||
valid = 1U;
|
||||
}
|
||||
else
|
||||
{
|
||||
float adaptedlog = log2(adapted_luminance);
|
||||
float rate = (averagelog < adaptedlog) ? adaptation.y : adaptation.z;
|
||||
adaptedlog = mix(adaptedlog, averagelog, 1.0 - exp(-adaptation.x * rate));
|
||||
adapted_luminance = exp2(adaptedlog);
|
||||
}
|
||||
exposure = clamp(adaptation.w / adapted_luminance, exposure_range.x, exposure_range.y);
|
||||
}
|
||||
51
shaders/ogl46/exposure_histogram.comp
Normal file
51
shaders/ogl46/exposure_histogram.comp
Normal file
@@ -0,0 +1,51 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// luminance histogram of the image for the automatic exposure: every second pixel in both directions, binned by the log2
|
||||
// of its luminance. bin 0 takes the black pixels, which the average leaves out. exposure_average.comp reads and clears it
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = EXPOSURE_HISTOGRAM_GROUP_SIZE, local_size_y = EXPOSURE_HISTOGRAM_GROUP_SIZE) in;
|
||||
|
||||
layout(binding = 0) uniform sampler2D hdr_image;
|
||||
|
||||
layout(std430, binding = EXPOSURE_SSBO_BINDING) buffer exposure_ssbo
|
||||
{
|
||||
uint histogram[EXPOSURE_HISTOGRAM_BINS];
|
||||
float adapted_luminance;
|
||||
float exposure;
|
||||
float average_luminance;
|
||||
uint valid;
|
||||
};
|
||||
|
||||
// x: log2 of the lowest luminance binned, y: number of bins per unit of log2 luminance
|
||||
layout(location = 0) uniform vec2 log_range;
|
||||
|
||||
shared uint shared_histogram[EXPOSURE_HISTOGRAM_BINS];
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_LocalInvocationIndex;
|
||||
shared_histogram[index] = 0U;
|
||||
barrier();
|
||||
|
||||
ivec2 pixel = ivec2(gl_GlobalInvocationID.xy) * 2;
|
||||
ivec2 size = textureSize(hdr_image, 0);
|
||||
if (pixel.x < size.x && pixel.y < size.y)
|
||||
{
|
||||
vec3 color = texelFetch(hdr_image, pixel, 0).rgb;
|
||||
float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
|
||||
uint bin = 0U;
|
||||
// also false for a NaN
|
||||
if (luminance > 1e-6)
|
||||
{
|
||||
float position = clamp((log2(luminance) - log_range.x) * log_range.y, 0.0, float(EXPOSURE_HISTOGRAM_BINS - 2U));
|
||||
bin = 1U + uint(position);
|
||||
}
|
||||
atomicAdd(shared_histogram[bin], 1U);
|
||||
}
|
||||
barrier();
|
||||
|
||||
if (shared_histogram[index] != 0U)
|
||||
atomicAdd(histogram[index], shared_histogram[index]);
|
||||
}
|
||||
44
shaders/ogl46/fog_horizon.comp
Normal file
44
shaders/ogl46/fog_horizon.comp
Normal file
@@ -0,0 +1,44 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// colours of the fog towards the horizon by the angle from the azimuth of the sun, from the sky-view table of the atmosphere,
|
||||
// written into the weather buffer (weather_common.glsl: fog_horizon). the far fog then has the colour of the sky behind it.
|
||||
// the atmosphere blends into the old sky as opengl46_sky does it, by its share, white balance and brightness
|
||||
|
||||
#include "common.glsl"
|
||||
#include "atmosphere_common.glsl"
|
||||
|
||||
layout(local_size_x = FOG_HORIZON_SAMPLES) in;
|
||||
|
||||
layout(binding = SKY_VIEW_TEX) uniform sampler2D sky_view_lut;
|
||||
|
||||
layout(std430, binding = WEATHER_SCRATCH_SSBO_BINDING) buffer weather_scratch
|
||||
{
|
||||
vec4 weather_words[];
|
||||
};
|
||||
|
||||
// first vec4 of fog_horizon in the weather buffer
|
||||
layout(location = 0) uniform int offset;
|
||||
// x: share of the atmosphere, y: its brightness, z: desaturation by the overcast, w: brightest the colour gets
|
||||
layout(location = 1) uniform vec4 params;
|
||||
layout(location = 2) uniform vec3 white_balance;
|
||||
// the colour of the old fog, which the atmosphere replaces by its share
|
||||
layout(location = 3) uniform vec3 old_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_LocalInvocationID.x;
|
||||
float angle = float(index) / float(FOG_HORIZON_SAMPLES - 1) * ATMO_PI;
|
||||
vec3 sun = normalize(atmo_sun_direction.xyz);
|
||||
vec2 flat_sun = (dot(sun.xz, sun.xz) > 1e-8) ? normalize(sun.xz) : vec2(1.0, 0.0);
|
||||
vec2 side = vec2(-flat_sun.y, flat_sun.x);
|
||||
vec2 flat_view = flat_sun * cos(angle) + side * sin(angle);
|
||||
// a little above the horizon, where the far fog meets the sky
|
||||
vec3 direction = normalize(vec3(flat_view.x, 0.035, flat_view.y));
|
||||
vec3 color = textureLod(sky_view_lut, atmo_skyview_uv_world(direction, sun), 0.0).rgb * white_balance * params.y;
|
||||
color = min(color, vec3(params.w));
|
||||
float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
|
||||
color = mix(color, vec3(luminance), params.z);
|
||||
if (any(isnan(color)))
|
||||
color = old_color;
|
||||
weather_words[offset + int(index)] = vec4(mix(old_color, color, params.x), 1.0);
|
||||
}
|
||||
318
shaders/ogl46/fog_inject.comp
Normal file
318
shaders/ogl46/fog_inject.comp
Normal file
@@ -0,0 +1,318 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// the fog volume of the main view, first pass: in every cell of a grid laid over the view (slices spaced exponentially in the
|
||||
// depth), how dense the fog is and how much light it scatters towards the camera. the density: the haze, the patches of the
|
||||
// ground fog, the low clouds; the light: the colour the fog has towards the horizon (weather_common.glsl), less what the
|
||||
// shadows of the sun and of the clouds hold back, and the lamps and headlights of the light clusters. blended with the
|
||||
// previous frame, found where the cell was then; the cells are sampled at another point every frame, each cell going through
|
||||
// the points from a place of its own, so the volume doesn't flicker as a whole, and the blending smooths what a cell holds over
|
||||
// its whole size: a beam doesn't swim over the grid of the cells as the camera moves and turns. what the cell takes the lights
|
||||
// from doesn't depend on the point (the clusters of the middle of the cell)
|
||||
// rgb: light scattered towards the camera per metre, a: extinction per metre
|
||||
|
||||
#include "common.glsl"
|
||||
#include "clouds_common.glsl"
|
||||
|
||||
layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in;
|
||||
|
||||
layout(rgba16f, binding = 0) uniform writeonly image3D fog_scattering;
|
||||
layout(binding = CLOUD_HISTORY_TEX) uniform sampler3D fog_history;
|
||||
|
||||
layout(binding = SHADOW_TEX) uniform sampler2DArrayShadow shadowmap;
|
||||
layout(binding = HEADLIGHT_TEX) uniform sampler2D headlightmap;
|
||||
layout(binding = HEADLIGHT_SHADOW_TEX) uniform sampler2DArrayShadow headlight_shadowmap;
|
||||
|
||||
layout(location = 0) uniform ivec3 grid;
|
||||
layout(location = 1) uniform mat4 inverse_projection;
|
||||
layout(location = 2) uniform mat4 view_to_world;
|
||||
// x: near, y: far, z: slices / log((far + FOG_VOLUME_BIAS) / (near + FOG_VOLUME_BIAS)), w: where in its slice the cell is
|
||||
// sampled this frame (0-1)
|
||||
layout(location = 3) uniform vec4 slicing;
|
||||
// world axes to the clip space of the previous frame, the camera of the previous frame at the origin
|
||||
layout(location = 4) uniform mat4 previous_projection_view;
|
||||
layout(location = 5) uniform vec3 previous_camera;
|
||||
// x: share of this frame (1 without history), y: brightness of the lamps in the fog (0: none), z: 1 when the sun casts shadows
|
||||
// into the fog, w: 1 when the headlights cast shadows into it
|
||||
layout(location = 6) uniform vec4 options;
|
||||
// the light of the cab lamp, which lights no fog (the air of the cab is clear), or -1
|
||||
layout(location = 7) uniform int cablamp;
|
||||
// x: near, y: slices / log((far + FOG_VOLUME_BIAS) / (near + FOG_VOLUME_BIAS)) of the previous frame
|
||||
layout(location = 8) uniform vec2 previous_slicing;
|
||||
// where in its cell the cell is sampled this frame across the view (0-1), as slicing.w is along the depth
|
||||
layout(location = 9) uniform vec2 jitter;
|
||||
|
||||
// ray of the view space through a point of the view (0-1), one metre of the depth long
|
||||
vec3 view_ray(vec2 coords)
|
||||
{
|
||||
vec4 near = inverse_projection * vec4(coords * 2.0 - 1.0, 1.0, 1.0);
|
||||
vec3 ray = near.xyz / near.w;
|
||||
return ray / -ray.z;
|
||||
}
|
||||
|
||||
// view depth at a place along the slices: slice number and the part of the slice
|
||||
float slice_depth(float slice)
|
||||
{
|
||||
return (slicing.x + FOG_VOLUME_BIAS) * exp(slice / slicing.z) - FOG_VOLUME_BIAS;
|
||||
}
|
||||
|
||||
// where in the cell it is sampled this frame, 0-1 on each axis: the point of the sequence of the frames moved by a value of
|
||||
// the cell's own (pcg3d hash), the middle without the blending
|
||||
vec3 cell_offset(ivec3 cell)
|
||||
{
|
||||
if (options.x >= 1.0)
|
||||
return vec3(0.5);
|
||||
uvec3 v = uvec3(cell) * 1664525U + 1013904223U;
|
||||
v.x += v.y * v.z;
|
||||
v.y += v.z * v.x;
|
||||
v.z += v.x * v.y;
|
||||
v ^= v >> 16U;
|
||||
v.x += v.y * v.z;
|
||||
v.y += v.z * v.x;
|
||||
v.z += v.x * v.y;
|
||||
return fract(vec3(jitter, slicing.w) + vec3(v >> 8U) * (1.0 / 16777216.0));
|
||||
}
|
||||
|
||||
float henyey_greenstein(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
|
||||
float 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;
|
||||
}
|
||||
}
|
||||
|
||||
// light of a lamp or of the beam of headlights at a point of the view space, before the phase
|
||||
vec3 lamp_light(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 ((options.w > 0.5) && (light.headlight_weights.w > 0.5) && (intensity > 0.0))
|
||||
{
|
||||
// the shadow map of the beam, as in light_common.glsl without the offset along a normal
|
||||
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);
|
||||
}
|
||||
|
||||
// light of a lamp or of the beam of headlights scattered towards the camera, the average per metre along a segment of the ray
|
||||
// of a cell (start, unit direction, length; view space). point samples of a lamp next to the ray change many times over with
|
||||
// where in the cell they fall (the light falls off with the square of the distance), so the falloff of a lamp is integrated
|
||||
// along the segment and only the rest of its light (the cone, the edge of its reach, the phase) is sampled, at two points the
|
||||
// integral spreads by itself (equi-angular sampling, Kulla & Fajardo 2012). next to the lamp the light is spread over the size
|
||||
// of a cell: the distance of the lamp from the ray grows by the width of the cell there, and the cone of a spot light starts
|
||||
// that far behind the lamp. the point sampled moves within the cell from frame to frame, and the ray of the cell would pass at
|
||||
// almost no distance from the lamp in one frame and a cell away in the next, above the tip of the cone and below it. Part:
|
||||
// where between the two halves of the segment the points are (0-1)
|
||||
vec3 lamp_segment(light_s lamp, light_s cone, vec3 start, vec3 direction, float span, float width, float part);
|
||||
vec3 lamp_scattering(light_s lamp, vec3 start, vec3 direction, float span, float cellwidth, float part)
|
||||
{
|
||||
vec3 sum = vec3(0.0);
|
||||
if (lamp.type == LIGHT_HEADLIGHTS)
|
||||
{
|
||||
// the beam falls off slowly, two points spread evenly along the segment
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
vec3 position = start + direction * (span * (float(i) + part) * 0.5);
|
||||
sum += lamp_light(lamp, position) * henyey_greenstein(dot(normalize(position - lamp.pos), -direction), 0.4);
|
||||
}
|
||||
return sum * 0.5;
|
||||
}
|
||||
float closest = dot(lamp.pos - start, direction);
|
||||
vec3 nearest = start + direction * closest;
|
||||
float width = max(cellwidth * max(-nearest.z, 0.0), 1e-3);
|
||||
light_s cone = lamp;
|
||||
if (lamp.type == LIGHT_SPOT)
|
||||
cone.pos -= normalize(lamp.dir) * width;
|
||||
if (dot(lamp.pos - nearest, lamp.pos - nearest) < 9.0 * width * width)
|
||||
{
|
||||
// a lamp within a few cells of the ray: the light of the cell is taken along four rays spread over the cell, so it
|
||||
// doesn't depend on which side of the lamp the ray of this frame passes, and the points along the rays are staggered,
|
||||
// eight along the cell, where the cone changes the most (its tip)
|
||||
vec3 across = normalize(cross(direction, vec3(0.0, 1.0, 0.0) + vec3(1e-4, 0.0, 0.0)));
|
||||
vec3 up = cross(across, direction);
|
||||
for (int k = 0; k < 4; k++)
|
||||
{
|
||||
vec2 shift = (vec2(float(k & 1), float(k >> 1)) - 0.5) * 0.5 * width;
|
||||
sum += lamp_segment(lamp, cone, start + across * shift.x + up * shift.y, direction, span, width, fract(part + 0.25 * float(k)));
|
||||
}
|
||||
return sum * 0.25;
|
||||
}
|
||||
return lamp_segment(lamp, cone, start, direction, span, width, part);
|
||||
}
|
||||
|
||||
// lamp_scattering() along one ray: Cone the lamp with the tip of its cone moved behind it, Width the width of the cell at the lamp
|
||||
vec3 lamp_segment(light_s lamp, light_s cone, vec3 start, vec3 direction, float span, float width, float part)
|
||||
{
|
||||
vec3 sum = vec3(0.0);
|
||||
float closest = dot(lamp.pos - start, direction);
|
||||
vec3 nearest = start + direction * closest;
|
||||
float reach = sqrt(dot(lamp.pos - nearest, lamp.pos - nearest) + width * width);
|
||||
float first = atan(-closest, reach);
|
||||
float last = atan(span - closest, reach);
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
vec3 position = start + direction * (closest + reach * tan(mix(first, last, (float(i) + part) * 0.5)));
|
||||
vec3 tolight = cone.pos - position;
|
||||
// the light without its falloff, which the integral holds
|
||||
vec3 fromlamp = position - lamp.pos;
|
||||
sum += lamp_light(cone, position) * max(dot(tolight, tolight), 0.01) * henyey_greenstein(dot(fromlamp, -direction) * inversesqrt(max(dot(fromlamp, fromlamp), 1e-8)), 0.4);
|
||||
}
|
||||
return sum * (0.5 * (last - first) / (reach * max(span, 1e-4)));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec3 cell = ivec3(gl_GlobalInvocationID);
|
||||
if (any(greaterThanEqual(cell, grid)))
|
||||
return;
|
||||
vec3 offset = cell_offset(cell);
|
||||
vec2 coords = (vec2(cell.xy) + offset.xy) / vec2(grid.xy);
|
||||
float depth = slice_depth(float(cell.z) + offset.z);
|
||||
vec3 ray = view_ray(coords);
|
||||
vec3 view = ray * depth;
|
||||
// the segment of the ray through the cell, for the light which changes within the cell
|
||||
float raylength = length(ray);
|
||||
vec3 raydirection = ray / raylength;
|
||||
float front = slice_depth(float(cell.z));
|
||||
vec3 start = ray * front;
|
||||
float span = (slice_depth(float(cell.z) + 1.0) - front) * raylength;
|
||||
vec3 world = mat3(view_to_world) * view;
|
||||
float height = weather_camera.x + world.y;
|
||||
|
||||
// density
|
||||
float haze = fog_haze.x * exp(-fog_haze.y * max(height, 0.0));
|
||||
float ground = fog_ground.x * exp(-fog_ground.y * max(height - fog_ground.z, 0.0));
|
||||
if (ground > 0.0 && fog_ground.w > 0.0)
|
||||
{
|
||||
float patches = textureLod(cloud_shape_noise, (world + fog_noise.xyz) * fog_noise.w, 0.0).g;
|
||||
ground *= mix(1.0, clamp((patches - 0.3) * 2.6, 0.0, 2.2), fog_ground.w);
|
||||
}
|
||||
float clouds = cloud_density_coarse(world);
|
||||
float extinction = haze + ground + clouds;
|
||||
|
||||
vec4 result = vec4(0.0);
|
||||
if (extinction > 1e-7)
|
||||
{
|
||||
vec3 direction = normalize(world);
|
||||
// the light of the sky and the sun, held back by the shadows
|
||||
float sunlit = cloud_shadow(world);
|
||||
if (options.z > 0.5 && sunlit > 0.0)
|
||||
{
|
||||
// two points of the segment, one in each half: a shaft of light no longer comes and goes with the one point sampled
|
||||
float shadow = sun_shadow(start + raydirection * (span * 0.5 * offset.z)) + sun_shadow(start + raydirection * (span * 0.5 * (offset.z + 1.0)));
|
||||
sunlit *= 1.0 - 0.5 * shadow;
|
||||
}
|
||||
vec3 light = fog_whiten(fog_color_world(direction), (ground + clouds) / extinction) * fog_light_factor(sunlit);
|
||||
// lamps and headlights of the clusters the segment of the cell goes through, each part of the segment with the lights of
|
||||
// its own cluster. the lights of the cluster of the point sampled would change with the point, frame to frame, where the
|
||||
// cell reaches into two clusters which don't hold the same lights (a full cluster leaves the least important out)
|
||||
if (options.y > 0.0 && lights_clustered != 0U && cluster_valid != 0U)
|
||||
{
|
||||
vec2 middle = (vec2(cell.xy) + 0.5) / vec2(grid.xy);
|
||||
ivec2 tile = clamp(ivec2(middle * vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y)), ivec2(0), ivec2(CLUSTER_GRID_X - 1, CLUSTER_GRID_Y - 1));
|
||||
float back = front + span / raylength;
|
||||
int first = clamp(int(floor(cluster_slice_bias + log(max(front, 1e-4)) * cluster_slice_scale)), 0, CLUSTER_GRID_Z - 1);
|
||||
int last = clamp(int(floor(cluster_slice_bias + log(max(back, 1e-4)) * cluster_slice_scale)), first, min(first + 3, CLUSTER_GRID_Z - 1));
|
||||
float cellwidth = length(view_ray(coords + vec2(1.0 / float(grid.x), 0.0)) - ray);
|
||||
vec3 lamps = vec3(0.0);
|
||||
for (int slice = first; slice <= last; slice++)
|
||||
{
|
||||
float partfront = (slice == first) ? front : exp((float(slice) - cluster_slice_bias) / cluster_slice_scale);
|
||||
float partback = (slice == last) ? back : exp((float(slice + 1) - cluster_slice_bias) / cluster_slice_scale);
|
||||
float partspan = max(partback - partfront, 0.0) * raylength;
|
||||
if (partspan <= 0.0)
|
||||
continue;
|
||||
vec3 partstart = ray * partfront;
|
||||
cluster_range_s range = cluster_ranges[uint(tile.x + CLUSTER_GRID_X * (tile.y + CLUSTER_GRID_Y * slice))];
|
||||
vec3 partlamps = vec3(0.0);
|
||||
for (uint i = 0U; i < range.count; i++)
|
||||
{
|
||||
uint index = cluster_light_indices[range.offset + i];
|
||||
if (int(index) == cablamp)
|
||||
continue;
|
||||
partlamps += lamp_scattering(lights[index], partstart, raydirection, partspan, cellwidth, offset.z);
|
||||
}
|
||||
lamps += partlamps * (partspan / max(span, 1e-4));
|
||||
}
|
||||
light += lamps * options.y;
|
||||
}
|
||||
result = vec4(light * fog_haze.w * extinction, extinction);
|
||||
}
|
||||
|
||||
// the previous frame, where the middle of the cell was then: the cells of the history hold what the cells were on the whole,
|
||||
// not at the point they were sampled at
|
||||
float share = options.x;
|
||||
if (share < 1.0)
|
||||
{
|
||||
vec3 centre = mat3(view_to_world) * (view_ray((vec2(cell.xy) + 0.5) / vec2(grid.xy)) * slice_depth(float(cell.z) + 0.5));
|
||||
vec4 clip = previous_projection_view * vec4(centre - previous_camera, 1.0);
|
||||
if (clip.w > 0.0)
|
||||
{
|
||||
float previousslice = log(max((clip.w + FOG_VOLUME_BIAS) / (previous_slicing.x + FOG_VOLUME_BIAS), 1e-6)) * previous_slicing.y;
|
||||
vec3 previous = vec3(clip.xy / clip.w * 0.5 + 0.5, previousslice / float(grid.z));
|
||||
if (all(greaterThanEqual(previous, vec3(0.0))) && all(lessThanEqual(previous, vec3(1.0))))
|
||||
result = mix(textureLod(fog_history, previous, 0.0), result, share);
|
||||
}
|
||||
}
|
||||
if (any(isnan(result)))
|
||||
result = vec4(0.0);
|
||||
imageStore(fog_scattering, cell, result);
|
||||
}
|
||||
86
shaders/ogl46/fog_integrate.comp
Normal file
86
shaders/ogl46/fog_integrate.comp
Normal file
@@ -0,0 +1,86 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// the fog volume of the main view, second pass: the light and extinction of the cells (fog_inject.comp) added up along the rays
|
||||
// of the view, front to back, into what the fog adds and lets through from the camera to the far side of every slice
|
||||
// (apply_fog.glsl). the light of a cell is integrated over its length as Hillaire (2015) does it, so it doesn't grow with
|
||||
// the size of the cell. the light of the cells is taken with the light of their neighbours across the view, a 3x3 tent filter
|
||||
// (four fetches of the linear filter): every cell is sampled at a point of its own in a frame, and what is left of that after
|
||||
// the blending over frames differs from cell to cell, which the filter evens out. the history keeps the cells unfiltered.
|
||||
// past the last slice one more: the fog all the way through the volume without the low clouds it holds, for the sky
|
||||
|
||||
#include "common.glsl"
|
||||
#include "weather_common.glsl"
|
||||
|
||||
layout(local_size_x = WEATHER_GROUP_SIZE, local_size_y = WEATHER_GROUP_SIZE, local_size_z = 1) in;
|
||||
|
||||
layout(binding = CLOUD_HISTORY_TEX) uniform sampler3D fog_scattering;
|
||||
layout(binding = CLOUD_SHAPE_TEX) uniform sampler3D cloud_shape_noise;
|
||||
layout(rgba16f, binding = 0) uniform writeonly image3D fog_integrated;
|
||||
|
||||
layout(location = 0) uniform ivec3 grid;
|
||||
layout(location = 1) uniform mat4 inverse_projection;
|
||||
layout(location = 2) uniform mat4 view_to_world;
|
||||
// x: near, y: far, z: slices / log((far + FOG_VOLUME_BIAS) / (near + FOG_VOLUME_BIAS))
|
||||
layout(location = 3) uniform vec4 slicing;
|
||||
|
||||
// extinction (1/m) of the haze and the ground fog at a point, made as fog_inject.comp makes them
|
||||
float fog_clear_extinction(vec3 world)
|
||||
{
|
||||
float height = weather_camera.x + world.y;
|
||||
float haze = fog_haze.x * exp(-fog_haze.y * max(height, 0.0));
|
||||
float ground = fog_ground.x * exp(-fog_ground.y * max(height - fog_ground.z, 0.0));
|
||||
if (ground > 0.0 && fog_ground.w > 0.0)
|
||||
{
|
||||
float patches = textureLod(cloud_shape_noise, (world + fog_noise.xyz) * fog_noise.w, 0.0).g;
|
||||
ground *= mix(1.0, clamp((patches - 0.3) * 2.6, 0.0, 2.2), fog_ground.w);
|
||||
}
|
||||
return haze + ground;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 column = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(column, grid.xy)))
|
||||
return;
|
||||
vec2 coords = (vec2(column) + 0.5) / vec2(grid.xy);
|
||||
vec4 near = inverse_projection * vec4(coords * 2.0 - 1.0, 1.0, 1.0);
|
||||
vec3 ray = near.xyz / near.w;
|
||||
// metres along the ray per metre of the depth
|
||||
float stretch = length(ray / -ray.z);
|
||||
|
||||
vec3 light = vec3(0.0);
|
||||
float transmittance = 1.0;
|
||||
// the same without the clouds: over the sky the ray march draws them (clouds_march.comp), and the volume laid over it would draw
|
||||
// those within its reach a second time, blurred to the size of the cells and cut into bands by the slices
|
||||
vec3 clearlight = vec3(0.0);
|
||||
float cleartransmittance = 1.0;
|
||||
float front = slicing.x;
|
||||
for (int slice = 0; slice < grid.z; slice++)
|
||||
{
|
||||
float back = (slicing.x + FOG_VOLUME_BIAS) * exp(float(slice + 1) / slicing.z) - FOG_VOLUME_BIAS;
|
||||
float span = (back - front) * stretch;
|
||||
front = back;
|
||||
vec4 cell = texelFetch(fog_scattering, ivec3(column, slice), 0);
|
||||
float depthcoord = (float(slice) + 0.5) / float(grid.z);
|
||||
vec2 texel = 1.0 / vec2(grid.xy);
|
||||
vec4 filtered = 0.25 * (textureLod(fog_scattering, vec3(coords + vec2(-0.5, -0.5) * texel, depthcoord), 0.0)
|
||||
+ textureLod(fog_scattering, vec3(coords + vec2(0.5, -0.5) * texel, depthcoord), 0.0)
|
||||
+ textureLod(fog_scattering, vec3(coords + vec2(-0.5, 0.5) * texel, depthcoord), 0.0)
|
||||
+ textureLod(fog_scattering, vec3(coords + vec2(0.5, 0.5) * texel, depthcoord), 0.0));
|
||||
cell.rgb = filtered.rgb;
|
||||
float extinction = max(cell.a, 1e-7);
|
||||
float through = exp(-extinction * span);
|
||||
light += transmittance * cell.rgb * ((1.0 - through) / extinction);
|
||||
transmittance *= through;
|
||||
imageStore(fog_integrated, ivec3(column, slice), vec4(light, transmittance));
|
||||
// the haze and the ground fog of the cell, at its middle, and their light as that of the cell per extinction, the lamps and the
|
||||
// shadows with it; what is left of the extinction is the clouds. the light per extinction from the filtered cells, light and
|
||||
// extinction alike: the filtered light over the extinction of the cell alone would bring in the light of a cloud next to it
|
||||
float middle = (slicing.x + FOG_VOLUME_BIAS) * exp((float(slice) + 0.5) / slicing.z) - FOG_VOLUME_BIAS;
|
||||
float clear = min(fog_clear_extinction(mat3(view_to_world) * (ray / -ray.z * middle)), extinction);
|
||||
float clearthrough = exp(-clear * span);
|
||||
clearlight += cleartransmittance * (filtered.rgb / max(filtered.a, 1e-7)) * (1.0 - clearthrough);
|
||||
cleartransmittance *= clearthrough;
|
||||
}
|
||||
imageStore(fog_integrated, ivec3(column, grid.z), vec4(clearlight, cleartransmittance));
|
||||
}
|
||||
73
shaders/ogl46/fog_volume.glsl
Normal file
73
shaders/ogl46/fog_volume.glsl
Normal file
@@ -0,0 +1,73 @@
|
||||
// fog between the camera and a point of the view space (weather_common.glsl): from the fog volume of the main view
|
||||
// (fog_inject.comp, fog_integrate.comp) up to its far end and along the ray past it, or along the ray all the way in the
|
||||
// other passes. Fragcoord: the pixel of the target the point is seen at. the volume holds one slice more than fog_volume_params.w:
|
||||
// the fog through all of it without the clouds, for the sky
|
||||
#ifndef FOG_VOLUME_GLSL
|
||||
#define FOG_VOLUME_GLSL
|
||||
|
||||
#include "weather_common.glsl"
|
||||
|
||||
layout(binding = FOG_VOLUME_TEX) uniform sampler3D fog_volume;
|
||||
|
||||
// the volume at a point of the view (0-1) and a coordinate along the slices, filtered across the view with a cubic B-spline
|
||||
// (four fetches of the linear filter): a cell is several pixels wide, and the linear filter alone draws the shafts of light and
|
||||
// the edges of shadows in the fog as steps
|
||||
vec4 fog_volume_at(vec2 coords, float slice)
|
||||
{
|
||||
vec2 size = vec2(textureSize(fog_volume, 0).xy);
|
||||
vec2 texel = coords * size - 0.5;
|
||||
vec2 base = floor(texel);
|
||||
vec2 f = texel - base;
|
||||
vec2 f2 = f * f;
|
||||
vec2 f3 = f2 * f;
|
||||
vec2 w0 = (1.0 - 3.0 * f + 3.0 * f2 - f3) / 6.0;
|
||||
vec2 w1 = (4.0 - 6.0 * f2 + 3.0 * f3) / 6.0;
|
||||
vec2 w2 = (1.0 + 3.0 * f + 3.0 * f2 - 3.0 * f3) / 6.0;
|
||||
vec2 w3 = f3 / 6.0;
|
||||
vec2 g0 = w0 + w1;
|
||||
vec2 g1 = w2 + w3;
|
||||
vec2 h0 = (base - 0.5 + w1 / g0) / size;
|
||||
vec2 h1 = (base + 1.5 + w3 / g1) / size;
|
||||
return g0.y * (g0.x * textureLod(fog_volume, vec3(h0.x, h0.y, slice), 0.0) + g1.x * textureLod(fog_volume, vec3(h1.x, h0.y, slice), 0.0))
|
||||
+ g1.y * (g0.x * textureLod(fog_volume, vec3(h0.x, h1.y, slice), 0.0) + g1.x * textureLod(fog_volume, vec3(h1.x, h1.y, slice), 0.0));
|
||||
}
|
||||
|
||||
// rgb: light the fog adds, a: what it lets through
|
||||
vec4 fog_along_view(vec3 view, vec2 fragcoord)
|
||||
{
|
||||
float span_distance = length(view);
|
||||
if (span_distance <= 0.0)
|
||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec3 direction = mat3(inv_view) * (view / span_distance);
|
||||
if (fog_volume_params.x < 0.5)
|
||||
return fog_analytic(direction, 0.0, span_distance, false);
|
||||
// slices are spaced exponentially in the view depth plus FOG_VOLUME_BIAS; slice k holds the fog up to its far side
|
||||
float depth = max(-view.z, 1e-4);
|
||||
float slices = fog_volume_params.w;
|
||||
float slice = log(max((depth + FOG_VOLUME_BIAS) / (fog_volume_params.y + FOG_VOLUME_BIAS), 1e-6)) * fog_volume_params.z;
|
||||
vec2 coords = fragcoord * fog_volume_target.xy;
|
||||
if (slice < 1.0)
|
||||
return mix(vec4(0.0, 0.0, 0.0, 1.0), fog_volume_at(coords, 0.5 / (slices + 1.0)), max(slice, 0.0));
|
||||
if (slice < slices)
|
||||
return fog_volume_at(coords, (slice - 0.5) / (slices + 1.0));
|
||||
vec4 volume = fog_volume_at(coords, (slices - 0.5) / (slices + 1.0));
|
||||
vec4 rest = fog_analytic(direction, fog_volume_target.z * span_distance / depth, span_distance, false);
|
||||
return vec4(volume.rgb + volume.a * rest.rgb, volume.a * rest.a);
|
||||
}
|
||||
|
||||
// fog in front of the sky in a view space direction: through the fog volume to its far end, then along the ray without the
|
||||
// haze the atmosphere draws already. the far fog of the volume and the fog over the sky behind it are the same then. the volume
|
||||
// is taken without the low clouds it holds (its extra slice): the ray march draws them over the sky already
|
||||
vec4 fog_sky(vec3 viewdirection, vec2 fragcoord)
|
||||
{
|
||||
vec3 view = normalize(viewdirection);
|
||||
vec3 direction = mat3(inv_view) * view;
|
||||
if (fog_volume_params.x < 0.5 || view.z > -1e-3)
|
||||
return fog_analytic(direction, 0.0, 200000.0, true);
|
||||
float slices = fog_volume_params.w;
|
||||
vec4 volume = fog_volume_at(fragcoord * fog_volume_target.xy, (slices + 0.5) / (slices + 1.0));
|
||||
vec4 rest = fog_analytic(direction, fog_volume_target.z / -view.z, 200000.0, true);
|
||||
return vec4(volume.rgb + volume.a * rest.rgb, volume.a * rest.a);
|
||||
}
|
||||
|
||||
#endif
|
||||
33
shaders/ogl46/freespot.frag
Normal file
33
shaders/ogl46/freespot.frag
Normal file
@@ -0,0 +1,33 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#include "tonemapping.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = (gl_PointCoord.x - 0.5f) * 2.0f;
|
||||
float y = (gl_PointCoord.y - 0.5f) * 2.0f;
|
||||
float dist = sqrt(x * x + y * y);
|
||||
if (dist > 0.5f)
|
||||
discard;
|
||||
// vec4 color = vec4(param[0].rgb * emission, mix(param[0].a, 0.0f, dist * 2.0f));
|
||||
vec4 color = vec4(apply_fog(pow(param[0].rgb, vec3(2.2)) * emission), mix(param[0].a, 0.0f, dist * 2.0f));
|
||||
// vec4 color = vec4(add_fog(param[0].rgb * emission * (1.0 - get_fog_amount()), 1.0), mix(param[0].a, 0.0f, dist * 2.0f));
|
||||
out_color = FBOUT(color);
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
21
shaders/ogl46/freespot.vert
Normal file
21
shaders/ogl46/freespot.vert
Normal file
@@ -0,0 +1,21 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
layout(location = 0) in vec3 v_vert;
|
||||
layout(location = 1) in vec3 v_normal;
|
||||
layout(location = 2) in vec2 v_coord;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 3) out vec4 f_pos;
|
||||
layout(location = 4) out vec4 f_clip_pos;
|
||||
layout(location = 5) out vec4 f_clip_future_pos;
|
||||
|
||||
void main()
|
||||
{
|
||||
f_pos = modelview * vec4(v_vert, 1.0);
|
||||
f_clip_pos = (projection * modelview) * vec4(v_vert, 1.0f);
|
||||
f_clip_future_pos = (projection * future * modelview) * vec4(v_vert, 1.0f);
|
||||
|
||||
gl_Position = f_clip_pos;
|
||||
gl_PointSize = param[1].x;
|
||||
}
|
||||
43
shaders/ogl46/grass.frag
Normal file
43
shaders/ogl46/grass.frag
Normal file
@@ -0,0 +1,43 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// grass of the heightmap terrain (opengl46_grass, grass.vert): the blades lit like the rest of the scene
|
||||
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 12) in vec3 f_albedo;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
glossiness = phong_glossiness(6.0);
|
||||
metalic = 0.0;
|
||||
vec3 fragcolor = apply_lights(ambient, fragnormal, f_albedo, 0.0, 0.2, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), 1.0);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
229
shaders/ogl46/grass.vert
Normal file
229
shaders/ogl46/grass.vert
Normal file
@@ -0,0 +1,229 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
// grass of the heightmap terrain (opengl46_grass): a blade made of the numbers of its vertex and its instance.
|
||||
//
|
||||
// the instances of a draw stand in a tile of a chunk. a blade takes its place in the tile from a sequence which spreads
|
||||
// any number of its first members evenly (R2, shifted for each tile), so drawing fewer of them thins the grass out
|
||||
// evenly; a blade picks its kind by the densities painted there, and gets its height, its turn and its lean from its own
|
||||
// numbers, so it's the same in every frame. blades of no kind, in the holes, or past the share drawn at their distance are
|
||||
// collapsed into a point outside the view. the vertices go up the blade in pairs, the last one is its tip.
|
||||
// the chunk is drawn relative to its corner: x, z in metres from the corner, y the height as it is
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
// x, z: corner of the tile in the chunk, metres; y: side of the tile; w: blades of the tile where its densest kind is painted fully
|
||||
layout(location = 0) uniform vec4 grass_tile;
|
||||
// x: the share of the densest kind at the densest place of the tile (the blades of the tile are counted for it); y: segments
|
||||
// of a blade; z, w: number of the tile in the world
|
||||
layout(location = 1) uniform vec4 grass_part;
|
||||
// x: side of the chunk, metres; y: cells of its grid along a side; z: grass samples along a side less one
|
||||
layout(location = 2) uniform vec4 grass_chunk;
|
||||
// the camera relative to the corner of the chunk (y as it is)
|
||||
layout(location = 3) uniform vec4 grass_camera;
|
||||
// x: distance the grass starts to thin out at, for speed; y: distance the fading starts at (gfx.grass.distance); z: where the
|
||||
// grass ends (gfx.grass.fadedistance)
|
||||
layout(location = 4) uniform vec4 grass_lod;
|
||||
// x, y: direction of the wind (x, z of the world); z: its strength, 0..1; w: time, seconds
|
||||
layout(location = 5) uniform vec4 grass_wind;
|
||||
// the kinds of the chunk: colour at the root and at the tip (linear rgb), and height, its variation, width, lean
|
||||
layout(location = 6) uniform vec4 grass_base[4];
|
||||
layout(location = 10) uniform vec4 grass_tip[4];
|
||||
layout(location = 14) uniform vec4 grass_shape[4];
|
||||
// share of the blades of each kind against the densest one of the chunk
|
||||
layout(location = 18) uniform vec4 grass_share;
|
||||
// the colour of each kind dried out (linear rgb), and how much it follows the seasons
|
||||
layout(location = 19) uniform vec4 grass_dry[4];
|
||||
// the time of the year: x: how dry the grass is, y: how fresh (spring), z: how grown (share of the height)
|
||||
layout(location = 23) uniform vec4 grass_season;
|
||||
|
||||
layout(binding = 0) uniform sampler2D grass_heights; // heights of the grid points, R32F
|
||||
layout(binding = 1) uniform sampler2D grass_density; // densities of the kinds, RGBA8
|
||||
layout(binding = 2) uniform sampler2D grass_holes; // holes cut in the ground, R8
|
||||
|
||||
layout(location = 0) out vec3 f_normal;
|
||||
layout(location = 3) out vec4 f_pos;
|
||||
layout(location = 12) out vec3 f_albedo;
|
||||
layout(location = 4) out vec4 f_clip_pos;
|
||||
layout(location = 5) out vec4 f_clip_future_pos;
|
||||
layout(location = 7) out vec4 UserData;
|
||||
|
||||
uint hash(uint x)
|
||||
{
|
||||
x ^= x >> 16;
|
||||
x *= 0x7feb352dU;
|
||||
x ^= x >> 15;
|
||||
x *= 0x846ca68bU;
|
||||
x ^= x >> 16;
|
||||
return x;
|
||||
}
|
||||
|
||||
float random(inout uint state)
|
||||
{
|
||||
state = hash(state);
|
||||
return float(state >> 8) * (1.0 / 16777216.0);
|
||||
}
|
||||
|
||||
// the share of the blades drawn at specified distance for speed: fewer further away, each standing for more of them
|
||||
float lod_thinning(float distance)
|
||||
{
|
||||
return distance <= grass_lod.x ? 1.0 : pow(grass_lod.x / distance, 1.5);
|
||||
}
|
||||
|
||||
// the share of the blades drawn: the above, and fewer and fewer over the fading distance, where the grass really gets
|
||||
// sparser; the same as lod_share() of opengl46grass.cpp
|
||||
float lod_share(float distance)
|
||||
{
|
||||
return lod_thinning(distance) * (1.0 - smoothstep(grass_lod.y, grass_lod.z, distance));
|
||||
}
|
||||
|
||||
// height of the ground at a point of the chunk and its normal, the way the full grid is triangulated
|
||||
float ground(vec2 point, out vec3 normal)
|
||||
{
|
||||
float cells = grass_chunk.y;
|
||||
float spacing = grass_chunk.x / cells;
|
||||
vec2 place = clamp(point / spacing, vec2(0.0), vec2(cells));
|
||||
ivec2 cell = min(ivec2(place), ivec2(int(cells) - 1));
|
||||
vec2 t = place - vec2(cell);
|
||||
float h00 = texelFetch(grass_heights, cell, 0).r;
|
||||
float h10 = texelFetch(grass_heights, cell + ivec2(1, 0), 0).r;
|
||||
float h01 = texelFetch(grass_heights, cell + ivec2(0, 1), 0).r;
|
||||
float h11 = texelFetch(grass_heights, cell + ivec2(1, 1), 0).r;
|
||||
// the quads are cut along the diagonal from (x0, z1) to (x1, z0), as the mesh is made
|
||||
if (t.x + t.y <= 1.0)
|
||||
{
|
||||
normal = normalize(vec3(-(h10 - h00) / spacing, 1.0, -(h01 - h00) / spacing));
|
||||
return h00 + t.x * (h10 - h00) + t.y * (h01 - h00);
|
||||
}
|
||||
normal = normalize(vec3(-(h11 - h01) / spacing, 1.0, -(h11 - h10) / spacing));
|
||||
return h11 + (1.0 - t.x) * (h01 - h11) + (1.0 - t.y) * (h10 - h11);
|
||||
}
|
||||
|
||||
void collapse()
|
||||
{
|
||||
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
||||
f_normal = vec3(0.0, 1.0, 0.0);
|
||||
f_pos = vec4(0.0);
|
||||
f_albedo = vec3(0.0);
|
||||
f_clip_pos = gl_Position;
|
||||
f_clip_future_pos = gl_Position;
|
||||
UserData = vec4(0.0);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
int blade = gl_InstanceID;
|
||||
// the numbers of the blade, drawn in the same order whatever becomes of them
|
||||
uint state = hash(uint(blade) * 0x9E3779B9U ^ hash(uint(int(grass_part.z)) * 0x85EBCA6BU ^ hash(uint(int(grass_part.w)) + 0x27D4EB2FU)));
|
||||
uint tilestate = hash(uint(int(grass_part.z)) * 0x85EBCA6BU ^ hash(uint(int(grass_part.w)) + 0x165667B1U));
|
||||
vec2 shift = vec2(random(tilestate), random(tilestate));
|
||||
vec2 jitter = vec2(random(state), random(state)) - 0.5;
|
||||
float pick = random(state);
|
||||
float heightrandom = random(state) * 2.0 - 1.0;
|
||||
float widthrandom = random(state);
|
||||
float turnrandom = random(state) * 6.2831853;
|
||||
float leanrandom = random(state) * 6.2831853;
|
||||
float leanamount = random(state);
|
||||
float shade = random(state);
|
||||
float phase = random(state);
|
||||
float early = random(state);
|
||||
|
||||
// place: R2 spreads the first blades evenly at any count, a little jitter breaks its lattice
|
||||
vec2 spot = fract(shift + float(blade) * vec2(0.7548776662466927, 0.5698402909980532));
|
||||
vec2 point = grass_tile.xy + fract(spot + jitter * (0.7 / sqrt(grass_tile.w))) * grass_tile.z;
|
||||
if (any(lessThan(point, vec2(0.0))) || any(greaterThanEqual(point, vec2(grass_chunk.x))))
|
||||
{
|
||||
collapse();
|
||||
return;
|
||||
}
|
||||
vec2 local = point / grass_chunk.x;
|
||||
float samples = grass_chunk.z;
|
||||
vec4 density = texture(grass_density, (local * samples + 0.5) / (samples + 1.0));
|
||||
float holeside = float(textureSize(grass_holes, 0).x);
|
||||
float hole = texture(grass_holes, (local * (holeside - 1.0) + 0.5) / holeside).r;
|
||||
|
||||
// the kind: the blades of the tile are shared among the kinds by their densities here
|
||||
vec4 cover = density * grass_share / max(grass_part.x, 1e-3);
|
||||
float covered = cover.x + cover.y + cover.z + cover.w;
|
||||
if (covered > 1.0)
|
||||
cover /= covered;
|
||||
int kind = -1;
|
||||
float sum = 0.0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
sum += cover[i];
|
||||
if (pick < sum)
|
||||
{
|
||||
kind = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 groundnormal;
|
||||
vec3 root = vec3(point.x, ground(point, groundnormal), point.y);
|
||||
float distance = length(root - grass_camera.xyz);
|
||||
float share = lod_share(distance);
|
||||
float rank = (float(blade) + 0.5) / grass_tile.w;
|
||||
if (kind < 0 || hole > 0.5 || rank >= share || distance > grass_lod.z)
|
||||
{
|
||||
collapse();
|
||||
return;
|
||||
}
|
||||
|
||||
vec4 shape = grass_shape[kind];
|
||||
float height = shape.x * (1.0 + shape.y * heightrandom);
|
||||
// the last of the blades drawn at the distance grow in, and the grass fades out at the end of its reach
|
||||
height *= 1.0 - smoothstep(share * 0.8, share, rank);
|
||||
// lower in winter, growing in spring
|
||||
float seasonal = grass_dry[kind].a;
|
||||
height *= mix(1.0, grass_season.z, seasonal);
|
||||
// where the paint thins out the blades get shorter
|
||||
height *= mix(0.5, 1.0, clamp(density[kind] * 1.5, 0.0, 1.0));
|
||||
// the blades drawn further away stand for more of them, so they're wider
|
||||
float width = shape.z * (0.8 + 0.4 * widthrandom) / sqrt(lod_thinning(distance));
|
||||
|
||||
// the face of the blade turns towards the camera further away, so the blades don't vanish edge on
|
||||
vec2 facing = vec2(cos(turnrandom), sin(turnrandom));
|
||||
vec2 toview = grass_camera.xz - root.xz;
|
||||
toview = toview / max(length(toview), 1e-3);
|
||||
facing = mix(facing, toview, smoothstep(grass_lod.x * 0.5, grass_lod.x * 2.0, distance));
|
||||
facing = facing / max(length(facing), 1e-3);
|
||||
vec2 across = vec2(-facing.y, facing.x);
|
||||
|
||||
// the lean of the blade and the wind: a steady push along it, with gusts running across the field, and a flutter
|
||||
vec2 lean = vec2(cos(leanrandom), sin(leanrandom)) * (shape.w * (0.4 + 0.6 * leanamount));
|
||||
float gust = 0.5 + 0.5 * sin(grass_wind.w * 0.9 - dot(root.xz, grass_wind.xy) * 0.12);
|
||||
float flutter = sin(grass_wind.w * (2.0 + phase) + phase * 6.2831853);
|
||||
vec2 sway = grass_wind.xy * grass_wind.z * (0.15 + 0.45 * gust + 0.08 * flutter);
|
||||
vec2 bend = lean + sway;
|
||||
// a long lean takes some of the height
|
||||
float bent = min(length(bend), 0.9);
|
||||
|
||||
int segments = int(grass_part.y);
|
||||
int level = min(gl_VertexID / 2, segments);
|
||||
float side = (gl_VertexID >= segments * 2) ? 0.0 : ((gl_VertexID & 1) == 0 ? -0.5 : 0.5);
|
||||
float t = float(level) / float(segments);
|
||||
float halfwidth = width * (1.0 - pow(t, 1.5));
|
||||
vec2 xz = root.xz + across * (side * halfwidth) + bend * (height * t * t);
|
||||
float y = root.y - 0.02 + height * t * (1.0 - 0.35 * bent * t);
|
||||
vec3 position = vec3(xz.x, y, xz.y);
|
||||
|
||||
// lit mostly as the ground under it, which keeps a field of blades from shimmering
|
||||
vec3 normal = normalize(groundnormal * 0.8 + vec3(facing.x, 0.0, facing.y) * (side == 0.0 ? 0.0 : 0.25) + vec3(0.0, 0.3, 0.0));
|
||||
// the seasons: some blades dry out (and green up) earlier than others, so a field turns gradually; a dry blade takes the
|
||||
// dry colour, darker at its root. in spring the green is fresher
|
||||
float dry = clamp(grass_season.x * seasonal * (0.6 + 0.8 * early), 0.0, 1.0);
|
||||
vec3 bottom = mix(grass_base[kind].rgb, grass_dry[kind].rgb * 0.45, dry);
|
||||
vec3 top = mix(grass_tip[kind].rgb, grass_dry[kind].rgb, dry);
|
||||
top *= mix(vec3(1.0), vec3(1.08, 1.22, 0.85), grass_season.y * seasonal * (1.0 - dry));
|
||||
vec3 albedo = mix(bottom, top, t) * (0.85 + 0.3 * shade);
|
||||
// the lower part of the blades is shaded by the others
|
||||
albedo *= mix(0.7, 1.0, t);
|
||||
|
||||
f_pos = modelview * vec4(position, 1.0);
|
||||
f_normal = normalize(modelviewnormal * normal);
|
||||
f_albedo = albedo;
|
||||
f_clip_pos = projection * f_pos;
|
||||
f_clip_future_pos = (projection * future) * f_pos;
|
||||
gl_Position = f_clip_pos;
|
||||
UserData = vec4(0.0);
|
||||
}
|
||||
52
shaders/ogl46/gtao_denoise.comp
Normal file
52
shaders/ogl46/gtao_denoise.comp
Normal file
@@ -0,0 +1,52 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// smooths the ambient occlusion of gtao_main.comp: the average of the 4x4 pixels around each one, which takes out the
|
||||
// repeating 4x4 pattern of the slices, with the pixels at a clearly different distance left out so the occlusion does
|
||||
// not leak across the edges of objects. one invocation per pixel of the half size target
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8) in;
|
||||
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 1) uniform sampler2D ao_tex;
|
||||
layout(binding = 0, r8) writeonly uniform image2D ao_target;
|
||||
|
||||
layout(location = 0) uniform mat4 inv_projection;
|
||||
|
||||
// relative difference of the distance at which a neighbour stops counting
|
||||
const float DEPTH_TOLERANCE = 0.05;
|
||||
|
||||
float view_distance(ivec2 texel)
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
float depth = texelFetch(depth_tex, min(texel * 2, full_size - 1), 0).r;
|
||||
if (depth <= 0.0)
|
||||
return 1e30;
|
||||
vec2 uv = (vec2(texel * 2) + 0.5) / vec2(full_size);
|
||||
vec4 position = inv_projection * vec4(uv * 2.0 - 1.0, depth, 1.0);
|
||||
return length(position.xyz / position.w);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 size = imageSize(ao_target);
|
||||
if (texel.x >= size.x || texel.y >= size.y)
|
||||
return;
|
||||
|
||||
float distance = view_distance(texel);
|
||||
float sum = 0.0;
|
||||
float weights = 0.0;
|
||||
for (int y = -2; y < 2; ++y)
|
||||
{
|
||||
for (int x = -2; x < 2; ++x)
|
||||
{
|
||||
ivec2 sampletexel = clamp(texel + ivec2(x, y), ivec2(0), size - 1);
|
||||
float sampledistance = view_distance(sampletexel);
|
||||
float weight = clamp(1.0 - abs(sampledistance - distance) / (DEPTH_TOLERANCE * distance), 0.0, 1.0);
|
||||
sum += texelFetch(ao_tex, sampletexel, 0).r * weight;
|
||||
weights += weight;
|
||||
}
|
||||
}
|
||||
// the pixel itself is in the window with the full weight, so weights >= 1
|
||||
imageStore(ao_target, texel, vec4(sum / weights));
|
||||
}
|
||||
154
shaders/ogl46/gtao_main.comp
Normal file
154
shaders/ogl46/gtao_main.comp
Normal file
@@ -0,0 +1,154 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// ground truth ambient occlusion (Jimenez et al. 2016, "Practical realtime strategies for accurate indirect occlusion";
|
||||
// integration as in Intel XeGTAO) at half the resolution of the image. for every pixel, a few slices through the
|
||||
// hemisphere around the view direction; in each the horizons on both sides are searched in the depth buffer and the
|
||||
// cosine weighted visible arc between them is integrated against the normal rebuilt from the depth.
|
||||
// one invocation per pixel of the half size target. the pattern of the slice directions and steps repeats every 4x4
|
||||
// pixels; gtao_denoise.comp averages it out
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8) in;
|
||||
|
||||
// depth of the scene at full resolution: reversed, 1 at the near plane, 0 at infinity (the sky)
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 0, r8) writeonly uniform image2D ao_target;
|
||||
|
||||
layout(location = 0) uniform mat4 inv_projection;
|
||||
// x: radius in metres, y: projection[0][0] times half the width of the target in pixels
|
||||
layout(location = 1) uniform vec2 params;
|
||||
// the row of the projection which gives the clip w of a view space position
|
||||
layout(location = 2) uniform vec4 clipw_row;
|
||||
// x: number of slices, y: steps on each side of a slice
|
||||
layout(location = 3) uniform ivec2 quality;
|
||||
|
||||
const float PI = 3.14159265;
|
||||
const float HALF_PI = 1.57079633;
|
||||
// share of the radius over which the weight of an occluder falls to nothing
|
||||
const float FALLOFF_RANGE = 0.615;
|
||||
|
||||
// depth of the pixel of the half size target: the top left one of the four full size pixels it covers, so the
|
||||
// application (postfx_ssao_apply.frag) finds the same depth for it
|
||||
float half_depth(ivec2 texel)
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
return texelFetch(depth_tex, min(texel * 2, full_size - 1), 0).r;
|
||||
}
|
||||
|
||||
vec3 view_position(ivec2 texel, float depth)
|
||||
{
|
||||
ivec2 full_size = textureSize(depth_tex, 0);
|
||||
vec2 uv = (vec2(texel * 2) + 0.5) / vec2(full_size);
|
||||
vec4 position = inv_projection * vec4(uv * 2.0 - 1.0, depth, 1.0);
|
||||
return position.xyz / position.w;
|
||||
}
|
||||
|
||||
// 4x4 ordered pattern, values 0..15
|
||||
const float BAYER[16] = float[](0.0, 8.0, 2.0, 10.0, 12.0, 4.0, 14.0, 6.0, 3.0, 11.0, 1.0, 9.0, 15.0, 7.0, 13.0, 5.0);
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 size = imageSize(ao_target);
|
||||
if (texel.x >= size.x || texel.y >= size.y)
|
||||
return;
|
||||
|
||||
float depth = half_depth(texel);
|
||||
if (depth <= 0.0)
|
||||
{
|
||||
imageStore(ao_target, texel, vec4(1.0));
|
||||
return;
|
||||
}
|
||||
vec3 position = view_position(texel, depth);
|
||||
vec3 view = normalize(-position);
|
||||
|
||||
// normal from the neighbours: on each axis the one closer in depth, so edges of objects do not bend it
|
||||
vec3 left = view_position(texel - ivec2(1, 0), half_depth(texel - ivec2(1, 0)));
|
||||
vec3 right = view_position(texel + ivec2(1, 0), half_depth(texel + ivec2(1, 0)));
|
||||
vec3 down = view_position(texel - ivec2(0, 1), half_depth(texel - ivec2(0, 1)));
|
||||
vec3 up = view_position(texel + ivec2(0, 1), half_depth(texel + ivec2(0, 1)));
|
||||
vec3 dx = (abs(right.z - position.z) < abs(position.z - left.z)) ? right - position : position - left;
|
||||
vec3 dy = (abs(up.z - position.z) < abs(position.z - down.z)) ? up - position : position - down;
|
||||
vec3 normal = normalize(cross(dx, dy));
|
||||
if (dot(normal, view) < 0.0)
|
||||
normal = -normal;
|
||||
|
||||
float radius = params.x;
|
||||
float clipw = dot(clipw_row, vec4(position, 1.0));
|
||||
float screenradius = params.y * radius / max(clipw, 1e-6);
|
||||
// smaller than a pixel: nothing to find
|
||||
if (screenradius < 1.0)
|
||||
{
|
||||
imageStore(ao_target, texel, vec4(1.0));
|
||||
return;
|
||||
}
|
||||
screenradius = min(screenradius, 256.0);
|
||||
|
||||
float falloffrange = FALLOFF_RANGE * radius;
|
||||
float falloffmul = -1.0 / falloffrange;
|
||||
float falloffadd = (radius - falloffrange) / falloffrange + 1.0;
|
||||
|
||||
int pattern = (texel.x & 3) + 4 * (texel.y & 3);
|
||||
float slicenoise = (BAYER[pattern] + 0.5) / 16.0;
|
||||
float stepnoise = fract(float(pattern) * 0.618034 + 0.25);
|
||||
|
||||
int slices = max(quality.x, 1);
|
||||
int steps = max(quality.y, 1);
|
||||
float visibility = 0.0;
|
||||
for (int slice = 0; slice < slices; ++slice)
|
||||
{
|
||||
float angle = (float(slice) + slicenoise) * PI / float(slices);
|
||||
vec2 omega = vec2(cos(angle), sin(angle));
|
||||
vec3 direction = vec3(omega, 0.0);
|
||||
vec3 orthodirection = direction - dot(direction, view) * view;
|
||||
vec3 axis = normalize(cross(orthodirection, view));
|
||||
vec3 projectednormal = normal - axis * dot(normal, axis);
|
||||
float projectedlength = length(projectednormal);
|
||||
if (projectedlength < 1e-4)
|
||||
continue;
|
||||
float signnormal = sign(dot(orthodirection, projectednormal));
|
||||
float cosnormal = clamp(dot(projectednormal, view) / projectedlength, 0.0, 1.0);
|
||||
float n = signnormal * acos(cosnormal);
|
||||
|
||||
float lowcos0 = cos(n + HALF_PI);
|
||||
float lowcos1 = cos(n - HALF_PI);
|
||||
float horizoncos0 = lowcos0;
|
||||
float horizoncos1 = lowcos1;
|
||||
|
||||
for (int step = 0; step < steps; ++step)
|
||||
{
|
||||
// denser near the pixel, where the occluders matter most
|
||||
float s = (float(step) + stepnoise) / float(steps);
|
||||
s = s * s;
|
||||
vec2 offset = omega * max(s * screenradius, float(step) + 1.0);
|
||||
for (int side = 0; side < 2; ++side)
|
||||
{
|
||||
ivec2 sampletexel = texel + ivec2(round(side == 0 ? offset : -offset));
|
||||
if (any(lessThan(sampletexel, ivec2(0))) || any(greaterThanEqual(sampletexel, size)))
|
||||
continue;
|
||||
float sampledepth = half_depth(sampletexel);
|
||||
if (sampledepth <= 0.0)
|
||||
continue;
|
||||
vec3 delta = view_position(sampletexel, sampledepth) - position;
|
||||
float distance = length(delta);
|
||||
if (distance < 1e-5)
|
||||
continue;
|
||||
float horizoncos = dot(delta / distance, view);
|
||||
float weight = clamp(distance * falloffmul + falloffadd, 0.0, 1.0);
|
||||
if (side == 0)
|
||||
horizoncos0 = max(horizoncos0, mix(lowcos0, horizoncos, weight));
|
||||
else
|
||||
horizoncos1 = max(horizoncos1, mix(lowcos1, horizoncos, weight));
|
||||
}
|
||||
}
|
||||
|
||||
float h0 = -acos(clamp(horizoncos1, -1.0, 1.0));
|
||||
float h1 = acos(clamp(horizoncos0, -1.0, 1.0));
|
||||
h0 = n + clamp(h0 - n, -HALF_PI, HALF_PI);
|
||||
h1 = n + clamp(h1 - n, -HALF_PI, HALF_PI);
|
||||
float arc0 = (cosnormal + 2.0 * h0 * sin(n) - cos(2.0 * h0 - n)) * 0.25;
|
||||
float arc1 = (cosnormal + 2.0 * h1 * sin(n) - cos(2.0 * h1 - n)) * 0.25;
|
||||
visibility += projectedlength * (arc0 + arc1);
|
||||
}
|
||||
visibility = clamp(visibility / float(slices), 0.0, 1.0);
|
||||
imageStore(ao_target, texel, vec4(visibility));
|
||||
}
|
||||
16
shaders/ogl46/hiddenarea.frag
Normal file
16
shaders/ogl46/hiddenarea.frag
Normal file
@@ -0,0 +1,16 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = vec4(vec3(0.0f), 1.0f);
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
gl_FragDepth = 1.0f;
|
||||
}
|
||||
8
shaders/ogl46/hiddenarea.vert
Normal file
8
shaders/ogl46/hiddenarea.vert
Normal file
@@ -0,0 +1,8 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
layout(location = 0) in vec3 v_vert;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(v_vert.xy * 2.0f - 1.0f, 0.5f, 1.0f);
|
||||
}
|
||||
41
shaders/ogl46/hiz_build.comp
Normal file
41
shaders/ogl46/hiz_build.comp
Normal file
@@ -0,0 +1,41 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// one level of the depth pyramid of the occlusion test of the instances (opengl46_hiz): each texel holds the farthest
|
||||
// depth of the texels of the level below it which it covers. the depth is reversed (1 near, 0 far), so the farthest is
|
||||
// the smallest. the first level is made from the resolved depth of the scene, the others from the level below.
|
||||
// a level halves the one below, rounding down; when that one has an odd size, the last row and column of the new level
|
||||
// take its last row and column as well, so that nothing is left out
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = HIZ_GROUP_SIZE, local_size_y = HIZ_GROUP_SIZE) in;
|
||||
|
||||
layout(binding = 0) uniform sampler2D source;
|
||||
layout(r32f, binding = 0) writeonly uniform image2D target;
|
||||
|
||||
// xy: size of the source level, zw: size of the target level
|
||||
layout(location = 0) uniform ivec4 sizes;
|
||||
// x: level of the source read
|
||||
layout(location = 1) uniform ivec4 params;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
if (any(greaterThanEqual(texel, sizes.zw)))
|
||||
return;
|
||||
|
||||
ivec2 first = min(2 * texel, sizes.xy - 1);
|
||||
ivec2 last = min(2 * texel + 1, sizes.xy - 1);
|
||||
if (texel.x == sizes.z - 1)
|
||||
last.x = sizes.x - 1;
|
||||
if (texel.y == sizes.w - 1)
|
||||
last.y = sizes.y - 1;
|
||||
|
||||
float farthest = 1.0;
|
||||
for (int y = first.y; y <= last.y; ++y)
|
||||
{
|
||||
for (int x = first.x; x <= last.x; ++x)
|
||||
farthest = min(farthest, texelFetch(source, ivec2(x, y), params.x).r);
|
||||
}
|
||||
imageStore(target, texel, vec4(farthest));
|
||||
}
|
||||
283
shaders/ogl46/instance_cull.comp
Normal file
283
shaders/ogl46/instance_cull.comp
Normal file
@@ -0,0 +1,283 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// culls the model instances of the buckets queued in a render pass and assigns them their lod bands, with the tests
|
||||
// opengl46_renderer::Render_Instanced() makes on the cpu. one invocation per record of a queued bucket. the sleepers of the
|
||||
// tracks queued come as buckets too, with the tests of opengl46_renderer::Render_Sleepers().
|
||||
// stage 0 counts the instances which pass the tests, per entry (lod band of a group); after instance_cull_offsets.comp
|
||||
// turned the counts into the first slots of the entries, stage 1 writes the modelview matrices of the instances drawn first
|
||||
// there, in no particular order, and stage 2 (two phases) those of the instances drawn second after them.
|
||||
// with the occlusion test on, an instance whose bounding sphere lies whole behind the depth already drawn in the pass
|
||||
// (the farthest depth of the pyramid over the part of the view it takes, see hiz_build.comp) is left out as well.
|
||||
// in two phases (opengl46_instance_cull::dispatch_second()) stage 1 takes only the instances seen in the previous frame,
|
||||
// which are drawn, the pyramid is made again with them in it, and stage 2 tests the others against it; it also keeps for
|
||||
// the next frame which instances it found in view. an instance seen before but hidden behind the depth of the first
|
||||
// pyramid is hidden behind the second one as well (it only holds nearer depth), so each instance is drawn at most once,
|
||||
// and everything left out is hidden behind something drawn
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = INSTANCE_CULL_GROUP_SIZE) in;
|
||||
|
||||
// scene::basic_cell::instance_record
|
||||
struct instance_record
|
||||
{
|
||||
vec3 position; // relative to the origin of the bucket
|
||||
float radius;
|
||||
float rangesquaredmin;
|
||||
float rangesquaredmax;
|
||||
uint flags;
|
||||
uint visible; // the instance was seen in the previous frame (written by stage 2, 0 when the cpu sends the record)
|
||||
vec4 rotationscale[3];
|
||||
};
|
||||
|
||||
struct instance_cull_job
|
||||
{
|
||||
vec3 origin;
|
||||
uint first;
|
||||
vec3 distanceorigin;
|
||||
uint count;
|
||||
uint group;
|
||||
uint candidatestart;
|
||||
uint flags;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
struct instance_cull_group
|
||||
{
|
||||
uint lodfirst;
|
||||
uint lodcount;
|
||||
uint entryfirst;
|
||||
uint pad;
|
||||
};
|
||||
|
||||
layout(std430, binding = INSTANCE_RECORD_SSBO_BINDING) buffer instance_record_ssbo
|
||||
{
|
||||
instance_record records[];
|
||||
};
|
||||
layout(std430, binding = INSTANCE_JOB_SSBO_BINDING) readonly buffer instance_job_ssbo
|
||||
{
|
||||
instance_cull_job jobs[];
|
||||
};
|
||||
layout(std430, binding = INSTANCE_GROUP_SSBO_BINDING) readonly buffer instance_group_ssbo
|
||||
{
|
||||
instance_cull_group groups[];
|
||||
};
|
||||
layout(std430, binding = INSTANCE_LOD_SSBO_BINDING) readonly buffer instance_lod_ssbo
|
||||
{
|
||||
float lodbounds[];
|
||||
};
|
||||
layout(std430, binding = INSTANCE_COUNTER_SSBO_BINDING) buffer instance_counter_ssbo
|
||||
{
|
||||
uint counters[];
|
||||
};
|
||||
layout(std430, binding = INSTANCE_OUTPUT_SSBO_BINDING) writeonly buffer instance_output_ssbo
|
||||
{
|
||||
mat4 modelviews[];
|
||||
};
|
||||
|
||||
// rotation of the view of the pass: the instances are placed relative to its camera
|
||||
layout(location = 0) uniform mat4 view;
|
||||
// planes of the frustum of the pass, relative to its camera
|
||||
layout(location = 1) uniform vec4 planes[6];
|
||||
// x: scale of the squared distance, y: added to it past the range test, z: range of the scaled distance before the
|
||||
// addition (reflections), w: squared draw range
|
||||
layout(location = 7) uniform vec4 distance_params;
|
||||
// x: zoom factor
|
||||
layout(location = 8) uniform vec4 size_params;
|
||||
// x: jobs, y: records in all jobs, z: entries, w: stage
|
||||
layout(location = 9) uniform uvec4 counts;
|
||||
// occlusion test: projection of the pass, x: on, y: levels of the pyramid, zw: size of its first level
|
||||
layout(location = 10) uniform mat4 hiz_projection;
|
||||
layout(location = 14) uniform ivec4 hiz_params;
|
||||
// xy: size of the depth the pyramid was made from, pixels
|
||||
layout(location = 15) uniform vec4 hiz_screen;
|
||||
// x: two phases, stage 1 takes only the instances seen in the previous frame
|
||||
layout(location = 16) uniform uvec4 phases;
|
||||
|
||||
// depth pyramid, farthest (smallest, reversed) depth per texel
|
||||
layout(binding = 0) uniform sampler2D hiz;
|
||||
|
||||
const uint INSTANCE_VISIBLE = 1u;
|
||||
const uint INSTANCE_INSTANCEABLE = 2u;
|
||||
// smallest part of the view an instance has to take
|
||||
const float MIN_SCREEN_SIZE = 0.003;
|
||||
// header of the counters, see gl::INSTANCE_COUNTER_HEADER
|
||||
const uint COUNTER_OCCLUDED = 3u;
|
||||
const uint COUNTER_FIRST = 4u;
|
||||
|
||||
// the job which holds the given record: the last one starting at it or before it
|
||||
uint find_job(uint candidate)
|
||||
{
|
||||
uint low = 0u;
|
||||
uint high = counts.x - 1u;
|
||||
while (low < high)
|
||||
{
|
||||
uint middle = (low + high + 1u) / 2u;
|
||||
if (jobs[middle].candidatestart <= candidate)
|
||||
low = middle;
|
||||
else
|
||||
high = middle - 1u;
|
||||
}
|
||||
return low;
|
||||
}
|
||||
|
||||
// whether a sphere (view space) lies whole behind the depth in the pyramid over the rectangle of the view it takes
|
||||
bool occluded(vec3 center, float radius)
|
||||
{
|
||||
vec2 low = vec2(1.0);
|
||||
vec2 high = vec2(-1.0);
|
||||
float nearest = 0.0;
|
||||
for (int corner = 0; corner < 8; ++corner)
|
||||
{
|
||||
vec3 offset = vec3(((corner & 1) != 0) ? radius : -radius, ((corner & 2) != 0) ? radius : -radius, ((corner & 4) != 0) ? radius : -radius);
|
||||
vec4 clip = hiz_projection * vec4(center + offset, 1.0);
|
||||
// reaches the plane of the camera: no rectangle to test
|
||||
if (clip.w <= 1e-3)
|
||||
return false;
|
||||
vec3 ndc = clip.xyz / clip.w;
|
||||
low = min(low, ndc.xy);
|
||||
high = max(high, ndc.xy);
|
||||
nearest = max(nearest, ndc.z);
|
||||
}
|
||||
if (any(greaterThan(low, vec2(1.0))) || any(lessThan(high, vec2(-1.0))) || (nearest >= 1.0))
|
||||
return false;
|
||||
|
||||
// pixels of the rectangle, then texels of the first level: each covers 2x2 pixels
|
||||
ivec2 size = hiz_params.zw;
|
||||
ivec2 pixelfirst = ivec2(clamp((low * 0.5 + 0.5) * hiz_screen.xy, vec2(0.0), hiz_screen.xy - 1.0));
|
||||
ivec2 pixellast = ivec2(clamp((high * 0.5 + 0.5) * hiz_screen.xy, vec2(0.0), hiz_screen.xy - 1.0));
|
||||
ivec2 first = min(pixelfirst / 2, size - 1);
|
||||
ivec2 last = min(pixellast / 2, size - 1);
|
||||
// the level where the rectangle takes at most 2x2 texels
|
||||
int level = 0;
|
||||
while ((level < hiz_params.y - 1) && any(greaterThan((last >> level) - (first >> level), ivec2(1))))
|
||||
++level;
|
||||
ivec2 levelsize = max(size >> level, ivec2(1));
|
||||
first = min(first >> level, levelsize - 1);
|
||||
last = min(last >> level, levelsize - 1);
|
||||
|
||||
float farthest = 1.0;
|
||||
for (int y = first.y; y <= last.y; ++y)
|
||||
{
|
||||
for (int x = first.x; x <= last.x; ++x)
|
||||
farthest = min(farthest, texelFetch(hiz, ivec2(x, y), level).r);
|
||||
}
|
||||
return nearest < farthest;
|
||||
}
|
||||
|
||||
// the tests of the cpu (Render_Instanced(), for sleepers Render_Sleepers()), all but the occlusion one: the entry of the instance, or false when it's left out.
|
||||
// Location: relative to the camera of the pass
|
||||
bool passes(instance_cull_job job, instance_record record, out vec3 location, out uint entry)
|
||||
{
|
||||
location = job.origin + record.position;
|
||||
entry = 0u;
|
||||
// instances hidden, or drawn on their own after getting animations or skins from events
|
||||
if ((record.flags & (INSTANCE_VISIBLE | INSTANCE_INSTANCEABLE)) != (INSTANCE_VISIBLE | INSTANCE_INSTANCEABLE))
|
||||
return false;
|
||||
|
||||
for (int plane = 0; plane < 6; ++plane)
|
||||
{
|
||||
if (dot(planes[plane].xyz, location) + planes[plane].w <= -record.radius)
|
||||
return false;
|
||||
}
|
||||
|
||||
float distancesquared;
|
||||
if ((job.flags & INSTANCE_JOB_SLEEPERS) != 0u)
|
||||
{
|
||||
// the sleepers of a track, as Render_Sleepers() takes them: the lod by the plain distance from the camera of the pass
|
||||
distancesquared = dot(location, location);
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3 offset = job.distanceorigin + record.position;
|
||||
distancesquared = dot(offset, offset) * distance_params.x;
|
||||
if (distancesquared > distance_params.z)
|
||||
return false;
|
||||
distancesquared += distance_params.y;
|
||||
if ((distancesquared < record.rangesquaredmin) || (distancesquared >= record.rangesquaredmax))
|
||||
return false;
|
||||
if (distancesquared > distance_params.w)
|
||||
return false;
|
||||
if (record.radius * record.radius * size_params.x / distancesquared < MIN_SCREEN_SIZE * MIN_SCREEN_SIZE)
|
||||
return false;
|
||||
}
|
||||
|
||||
// band: the number of lod bounds the distance reached, like upper_bound over the sorted bounds
|
||||
instance_cull_group group = groups[job.group];
|
||||
uint band = 0u;
|
||||
for (uint bound = 0u; bound < group.lodcount; ++bound)
|
||||
{
|
||||
if (lodbounds[group.lodfirst + bound] <= distancesquared)
|
||||
band = bound + 1u;
|
||||
}
|
||||
entry = group.entryfirst + band;
|
||||
return true;
|
||||
}
|
||||
|
||||
void write(uint entry, instance_record record, vec3 location, instance_cull_job job)
|
||||
{
|
||||
uint slot = atomicAdd(counters[INSTANCE_COUNTER_HEADER + counts.z + entry], 1u);
|
||||
if ((job.flags & INSTANCE_JOB_SLEEPERS) != 0u)
|
||||
{
|
||||
// in the order Render_Sleepers() multiplies them (the view moved to the origin of the track, then the transform of the
|
||||
// sleeper), which gives the very same matrix: the other order rounds differently, enough to shift the edges of sleepers
|
||||
// sunk in the ballast by a pixel here and there
|
||||
mat4 originview = view * mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(job.origin, 1.0));
|
||||
modelviews[slot] = originview * mat4(record.rotationscale[0], record.rotationscale[1], record.rotationscale[2], vec4(record.position, 1.0));
|
||||
return;
|
||||
}
|
||||
modelviews[slot] = view * mat4(record.rotationscale[0], record.rotationscale[1], record.rotationscale[2], vec4(location, 1.0));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
// the dispatch can be two dimensional when the records don't fit in the work group count limit of one
|
||||
uint candidate = (gl_WorkGroupID.y * gl_NumWorkGroups.x + gl_WorkGroupID.x) * INSTANCE_CULL_GROUP_SIZE + gl_LocalInvocationIndex;
|
||||
if (candidate >= counts.y)
|
||||
return;
|
||||
|
||||
instance_cull_job job = jobs[find_job(candidate)];
|
||||
uint index = job.first + (candidate - job.candidatestart);
|
||||
instance_record record = records[index];
|
||||
vec3 location;
|
||||
uint entry;
|
||||
bool passed = passes(job, record, location, entry);
|
||||
|
||||
if (counts.w == 0u)
|
||||
{
|
||||
// room in the output for every instance which passes, whatever the occlusion test says
|
||||
if (passed)
|
||||
atomicAdd(counters[INSTANCE_COUNTER_HEADER + entry], 1u);
|
||||
return;
|
||||
}
|
||||
|
||||
bool seen = (record.visible != 0u);
|
||||
if (counts.w == 1u)
|
||||
{
|
||||
// the ones seen in the previous frame first, the others wait for stage 2
|
||||
if ((false == passed) || ((phases.x != 0u) && (false == seen)))
|
||||
return;
|
||||
if ((hiz_params.x != 0) && occluded((view * vec4(location, 1.0)).xyz, record.radius))
|
||||
{
|
||||
atomicAdd(counters[COUNTER_OCCLUDED], 1u);
|
||||
return;
|
||||
}
|
||||
write(entry, record, location, job);
|
||||
if (phases.x != 0u)
|
||||
atomicAdd(counters[COUNTER_FIRST], 1u);
|
||||
return;
|
||||
}
|
||||
|
||||
// stage 2, against the pyramid with the instances drawn first in it
|
||||
bool inview = passed && (false == occluded((view * vec4(location, 1.0)).xyz, record.radius));
|
||||
if (passed && (false == seen))
|
||||
{
|
||||
if (inview)
|
||||
write(entry, record, location, job);
|
||||
else
|
||||
atomicAdd(counters[COUNTER_OCCLUDED], 1u);
|
||||
}
|
||||
uint visible = (inview ? 1u : 0u);
|
||||
if (visible != record.visible)
|
||||
records[index].visible = visible;
|
||||
}
|
||||
64
shaders/ogl46/instance_cull_commands.comp
Normal file
64
shaders/ogl46/instance_cull_commands.comp
Normal file
@@ -0,0 +1,64 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// fills in the instances of the indirect draw commands of the entries, after instance_cull.comp wrote the modelview
|
||||
// matrices of the instances drawn. one invocation per command. the commands are kept three times over: the first copy
|
||||
// draws the instances of stage 1, the second all of them, the third those of stage 2 (two phases), each from its first
|
||||
// slot in the output on. stage 0 (after stage 1): the instance count of the first copy is how far stage 1 advanced the
|
||||
// cursor of its entry, the base instance the first slot of the entry. stage 1 (after stage 2): the other two copies take
|
||||
// the command of the first one, the second with the instances of both stages, the third with the ones past the first copy
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = INSTANCE_COMMAND_GROUP_SIZE) in;
|
||||
|
||||
layout(std430, binding = INSTANCE_COUNTER_SSBO_BINDING) readonly buffer instance_counter_ssbo
|
||||
{
|
||||
uint counters[];
|
||||
};
|
||||
|
||||
// gl::instance_draw_command, eight uints each: count, instance count, first, base vertex (base instance of an array
|
||||
// draw), base instance, entry, indexed, unused
|
||||
layout(std430, binding = INSTANCE_COMMAND_SSBO_BINDING) buffer instance_command_ssbo
|
||||
{
|
||||
uint commands[];
|
||||
};
|
||||
|
||||
// x: commands (of one copy), y: entries, z: stage
|
||||
layout(location = 0) uniform uvec4 counts;
|
||||
|
||||
void set_instances(uint field, uint count, uint base)
|
||||
{
|
||||
commands[field + 1u] = count;
|
||||
if (commands[field + 6u] != 0u)
|
||||
commands[field + 4u] = base;
|
||||
else
|
||||
commands[field + 3u] = base;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint command = gl_GlobalInvocationID.x;
|
||||
if (command >= counts.x)
|
||||
return;
|
||||
|
||||
uint field = command * 8u;
|
||||
uint entry = commands[field + 5u];
|
||||
uint first = counters[INSTANCE_COUNTER_HEADER + 2u * counts.y + entry];
|
||||
uint count = counters[INSTANCE_COUNTER_HEADER + counts.y + entry] - first;
|
||||
if (counts.z == 0u)
|
||||
{
|
||||
set_instances(field, count, first);
|
||||
return;
|
||||
}
|
||||
|
||||
uint drawnfirst = commands[field + 1u];
|
||||
uint all = (counts.x + command) * 8u;
|
||||
uint second = (2u * counts.x + command) * 8u;
|
||||
for (uint value = 0u; value < 8u; ++value)
|
||||
{
|
||||
commands[all + value] = commands[field + value];
|
||||
commands[second + value] = commands[field + value];
|
||||
}
|
||||
set_instances(all, count, first);
|
||||
set_instances(second, count - drawnfirst, first + drawnfirst);
|
||||
}
|
||||
57
shaders/ogl46/instance_cull_offsets.comp
Normal file
57
shaders/ogl46/instance_cull_offsets.comp
Normal file
@@ -0,0 +1,57 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// turns the counts of instances of the entries, written by stage 0 of instance_cull.comp, into the first slots of the
|
||||
// entries in the output (cursors, which stages 1 and 2 advance as they write, and the same first slots kept for
|
||||
// instance_cull_commands.comp), and puts the total in the header.
|
||||
// a single work group: each invocation sums a run of the entries, then the sums are scanned in shared memory
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(local_size_x = INSTANCE_OFFSETS_GROUP_SIZE) in;
|
||||
|
||||
layout(std430, binding = INSTANCE_COUNTER_SSBO_BINDING) buffer instance_counter_ssbo
|
||||
{
|
||||
uint counters[];
|
||||
};
|
||||
|
||||
// x: entries
|
||||
layout(location = 0) uniform uvec4 counts;
|
||||
|
||||
shared uint sums[INSTANCE_OFFSETS_GROUP_SIZE];
|
||||
|
||||
void main()
|
||||
{
|
||||
uint entries = counts.x;
|
||||
uint invocation = gl_LocalInvocationIndex;
|
||||
uint run = (entries + INSTANCE_OFFSETS_GROUP_SIZE - 1u) / INSTANCE_OFFSETS_GROUP_SIZE;
|
||||
uint begin = min(invocation * run, entries);
|
||||
uint end = min(begin + run, entries);
|
||||
|
||||
uint sum = 0u;
|
||||
for (uint entry = begin; entry < end; ++entry)
|
||||
sum += counters[INSTANCE_COUNTER_HEADER + entry];
|
||||
sums[invocation] = sum;
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
|
||||
// inclusive scan of the sums
|
||||
for (uint step = 1u; step < INSTANCE_OFFSETS_GROUP_SIZE; step *= 2u)
|
||||
{
|
||||
uint added = (invocation >= step ? sums[invocation - step] : 0u);
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
sums[invocation] += added;
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
}
|
||||
|
||||
uint offset = sums[invocation] - sum;
|
||||
for (uint entry = begin; entry < end; ++entry)
|
||||
{
|
||||
counters[INSTANCE_COUNTER_HEADER + entries + entry] = offset;
|
||||
counters[INSTANCE_COUNTER_HEADER + 2u * entries + entry] = offset;
|
||||
offset += counters[INSTANCE_COUNTER_HEADER + entry];
|
||||
}
|
||||
if (invocation == INSTANCE_OFFSETS_GROUP_SIZE - 1u)
|
||||
counters[1] = sums[invocation];
|
||||
}
|
||||
801
shaders/ogl46/light_common.glsl
Normal file
801
shaders/ogl46/light_common.glsl
Normal file
@@ -0,0 +1,801 @@
|
||||
layout(binding = SHADOW_TEX) uniform sampler2DArrayShadow shadowmap;
|
||||
layout(binding = HEADLIGHT_TEX) uniform sampler2D headlightmap;
|
||||
// cube shadow maps of the lamps, see gl::LOCAL_SHADOW_TEX and opengl46_local_shadows
|
||||
layout(binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow local_shadowmap;
|
||||
// the same for the lamps of scenery models, the lights with linear 1
|
||||
layout(binding = LOCAL_SHADOW_LAMPS_TEX) uniform samplerCubeArrayShadow local_shadowmap_lamps;
|
||||
// shadow maps of the headlights of vehicles, see gl::HEADLIGHT_SHADOW_TEX and gl::scene_ubs::headlight_shadow
|
||||
layout(binding = HEADLIGHT_SHADOW_TEX) uniform sampler2DArrayShadow headlight_shadowmap;
|
||||
// depth of the opaque scene drawn before the colour, for the contact shadows (gl::scene_ubs::contact_shadow_params)
|
||||
layout(binding = SCENE_DEPTH_TEX) uniform sampler2D scene_depth;
|
||||
|
||||
#include "envmapping.glsl"
|
||||
#include "conversion.glsl"
|
||||
#include "weather_common.glsl"
|
||||
|
||||
float glossiness = 1.0;
|
||||
float metalic = 0.0;
|
||||
layout(location = 7) in vec4 UserData;
|
||||
#pragma param(weathering, 2, 3, 1, zero)
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Lighting balance tunables - tweak these to control overall scene
|
||||
// exposure without touching tonemapping.glsl.
|
||||
//
|
||||
// AMBIENT_SCALE: brightness of SHADED faces (indirect/sky term).
|
||||
// Lower -> deeper shadows, less burn under bright
|
||||
// textures. Higher -> flatter / brighter shading.
|
||||
//
|
||||
// SUN_DIFFUSE_SCALE: brightness of UNSHADED (sun-lit) faces. Lower
|
||||
// this to dim hot surfaces in direct sunlight
|
||||
// without affecting shaded areas. Was 3.5; 2.5
|
||||
// calmly fits the ACES tonemap shoulder.
|
||||
//
|
||||
// SUN_NDOTL_SHARPNESS: N.L curve on the sun. 1.0 = pure Lambert, higher
|
||||
// = sharper terminator (more contrast between
|
||||
// lit and shaded faces of the same surface).
|
||||
// ---------------------------------------------------------------------
|
||||
const float AMBIENT_SCALE = 0.65;
|
||||
const float SUN_DIFFUSE_SCALE = 1.5;
|
||||
const float SUN_NDOTL_SHARPNESS = 1.25;
|
||||
|
||||
float length2(vec3 v)
|
||||
{
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
// how many more lamps and headlights the fragment traces contact shadows to, see add_headlights()
|
||||
int contact_shadow_budget = 0;
|
||||
|
||||
// contact shadow of a light in direction Light_dir (view space, towards the light), 0..1: whether the depth of the scene drawn
|
||||
// before the colour (scene_depth) holds a surface between the fragment and a point up to Length metres towards the light. it
|
||||
// catches what the shadow maps are too coarse for (rails on the sleepers, bolts, edges of panels, where a thing stands on the
|
||||
// ground), and things too small or near for them. only what is in the depth casts it, and only within the image.
|
||||
// the ray starts a pixel and a half off the visible side of the surface: the depth there, sampled anywhere within a pixel of
|
||||
// the projected point, then stays behind the ray on a flat surface, so it doesn't shadow itself. a surface nearer than the
|
||||
// ray by more than the thickness is taken for something in front of the ray, not something it goes through.
|
||||
// the shadow fades over the last part of the ray (it doesn't end with an edge) and over the last quarter of the range
|
||||
float contact_shadow(vec3 light_dir, float raylength, float steps)
|
||||
{
|
||||
float range = contact_shadow_params.y;
|
||||
vec3 position = f_pos.xyz;
|
||||
float fade = 1.0 - smoothstep(range * 0.75, range, length(position));
|
||||
if ((fade <= 0.0) || (raylength <= 0.0) || (steps < 1.0))
|
||||
return 0.0;
|
||||
vec3 normal = normalize(f_normal);
|
||||
if (dot(normal, position) > 0.0)
|
||||
normal = -normal;
|
||||
if (dot(normal, light_dir) <= 0.0)
|
||||
return 0.0;
|
||||
vec2 size = vec2(textureSize(scene_depth, 0));
|
||||
// the projection is perspective with the depth n / w (reverse z, infinite far plane): w is the distance along the axis
|
||||
// of the view, and a depth d is a surface at n / d
|
||||
float near = projection[3][2];
|
||||
float distance = (projection * vec4(position, 1.0)).w;
|
||||
float pixel = 2.0 * distance / (abs(projection[1][1]) * size.y);
|
||||
vec4 start = projection * vec4(position + normal * (pixel * 1.5), 1.0);
|
||||
vec4 ray = projection * vec4(light_dir * raylength, 0.0);
|
||||
float thickness = 0.3 + pixel * 4.0;
|
||||
float bias = pixel * 0.5;
|
||||
// a different start along the ray in neighbouring pixels: the steps give noise instead of bands
|
||||
float jitter = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));
|
||||
for (float i = 0.0; i < steps; i += 1.0)
|
||||
{
|
||||
float t = (i + jitter) / steps;
|
||||
vec4 clip = start + ray * t;
|
||||
if (clip.w <= near)
|
||||
break;
|
||||
vec2 coords = (clip.xy / clip.w) * 0.5 + 0.5;
|
||||
if (any(lessThan(coords, vec2(0.0))) || any(greaterThan(coords, vec2(1.0))))
|
||||
break;
|
||||
float depth = texelFetch(scene_depth, min(ivec2(coords * size), ivec2(size) - 1), 0).r;
|
||||
// nothing drawn there (the sky)
|
||||
if (depth <= 0.0)
|
||||
continue;
|
||||
float nearer = clip.w - near / depth;
|
||||
if ((nearer > bias) && (nearer < thickness))
|
||||
return fade * (1.0 - smoothstep(0.6, 1.0, t));
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// contact shadow of a lamp or headlight Distance metres away, whose light reaches the fragment with Strength, for the first
|
||||
// few lamps of a fragment (contact_shadow_lamps.x) which light it noticeably. the ray goes at most half way to the light,
|
||||
// so the lamp doesn't shadow with its own fitting
|
||||
float contact_shadow_lamp(vec3 light_dir, float distance, float strength)
|
||||
{
|
||||
if ((contact_shadow_budget <= 0) || (strength < 0.005))
|
||||
return 0.0;
|
||||
--contact_shadow_budget;
|
||||
return contact_shadow(light_dir, min(contact_shadow_params.x, distance * 0.5), contact_shadow_params.w);
|
||||
}
|
||||
|
||||
// share of the sun (the moon) held back before the fragment by the shadow map and the contact shadow, 0..1
|
||||
float calc_shadow_map();
|
||||
float calc_shadow()
|
||||
{
|
||||
float shadow = calc_shadow_map();
|
||||
if ((contact_shadow_params.x > 0.0) && (shadow < 1.0))
|
||||
shadow = max(shadow, contact_shadow(normalize(-lights[0].dir), contact_shadow_params.x, contact_shadow_params.z));
|
||||
return shadow;
|
||||
}
|
||||
|
||||
float calc_shadow_map()
|
||||
{
|
||||
if (SHADOWMAP_ENABLED)
|
||||
{
|
||||
float distance = dot(f_pos.xyz, f_pos.xyz);
|
||||
uint cascade;
|
||||
for (cascade = 0U; cascade < MAX_CASCADES; cascade++)
|
||||
if (distance <= cascade_end[cascade])
|
||||
break;
|
||||
// cascades fitted to the depth of the view of an earlier frame (gfx.shadow.sdsm) cover only the area of the points seen
|
||||
// then: a point at the edge of the view or one which came closer since may be outside the map of its cascade. it goes to
|
||||
// the first further cascade whose map holds it with the reach of the filter, and is lit past the last one
|
||||
if (shadow_fit.x > 0.0)
|
||||
{
|
||||
float border = 4.0 / float(textureSize(shadowmap, 0).x);
|
||||
for (; cascade < MAX_CASCADES; cascade++)
|
||||
{
|
||||
vec2 fitcoords = (lightview[cascade] * vec4(f_pos.xyz, 1.0)).xy;
|
||||
if (all(greaterThanEqual(fitcoords, vec2(border))) && all(lessThanEqual(fitcoords, vec2(1.0 - border))))
|
||||
break;
|
||||
}
|
||||
if (cascade >= MAX_CASCADES)
|
||||
return 0.0;
|
||||
}
|
||||
// Normal offset: sample the shadow map from a point pushed along the surface normal
|
||||
// by a fraction of the cascade's texel footprint. A constant depth bias can't keep up
|
||||
// with large texels (far cascades, small shadow maps) on surfaces at a grazing angle
|
||||
// to the sun, which then show shadow acne stripes. lightview maps view space metres
|
||||
// to [0,1] shadow map coordinates, so its x/y row lengths give texels per metre.
|
||||
vec3 shadownormal = normalize(f_normal);
|
||||
float shadowNdotL = clamp(dot(shadownormal, normalize(-lights[0].dir)), 0.0, 1.0);
|
||||
mat4 shadowview = lightview[cascade];
|
||||
float texelspermetre = min(length(vec3(shadowview[0][0], shadowview[1][0], shadowview[2][0])),
|
||||
length(vec3(shadowview[0][1], shadowview[1][1], shadowview[2][1]))) * float(textureSize(shadowmap, 0).x);
|
||||
float texelsize = 1.0 / texelspermetre;
|
||||
vec3 shadowpos = f_pos.xyz + shadownormal * (texelsize * 1.5 * (1.0 - shadowNdotL));
|
||||
vec4 lightpos = shadowview * vec4(shadowpos, 1.0);
|
||||
vec3 coords = lightpos.xyz / lightpos.w;
|
||||
if (coords.z < 0.0)
|
||||
return 0.0f;
|
||||
|
||||
|
||||
|
||||
float bias = 0.00005f * float(cascade + 1U);
|
||||
|
||||
// Fast path: 4 textureGather() calls cover a 4x4 texel block. Raw gathered
|
||||
// comparisons must be weighted by the sub-texel position (bilinear weights),
|
||||
// otherwise the result is constant across each shadow map texel and shadow
|
||||
// edges turn into texel-sized stair steps. With per-axis weights
|
||||
// (1-f, 1, 1, f) this equals a 3x3 grid of hardware bilinear PCF taps.
|
||||
vec2 size = vec2(textureSize(shadowmap, 0).xy);
|
||||
vec2 uv = coords.xy * size - 0.5;
|
||||
vec2 base = floor(uv);
|
||||
vec2 f = uv - base;
|
||||
float refz = coords.z + bias;
|
||||
float layer = float(cascade);
|
||||
// sampling at texel corners selects blocks with lower-left texel base-1 and base+1
|
||||
vec4 g00 = textureGather(shadowmap, vec3((base + vec2(0.0, 0.0)) / size, layer), refz);
|
||||
vec4 g10 = textureGather(shadowmap, vec3((base + vec2(2.0, 0.0)) / size, layer), refz);
|
||||
vec4 g01 = textureGather(shadowmap, vec3((base + vec2(0.0, 2.0)) / size, layer), refz);
|
||||
vec4 g11 = textureGather(shadowmap, vec3((base + vec2(2.0, 2.0)) / size, layer), refz);
|
||||
// gather component order: x=(0,1) y=(1,1) z=(1,0) w=(0,0)
|
||||
float shadow = dot(g00, vec4(1.0 - f.x, 1.0, 1.0 - f.y, (1.0 - f.x) * (1.0 - f.y)))
|
||||
+ dot(g10, vec4(1.0, f.x, f.x * (1.0 - f.y), 1.0 - f.y))
|
||||
+ dot(g01, vec4((1.0 - f.x) * f.y, f.y, 1.0, 1.0 - f.x))
|
||||
+ dot(g11, vec4(f.y, f.x * f.y, f.x, 1.0));
|
||||
return shadow / 9.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// GGX Microfacet BRDF helpers (Cook-Torrance)
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// Trowbridge-Reitz (GGX) Normal Distribution Function
|
||||
// D(N,H,α) = α⁴ / (π · ((NdotH)²·(α⁴−1)+1)²)
|
||||
// α = roughness² (perceptual remapping so the slider feels linear)
|
||||
float D_GGX(float NdotH, float roughness)
|
||||
{
|
||||
float a = roughness * roughness; // perceptual -> linear roughness
|
||||
float a2 = a * a;
|
||||
float d = (NdotH * NdotH) * (a2 - 1.0) + 1.0;
|
||||
return a2 / (3.14159265359 * d * d);
|
||||
}
|
||||
|
||||
// Schlick-GGX single-term masking/shadowing (k remapped for direct lighting)
|
||||
float G_SchlickGGX(float NdotX, float roughness)
|
||||
{
|
||||
float r = roughness + 1.0;
|
||||
float k = (r * r) * (1.0 / 8.0); // k_direct = (roughness+1)²/8
|
||||
return NdotX / (NdotX * (1.0 - k) + k);
|
||||
}
|
||||
|
||||
// Separable Smith geometry term (not height-correlated)
|
||||
// G(N,V,L) = G_SchlickGGX(NdotV) · G_SchlickGGX(NdotL)
|
||||
float G_Smith(float NdotV, float NdotL, float roughness)
|
||||
{
|
||||
return G_SchlickGGX(NdotV, roughness) * G_SchlickGGX(NdotL, roughness);
|
||||
}
|
||||
|
||||
const float BRDF_PI = 3.14159265359;
|
||||
// the smoothest surface the lights are reflected by: a mirror would make a highlight smaller than a pixel
|
||||
const float MIN_ROUGHNESS = 0.045;
|
||||
// cap of the brdf for the lamps and headlights (see specular_light()): a tiny light next to a polished surface
|
||||
const float LOCAL_LIGHT_BRDF_LIMIT = 16.0;
|
||||
// no cap for the sun
|
||||
const float SUN_BRDF_LIMIT = 1.0e6;
|
||||
|
||||
// the specular reflectance at normal incidence and the perceptual roughness the lights are reflected with, worked out by
|
||||
// apply_lights() for the fragment before it adds the lamps (add_local_lights())
|
||||
vec3 spec_f0 = vec3(0.04);
|
||||
float spec_roughness = 0.5;
|
||||
|
||||
// a matte material (mat_default) reflects nothing: neither the surroundings nor the sun and the lamps, only the diffuse light
|
||||
bool matte = false;
|
||||
|
||||
// light of a light from Light_dir reflected by the surface, in the units of the diffuse shading (a white lambertian surface
|
||||
// facing the light reflects 1 of it): pi times the cook-torrance brdf with schlick's fresnel of F0, times n.l. Limit caps the
|
||||
// brdf without the n.l
|
||||
vec3 specular_light(vec3 light_dir, vec3 fragnormal, vec3 f0, float roughness, float limit)
|
||||
{
|
||||
float NdotL = dot(fragnormal, light_dir);
|
||||
if (NdotL <= 0.0)
|
||||
return vec3(0.0);
|
||||
vec3 V = normalize(-f_pos.xyz);
|
||||
vec3 H = normalize(light_dir + V);
|
||||
float NdotV = max(dot(fragnormal, V), 1e-4);
|
||||
float NdotH = max(dot(fragnormal, H), 0.0);
|
||||
float VdotH = max(dot(V, H), 0.0);
|
||||
float brdf = D_GGX(NdotH, roughness) * G_Smith(NdotV, NdotL, roughness) / (4.0 * NdotL * NdotV);
|
||||
vec3 fresnel = f0 + (1.0 - f0) * pow(1.0 - VdotH, 5.0);
|
||||
return fresnel * (min(brdf, limit) * NdotL * BRDF_PI);
|
||||
}
|
||||
|
||||
// perceptual roughness raised where the normal changes much from a pixel to the next (normal maps, tight curves), so the
|
||||
// highlight of a detail smaller than a pixel spreads over the pixel instead of flickering. Kaplanyan, Tokuyoshi, "Improved
|
||||
// geometric specular antialiasing", 2019
|
||||
float specular_antialiasing(vec3 fragnormal, float roughness)
|
||||
{
|
||||
vec3 dndx = dFdx(fragnormal);
|
||||
vec3 dndy = dFdy(fragnormal);
|
||||
float variance = 0.25 * (dot(dndx, dndx) + dot(dndy, dndy));
|
||||
float kernel = min(2.0 * variance, 0.18);
|
||||
float a2 = roughness * roughness * roughness * roughness;
|
||||
return sqrt(sqrt(clamp(a2 + kernel, 0.0, 1.0)));
|
||||
}
|
||||
|
||||
// the materials without a specgloss map give glossiness: (param[1].w, 10 by default) as the exponent of the blinn-phong
|
||||
// highlight of the old renderer. a lobe of exponent n is about a ggx one of alpha sqrt(2 / (n + 2))
|
||||
float phong_roughness(float exponent)
|
||||
{
|
||||
return sqrt(sqrt(2.0 / (max(exponent, 0.0) + 2.0)));
|
||||
}
|
||||
|
||||
// glossiness which apply_lights() turns into the roughness of a blinn-phong exponent (it takes 1 - glossiness / param[1].w)
|
||||
float phong_glossiness(float exponent)
|
||||
{
|
||||
return (1.0 - phong_roughness(exponent)) * max(abs(param[1].w), 1.0);
|
||||
}
|
||||
|
||||
// the reflection of the surroundings of a material without a specgloss map is a smooth varnish over the rough surface: the
|
||||
// old renderer showed it as a sharp reflection, as much of it as reflection: (and the masks of the material) asked for
|
||||
bool legacy_coat = false;
|
||||
|
||||
// set up the gloss of a material without a specgloss map, before apply_lights()
|
||||
void legacy_gloss()
|
||||
{
|
||||
glossiness = phong_glossiness(abs(param[1].w));
|
||||
legacy_coat = true;
|
||||
}
|
||||
|
||||
// Returns vec2(diffuse, specular) for a single punctual light.
|
||||
//
|
||||
// diffuse – Lambert N·L (Fresnel-weighted diffuse is handled per-material
|
||||
// in apply_lights, so we return raw N·L here).
|
||||
// specular – Cook-Torrance GGX: D·G / (4·NdotL·NdotV).
|
||||
// The Fresnel factor (F) is intentionally omitted here;
|
||||
// apply_lights already carries a per-material Fresnel term
|
||||
// that is applied to env reflections and can be routed to
|
||||
// direct specular there.
|
||||
//
|
||||
// Roughness is derived identically to env_roughness in apply_lights so
|
||||
// that direct and indirect specular highlights read consistently.
|
||||
vec2 calc_light(vec3 light_dir, vec3 fragnormal)
|
||||
{
|
||||
vec3 N = fragnormal;
|
||||
vec3 L = light_dir;
|
||||
vec3 V = normalize(-f_pos.xyz);
|
||||
vec3 H = normalize(L + V);
|
||||
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float NdotV = max(dot(N, V), 1e-4);
|
||||
float NdotH = max(dot(N, H), 0.0);
|
||||
|
||||
float diffuse_v = NdotL;
|
||||
|
||||
// Mirror the env-map roughness derivation so direct and indirect lobes match.
|
||||
// glossiness == param[1].w → roughness == 0.04 (near-mirror)
|
||||
// glossiness == 0 → roughness == 1.0 (fully diffuse)
|
||||
float roughness = clamp(1.0 - glossiness / max(abs(param[1].w), 1.0), 0.04, 1.0);
|
||||
|
||||
// Cook-Torrance specular (no Fresnel — see above):
|
||||
// f_spec = D(N,H,α) · G(N,V,L,α) / (4 · NdotL · NdotV)
|
||||
float D = D_GGX(NdotH, roughness);
|
||||
float G = G_Smith(NdotV, NdotL, roughness);
|
||||
float specular_v = (NdotL > 0.0)
|
||||
? (D * G) / max(4.0 * NdotL * NdotV, 1e-4)
|
||||
: 0.0;
|
||||
|
||||
return vec2(diffuse_v, specular_v);
|
||||
}
|
||||
|
||||
vec2 calc_point_light(light_s light, vec3 fragnormal)
|
||||
{
|
||||
vec3 light_dir = normalize(light.pos - f_pos.xyz);
|
||||
vec2 val = calc_light(light_dir, fragnormal);
|
||||
val.x += light.ambient;
|
||||
val *= light.intensity;
|
||||
|
||||
float distance = length(light.pos - f_pos.xyz);
|
||||
float atten = 1.0f / (distance * distance);
|
||||
//float atten = 1.0f / (1.0f + light.linear * distance + light.quadratic * (distance * distance));
|
||||
|
||||
return val * atten;
|
||||
}
|
||||
|
||||
vec2 calc_spot_light(light_s light, vec3 fragnormal)
|
||||
{
|
||||
vec3 light_dir = normalize(light.pos - f_pos.xyz);
|
||||
|
||||
float theta = dot(light_dir, normalize(-light.dir));
|
||||
float epsilon = light.in_cutoff - light.out_cutoff;
|
||||
float intensity = clamp((theta - light.out_cutoff) / epsilon, 0.0, 1.0);
|
||||
|
||||
vec2 point = calc_point_light(light, fragnormal);
|
||||
return point * intensity;
|
||||
}
|
||||
|
||||
vec2 calc_dir_light(light_s light, vec3 fragnormal)
|
||||
{
|
||||
vec3 light_dir = normalize(-light.dir);
|
||||
return calc_light(light_dir, fragnormal);
|
||||
}
|
||||
|
||||
// samples of the soft shadows of the lamps and headlights: a disc, rotated per pixel
|
||||
const vec2 SHADOW_DISC[8] = vec2[](vec2(-0.326, -0.406), vec2(-0.840, -0.074), vec2(-0.696, 0.457), vec2(-0.203, 0.621),
|
||||
vec2(0.962, -0.195), vec2(0.473, -0.480), vec2(0.519, 0.767), vec2(0.185, -0.893));
|
||||
|
||||
float shadow_disc_angle()
|
||||
{
|
||||
return 6.2831853 * fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));
|
||||
}
|
||||
|
||||
// share of the beam of headlights with a shadow map (Map, headlight_weights.w - 1 of the light) held back before the fragment,
|
||||
// 0..1. the map was drawn from the projector of the beam: x and y of the projection across it, z / w the depth (near plane over
|
||||
// the distance along the axis, as the reverse z of the scene), w the distance. the comparison gives 1 where the map holds a
|
||||
// value at least as large as the reference: a surface closer to the projector
|
||||
float headlight_beam_shadow(uint map, vec3 fragnormal, vec3 light_dir)
|
||||
{
|
||||
mat4 tomap = headlight_shadow[map];
|
||||
vec4 params = headlight_shadow_params[map];
|
||||
// a texel of the map is as large as this here. the point is moved along the normal by about a texel, and the reference towards
|
||||
// the projector by a part of one. only the back faces are in the map, which leaves the lit faces little to shadow themselves with
|
||||
float texel = params.x * max((tomap * vec4(f_pos.xyz, 1.0)).w, 0.0);
|
||||
float NdotL = clamp(dot(fragnormal, light_dir), 0.0, 1.0);
|
||||
vec4 position = tomap * vec4(f_pos.xyz + fragnormal * (texel * (0.5 + 1.5 * (1.0 - NdotL))), 1.0);
|
||||
if (position.w <= 0.0)
|
||||
return 0.0;
|
||||
vec2 coords = position.xy / position.w;
|
||||
if (any(lessThan(coords, vec2(0.0))) || any(greaterThan(coords, vec2(1.0))))
|
||||
return 0.0;
|
||||
// nearer to the projector than its near plane (the vehicle itself) the reference is past anything in the map
|
||||
float reference = position.z / max(position.w - texel * 1.5, 1e-3);
|
||||
float layer = float(map);
|
||||
float radius = params.y;
|
||||
if (radius <= 0.0)
|
||||
return texture(headlight_shadowmap, vec4(coords, layer, reference));
|
||||
float angle = shadow_disc_angle();
|
||||
vec2 rotation = vec2(cos(angle), sin(angle));
|
||||
float spread = radius / float(textureSize(headlight_shadowmap, 0).x);
|
||||
int count = (radius > 1.5) ? 8 : 4;
|
||||
float shadow = 0.0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
vec2 offset = vec2(SHADOW_DISC[i].x * rotation.x - SHADOW_DISC[i].y * rotation.y, SHADOW_DISC[i].x * rotation.y + SHADOW_DISC[i].y * rotation.x) * spread;
|
||||
shadow += texture(headlight_shadowmap, vec4(coords + offset, layer, reference));
|
||||
}
|
||||
return shadow / float(count);
|
||||
}
|
||||
|
||||
// dpdx and dpdy are the change of the view space position of the fragment from a pixel to the next one. the beam texture is
|
||||
// sampled with gradients worked out from them instead of the ones of the hardware: those are undefined where the fragments of
|
||||
// a quad do not go through the same lights, which is what happens at the edges of the clusters
|
||||
vec2 calc_headlights(light_s light, vec3 fragnormal, vec3 dpdx, vec3 dpdy)
|
||||
{
|
||||
vec4 headlightpos = light.headlight_projection * f_pos;
|
||||
vec3 coords = headlightpos.xyz / headlightpos.w;
|
||||
|
||||
if (coords.z > 1.0)
|
||||
return vec2(0.0);
|
||||
if (coords.z < 0.0)
|
||||
return vec2(0.0);
|
||||
// the beam is what the projection takes in. beyond its sides the texture would be clamped to its edge, and near the projector
|
||||
// (where a pixel spans many texels) its coarsest levels spill the average of the whole beam over the surroundings
|
||||
if (coords.x < 0.0 || coords.x > 1.0 || coords.y < 0.0 || coords.y > 1.0)
|
||||
return vec2(0.0);
|
||||
|
||||
vec3 light_dir = normalize(light.pos - f_pos.xyz);
|
||||
// Tighter wrap (was +0.25): faces angled away from the headlight cone
|
||||
// fall off to dark much faster, so cab/exterior surfaces read with a
|
||||
// clear directional shape instead of a flat half-lit wash.
|
||||
vec2 part = vec2(1.0) * clamp(dot(fragnormal, light_dir) + 0.10, 0.0, 1.0);
|
||||
float distance = length(light.pos - f_pos.xyz);
|
||||
float atten = 1.0f / (1.0f + light.linear * distance + light.quadratic * (distance * distance));
|
||||
atten *= mix(1.0, 0.0, clamp((coords.z - 0.998) * 500.0, 0.0, 1.0));
|
||||
vec2 beamcoords = headlightpos.xy / headlightpos.w;
|
||||
vec4 dheadlightposdx = light.headlight_projection * vec4(dpdx, 0.0);
|
||||
vec4 dheadlightposdy = light.headlight_projection * vec4(dpdy, 0.0);
|
||||
vec2 dbeamdx = (dheadlightposdx.xy - beamcoords * dheadlightposdx.w) / headlightpos.w;
|
||||
vec2 dbeamdy = (dheadlightposdy.xy - beamcoords * dheadlightposdy.w) / headlightpos.w;
|
||||
vec3 lights = textureGrad(headlightmap, beamcoords, dbeamdx, dbeamdy).rgb * light.headlight_weights.rgb;
|
||||
float lightintensity = max(max(lights.r, lights.g), lights.b);
|
||||
// headlights with a shadow map: what is in the way holds the beam back
|
||||
if ((light.headlight_weights.w > 0.5) && (lightintensity > 0.0) && (part.x > 0.0))
|
||||
lightintensity *= 1.0 - headlight_beam_shadow(uint(light.headlight_weights.w - 0.5), fragnormal, light_dir);
|
||||
if ((contact_shadow_budget > 0) && (lightintensity > 0.0) && (part.x > 0.0))
|
||||
lightintensity *= 1.0 - contact_shadow_lamp(light_dir, distance,
|
||||
part.x * atten * lightintensity * light.intensity * max(light.color.r, max(light.color.g, light.color.b)));
|
||||
// x the diffuse light, y the light the specular reflection takes (specular_light() brings its own n.l)
|
||||
return vec2(part.x, 1.0) * (atten * lightintensity);
|
||||
}
|
||||
|
||||
// closest distance (squared) the light of a lamp is computed for: nearer the inverse square would grow without a limit
|
||||
const float LOCAL_LIGHT_MIN_DISTANCE2 = 0.01;
|
||||
|
||||
// one comparison with the cube shadow map at the point of the space of the map, moved towards the lamp by Bias (metres). the
|
||||
// map is in the array of the scenery lamps when Lamps, in the one of the cab lamp otherwise
|
||||
float local_light_shadow_tap(vec3 position, float layer, float near, float bias, bool lamps)
|
||||
{
|
||||
vec3 axes = abs(position);
|
||||
float major = max(axes.x, max(axes.y, axes.z));
|
||||
float reference = near / max(major - bias, near);
|
||||
return lamps ? texture(local_shadowmap_lamps, vec4(position, layer), reference) : texture(local_shadowmap, vec4(position, layer), reference);
|
||||
}
|
||||
|
||||
// share of the light of a lamp with a cube shadow map (headlight_weights.x is its layer) which reaches the fragment, 0..1.
|
||||
// the map holds n / d of the closest surface, d the distance along the axis of the face, n the near plane: the farther, the
|
||||
// smaller (as the depth of the scene, reverse z). the comparison gives 1 where the map holds a value at least as large as
|
||||
// the reference, i.e. a surface at most as far: in shadow
|
||||
float local_light_shadow(light_s light, vec3 fragnormal)
|
||||
{
|
||||
// the space of the map: origin at the lamp, axes of the model the shadows are cast by
|
||||
vec3 position = (light.headlight_projection * vec4(f_pos.xyz, 1.0)).xyz;
|
||||
vec3 normal = normalize(mat3(light.headlight_projection) * fragnormal);
|
||||
float distance = length(position);
|
||||
// a texel of the map is as large as this here. the point is moved along the normal by about a texel, which keeps a surface
|
||||
// from shadowing itself at grazing angles, and the reference is moved towards the lamp by a part of a texel
|
||||
float texel = light.headlight_weights.z * distance;
|
||||
vec3 tolamp = -position / max(distance, 1e-4);
|
||||
float NdotL = clamp(dot(normal, tolamp), 0.0, 1.0);
|
||||
position += normal * (texel * (0.5 + 1.5 * (1.0 - NdotL)));
|
||||
float bias = texel * 1.5;
|
||||
float layer = light.headlight_weights.x;
|
||||
float radius = light.headlight_weights.w;
|
||||
bool lamps = (light.linear > 0.5);
|
||||
if (radius <= 0.0)
|
||||
return local_light_shadow_tap(position, layer, light.headlight_weights.y, bias, lamps);
|
||||
// a disc of samples in the plane of the surface, each compared with its own distance: points of the surface itself, which
|
||||
// samples spread across the direction to the lamp would not be at grazing angles. rotated per pixel to turn the banding into noise
|
||||
vec3 side = normalize(cross(normal, (abs(normal.y) < 0.99) ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0)));
|
||||
vec3 up = cross(normal, side);
|
||||
float angle = shadow_disc_angle();
|
||||
vec2 rotation = vec2(cos(angle), sin(angle));
|
||||
float spread = texel * radius;
|
||||
int count = (radius > 1.5) ? 8 : 4;
|
||||
float shadow = 0.0;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
vec2 offset = vec2(SHADOW_DISC[i].x * rotation.x - SHADOW_DISC[i].y * rotation.y, SHADOW_DISC[i].x * rotation.y + SHADOW_DISC[i].y * rotation.x) * spread;
|
||||
shadow += local_light_shadow_tap(position + side * offset.x + up * offset.y, layer, light.headlight_weights.y, bias, lamps);
|
||||
}
|
||||
return shadow / float(count);
|
||||
}
|
||||
|
||||
// light of a lamp (point or spot light: the lamps of models and vehicles, the cab lamp) reaching the fragment, x the diffuse
|
||||
// part and y the light the specular reflection takes (specular_light() brings its own n.l). it falls off with the square of the distance, and smoothly to nothing at the range of the light,
|
||||
// 1 / sqrt(quadratic), past which the culling of the clusters does not take it. a spot light fades from its inner cone to the
|
||||
// outer one. a lamp with a shadow map is held back by what is between it and the fragment
|
||||
vec2 calc_local_light(light_s light, vec3 fragnormal)
|
||||
{
|
||||
vec3 tolight = light.pos - f_pos.xyz;
|
||||
float distance2 = dot(tolight, tolight);
|
||||
vec3 light_dir = tolight * inversesqrt(max(distance2, 1e-8));
|
||||
float edge = clamp(1.0 - distance2 * distance2 * (light.quadratic * light.quadratic), 0.0, 1.0);
|
||||
float atten = edge * edge / max(distance2, LOCAL_LIGHT_MIN_DISTANCE2);
|
||||
if (light.type == LIGHT_SPOT)
|
||||
atten *= smoothstep(light.out_cutoff, light.in_cutoff, dot(-light_dir, normalize(light.dir)));
|
||||
if ((atten > 0.0) && (light.headlight_weights.x >= 0.0) && (dot(fragnormal, light_dir) > 0.0))
|
||||
atten *= 1.0 - local_light_shadow(light, fragnormal);
|
||||
if ((atten > 0.0) && (contact_shadow_budget > 0))
|
||||
atten *= 1.0 - contact_shadow_lamp(light_dir, sqrt(distance2),
|
||||
atten * max(dot(fragnormal, light_dir), 0.0) * light.intensity * max(light.color.r, max(light.color.g, light.color.b)));
|
||||
if (atten <= 0.0)
|
||||
return vec2(0.0);
|
||||
return vec2(max(dot(fragnormal, light_dir), 0.0), 1.0) * atten;
|
||||
}
|
||||
|
||||
// index of the light cluster the fragment is in, see gl/cluster_grid.h: its screen tile and slice of depth. the depth is the clip
|
||||
// space w, which is 1 / gl_FragCoord.w. a tile is kept in the grid, as the far edge of the render target and rounding
|
||||
// right below it give one past the last
|
||||
uint fragment_cluster()
|
||||
{
|
||||
ivec2 tile = ivec2(gl_FragCoord.xy * (vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y) / cluster_viewport_size));
|
||||
tile = clamp(tile, ivec2(0), ivec2(CLUSTER_GRID_X - 1, CLUSTER_GRID_Y - 1));
|
||||
int slice = clamp(int(floor(cluster_slice_bias - log(gl_FragCoord.w) * cluster_slice_scale)), 0, CLUSTER_GRID_Z - 1);
|
||||
return uint(tile.x + CLUSTER_GRID_X * (tile.y + CLUSTER_GRID_Y * slice));
|
||||
}
|
||||
|
||||
// adds the light of a headlight or lamp to the diffuse and the specular light of the fragment
|
||||
void add_local_light(light_s light, vec2 part, vec3 fragnormal, inout vec3 diffuse, inout vec3 specular)
|
||||
{
|
||||
if (part.y <= 0.0)
|
||||
return;
|
||||
vec3 color = light.color * light.intensity;
|
||||
diffuse += color * part.x;
|
||||
if (matte)
|
||||
return;
|
||||
specular += color * (part.y * param[1].y) * specular_light(normalize(light.pos - f_pos.xyz), fragnormal, spec_f0, spec_roughness, LOCAL_LIGHT_BRDF_LIMIT);
|
||||
}
|
||||
|
||||
// adds the light of the headlights and lamps which reach the fragment to its diffuse and specular light. these are the ones in
|
||||
// the list of its cluster when the lights are clustered (the sun is not on the list, the callers take care of it), and the
|
||||
// first lights_count - 1 of lights[] otherwise, which are headlights only. the specular light is reflected with spec_f0 and
|
||||
// spec_roughness, and is already coloured by the fresnel term
|
||||
void add_local_lights(vec3 fragnormal, inout vec3 diffuse, inout vec3 specular)
|
||||
{
|
||||
// worked out before the loops, which the fragments of a quad do not leave together
|
||||
vec3 dpdx = dFdx(f_pos.xyz);
|
||||
vec3 dpdy = dFdy(f_pos.xyz);
|
||||
contact_shadow_budget = (contact_shadow_params.x > 0.0) ? int(contact_shadow_lamps.x) : 0;
|
||||
|
||||
if (lights_clustered != 0U)
|
||||
{
|
||||
cluster_range_s range = cluster_ranges[fragment_cluster()];
|
||||
for (uint i = 0U; i < range.count; i++)
|
||||
{
|
||||
light_s light = lights[cluster_light_indices[range.offset + i]];
|
||||
vec2 part = (light.type == LIGHT_HEADLIGHTS) ? calc_headlights(light, fragnormal, dpdx, dpdy) : calc_local_light(light, fragnormal);
|
||||
add_local_light(light, part, fragnormal, diffuse, specular);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint i = 1U; i < lights_count; i++)
|
||||
{
|
||||
light_s light = lights[i];
|
||||
add_local_light(light, calc_headlights(light, fragnormal, dpdx, dpdy), fragnormal, diffuse, specular);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the light of the headlights and lamps added to Fragcolor, diffuse and specular together (for shaders which don't tell
|
||||
// the albedo from the reflection)
|
||||
vec3 add_headlights(vec3 fragcolor, vec3 fragnormal)
|
||||
{
|
||||
vec3 diffuse = vec3(0.0);
|
||||
vec3 specular = vec3(0.0);
|
||||
add_local_lights(fragnormal, diffuse, specular);
|
||||
return fragcolor + diffuse + specular;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Split-sum environment BRDF (Karis / UE4 analytic approximation).
|
||||
//
|
||||
// This is the missing piece that made matte specgloss surfaces "shine like
|
||||
// crazy": previously the env reflection was added at full strength
|
||||
// (envcolor * fresnel * reflectivity) and roughness only blurred the mip,
|
||||
// never dimmed the energy. A rough surface therefore mirrored the bright
|
||||
// sky just as strongly as a polished one.
|
||||
//
|
||||
// EnvBRDFApprox returns the pre-integrated specular scale (the "DFG" term)
|
||||
// for a given F0, roughness and view angle. For rough surfaces it collapses
|
||||
// toward ~0, so low-glossiness materials reflect almost nothing — matching
|
||||
// what Substance 3D Painter shows. Polished surfaces keep their full
|
||||
// reflection, and the grazing-angle Fresnel edge is preserved.
|
||||
// roughness 1.0 (matte) -> scale ~0.015 (virtually no reflection)
|
||||
// roughness 0.0 (mirror) -> scale ~F0..1 (full reflection + Fresnel rim)
|
||||
vec3 EnvBRDFApprox(vec3 F0, float roughness, float NoV)
|
||||
{
|
||||
const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022);
|
||||
const vec4 c1 = vec4( 1.0, 0.0425, 1.040, -0.040);
|
||||
vec4 r = roughness * c0 + c1;
|
||||
float a004 = min(r.x * r.x, exp2(-9.28 * NoV)) * r.x + r.y;
|
||||
vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw;
|
||||
return F0 * AB.x + AB.y;
|
||||
}
|
||||
|
||||
// Diffuse colour of the submodel (t3d/e3d) or shape, used as the albedo multiplier:
|
||||
// untextured materials take it as their colour, textured ones multiply the base texture by it
|
||||
// (same as the legacy renderer's GL_MODULATE). param[0].rgb holds the authored sRGB colour,
|
||||
// param[1].x its intensity (HSV value unless a material overrides it).
|
||||
vec3 material_diffuse()
|
||||
{
|
||||
vec3 diffusecolor = pow(max(param[0].rgb, vec3(0.0)), vec3(2.2));
|
||||
float value = max(param[0].r, max(param[0].g, param[0].b));
|
||||
return (value > 0.0 ? diffusecolor * (param[1].x / value) : vec3(0.0));
|
||||
}
|
||||
|
||||
// directional part of the sky ambient light (sky_ssbo, bands 1 and 2 of the spherical harmonics) for a world space direction
|
||||
vec3 sky_ambient_directional(vec3 n)
|
||||
{
|
||||
return sky_ambient_sh[1].rgb * (0.488603 * n.y)
|
||||
+ sky_ambient_sh[2].rgb * (0.488603 * n.z)
|
||||
+ sky_ambient_sh[3].rgb * (0.488603 * n.x)
|
||||
+ sky_ambient_sh[4].rgb * (1.092548 * n.x * n.y)
|
||||
+ sky_ambient_sh[5].rgb * (1.092548 * n.y * n.z)
|
||||
+ sky_ambient_sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0))
|
||||
+ sky_ambient_sh[7].rgb * (1.092548 * n.x * n.z)
|
||||
+ sky_ambient_sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y));
|
||||
}
|
||||
|
||||
// adds the directional part of the sky light to the ambient light the material starts from (the uniform ambient, or a share
|
||||
// of it, like the water takes). the same share of the directional part is added, so with the directional part at zero
|
||||
// (gfx.ambient.sh off) the result is what it always was
|
||||
vec3 add_sky_ambient(vec3 basecolor, vec3 fragnormal)
|
||||
{
|
||||
if (sky_ambient_factor == 0.0)
|
||||
return basecolor;
|
||||
float share = dot(basecolor, vec3(1.0)) / max(dot(ambient, vec3(1.0)), 1e-4);
|
||||
vec3 worldnormal = normalize(mat3(inv_view) * fragnormal);
|
||||
return max(basecolor + sky_ambient_directional(worldnormal) * (sky_ambient_factor * share), vec3(0.0));
|
||||
}
|
||||
|
||||
// lights the surface: the ambient light, the sun (the moon), the headlights and lamps and the reflection of the surroundings.
|
||||
// the units are the ones of the ambient light, the irradiance over pi: a white lambertian surface lit by 1 of it shows 1.
|
||||
// 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
|
||||
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
|
||||
texturecolor *= material_diffuse();
|
||||
float polished = 0.0;
|
||||
if (sunless)
|
||||
{
|
||||
// Cab interior: dim ambient less than the exterior path - ambient
|
||||
// is the dominant indoor illumination, but it was still too bright.
|
||||
fragcolor *= 0.80;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the sky lights the faces turned up and towards the bright part of the sky more than the ones turned to the ground
|
||||
fragcolor = add_sky_ambient(fragcolor, fragnormal);
|
||||
#ifndef NO_WEATHERING
|
||||
float weathered = (param[2].w > 0.0 ? clamp(param[2].w * UserData.x, 0.0, 1.0) : 0.0);
|
||||
float tinted = (param[2].w < 0.0 ? clamp(-param[2].w * UserData.x, 0.0, 1.0) : 0.0);
|
||||
// a tint only scales the colour of the texture, the steel keeps its own polish
|
||||
polished = weathered * smoothstep(0.03, 0.12, dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)));
|
||||
float weatheredluma = min(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 0.05);
|
||||
vec3 unused = texturecolor * (weatheredluma / max(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 1e-4));
|
||||
unused = mix(unused, vec3(1.0, 0.82, 0.74) * (weatheredluma / 0.8525), 0.5);
|
||||
texturecolor = mix(texturecolor, unused, smoothstep(0.0, 0.4, weathered));
|
||||
texturecolor = (tinted > 0.0 ? texturecolor * mix(vec3(1.0), param[2].rgb, tinted) : texturecolor);
|
||||
specularity = mix(specularity, min(specularity, 0.03), smoothstep(0.0, 0.4, polished));
|
||||
metalic = mix(metalic, 0.0, polished);
|
||||
glossiness = mix(glossiness, min(glossiness, 0.06 * abs(param[1].w)), polished);
|
||||
reflectivity = mix(reflectivity, min(reflectivity, 0.08 * param[1].z), smoothstep(0.0, 0.4, polished));
|
||||
#endif
|
||||
// Scale ambient before it gets tinted by the texture.
|
||||
// Sun, headlights and emission are added afterwards so they are NOT
|
||||
// attenuated by AMBIENT_SCALE - this only dims the indirect term.
|
||||
fragcolor *= AMBIENT_SCALE;
|
||||
}
|
||||
|
||||
vec3 emissioncolor = vec3(emission);
|
||||
vec3 view_dir = normalize(-f_pos.xyz);
|
||||
float NdotV = max(dot(fragnormal, view_dir), 1e-4);
|
||||
|
||||
// glossiness == param[1].w -> mirror, glossiness == 0 -> fully rough
|
||||
float roughness = clamp(1.0 - glossiness / max(abs(param[1].w), 1.0), 0.0, 1.0);
|
||||
roughness = max(specular_antialiasing(fragnormal, roughness), MIN_ROUGHNESS);
|
||||
// metals reflect with their base colour, dielectrics with the specular level (0.5 -> 0.04)
|
||||
vec3 f0 = mix(vec3(0.08 * specularity), texturecolor, metalic);
|
||||
spec_f0 = f0;
|
||||
spec_roughness = roughness;
|
||||
|
||||
// Pre-integrated env BRDF: how much of the surroundings the surface reflects, for its F0, roughness and the view angle.
|
||||
// the surroundings are the cubemap where the material asks for it (reflectivity) and the ambient light elsewhere, so a
|
||||
// surface reflects them always, the rough one barely
|
||||
vec3 env_spec = EnvBRDFApprox(f0, roughness, NdotV);
|
||||
float env_roughness = roughness;
|
||||
vec3 cubemap_spec = env_spec;
|
||||
float cubemap_share = max(reflectivity, 0.0);
|
||||
float ambient_share = clamp(1.0 - reflectivity, 0.0, 1.0);
|
||||
if (matte)
|
||||
{
|
||||
env_spec = vec3(0.0);
|
||||
cubemap_share = 0.0;
|
||||
}
|
||||
// what the diffuse layer is left with
|
||||
vec3 diffuse_albedo = texturecolor * (1.0 - metalic) * (1.0 - env_spec);
|
||||
if (legacy_coat)
|
||||
{
|
||||
// the varnish: a smooth dielectric layer reflecting the cubemap, worn off where the material is weathered
|
||||
env_roughness = specular_antialiasing(fragnormal, roughness * smoothstep(0.0, 0.4, polished));
|
||||
cubemap_spec = EnvBRDFApprox(mix(vec3(0.04), texturecolor, metalic), env_roughness, NdotV);
|
||||
ambient_share = 1.0;
|
||||
diffuse_albedo *= 1.0 - cubemap_spec * min(cubemap_share, 1.0);
|
||||
}
|
||||
vec3 envcolor = (matte ? vec3(0.0) : envmap_color_rough(fragnormal, env_roughness));
|
||||
|
||||
if (lights_count == 0U)
|
||||
return fragcolor * diffuse_albedo
|
||||
+ emissioncolor * texturecolor
|
||||
+ fragcolor * env_spec * ambient_share
|
||||
+ envcolor * cubemap_spec * cubemap_share;
|
||||
|
||||
vec3 light_dir = normalize(-lights[0].dir);
|
||||
float NdotL = max(dot(fragnormal, light_dir), 0.0);
|
||||
// Sharpen sun N.L falloff so the lit-to-shaded terminator on cab
|
||||
// panels, vehicle bodies and terrain reads as a clear edge rather
|
||||
// than a soft Lambertian ramp. Tunable via SUN_NDOTL_SHARPNESS.
|
||||
float sun_NdotL = pow(NdotL, SUN_NDOTL_SHARPNESS);
|
||||
// the clouds hold back the sun (the moon at night) where their shadow falls
|
||||
float cloudsun = cloud_shadow(mat3(inv_view) * f_pos.xyz);
|
||||
// Sun HDR scale -> SUN_DIFFUSE_SCALE, the same for the diffuse and the specular light
|
||||
vec3 sunlight = lights[0].color * (SUN_DIFFUSE_SCALE * lights[0].intensity * cloudsun);
|
||||
|
||||
float shadow = 0.0;
|
||||
if (shadowtone < 1.0)
|
||||
shadow = clamp(calc_shadow(), 0.0, 1.0);
|
||||
|
||||
// the cubemap knows nothing of what shades the fragment: it is dimmed a little in the shadow of the sun
|
||||
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
|
||||
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));
|
||||
}
|
||||
else
|
||||
cubemap_shadow = 1.0 - (1.0 - shadowtone) * shadow * 0.5;
|
||||
|
||||
// the reflection of the surroundings, before the light of the sun and the lamps joins the ambient light
|
||||
vec3 env_reflection = fragcolor * env_spec * ambient_share
|
||||
+ envcolor * cubemap_spec * (cubemap_share * cubemap_shadow);
|
||||
|
||||
// the shadow leaves the share of the sun Shadowtone tells (the old shadow colour of the scenery) to the diffuse light,
|
||||
// nothing to the highlight
|
||||
if (!sunless)
|
||||
fragcolor += sunlight * (sun_NdotL * (1.0 - (1.0 - shadowtone) * shadow));
|
||||
vec3 specular = matte ? vec3(0.0) : sunlight * specular_light(light_dir, fragnormal, f0, roughness, SUN_BRDF_LIMIT) * (1.0 - shadow);
|
||||
|
||||
vec3 lampspecular = vec3(0.0);
|
||||
add_local_lights(fragnormal, fragcolor, lampspecular);
|
||||
|
||||
return fragcolor * diffuse_albedo // ambient, sun and lamps on the diffuse layer
|
||||
+ specular * param[1].y // highlight of the sun, fresnel coloured (metals take their base colour)
|
||||
+ lampspecular // highlights of the lamps, param[1].y already applied
|
||||
+ emissioncolor * texturecolor
|
||||
+ env_reflection;
|
||||
}
|
||||
|
||||
vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone)
|
||||
{
|
||||
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
|
||||
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);
|
||||
}
|
||||
10
shaders/ogl46/map.frag
Normal file
10
shaders/ogl46/map.frag
Normal file
@@ -0,0 +1,10 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = vec4(vec3(cascade_end), 1.0f);
|
||||
}
|
||||
16
shaders/ogl46/map.vert
Normal file
16
shaders/ogl46/map.vert
Normal file
@@ -0,0 +1,16 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
layout(location = 0) in vec3 v_vert;
|
||||
layout(location = 2) in vec2 v_coords;
|
||||
|
||||
layout(location = 19) out vec2 g_coords;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 clip_pos = projection * vec4(v_vert, 1.0f);
|
||||
|
||||
gl_Position = vec4(clip_pos.xz, 0.5, 1.0);
|
||||
g_coords = v_coords;
|
||||
}
|
||||
16
shaders/ogl46/map_poi.frag
Normal file
16
shaders/ogl46/map_poi.frag
Normal file
@@ -0,0 +1,16 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 14) in vec2 f_coords;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D tex1;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 texcoord = f_coords;
|
||||
vec4 color = texture(tex1, texcoord);
|
||||
|
||||
out_color = color;
|
||||
}
|
||||
32
shaders/ogl46/map_poi.geom
Normal file
32
shaders/ogl46/map_poi.geom
Normal file
@@ -0,0 +1,32 @@
|
||||
#version 460 core
|
||||
#define STAGE_GEOMETRY 1
|
||||
layout (points) in;
|
||||
layout (triangle_strip, max_vertices = 4) out;
|
||||
|
||||
layout(location = 19) in vec2 g_coords[];
|
||||
layout(location = 14) out vec2 f_coords;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
void main() {
|
||||
vec4 position = gl_in[0].gl_Position;
|
||||
vec2 size = vec2(0.1) * cascade_end.xy;
|
||||
|
||||
gl_Position = position + vec4(-size.x, -size.y, 0.0, 0.0);
|
||||
f_coords = vec2(g_coords[0].s, 0.0);
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = position + vec4( size.x, -size.y, 0.0, 0.0);
|
||||
f_coords = vec2(g_coords[0].t, 0.0);
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = position + vec4(-size.x, size.y, 0.0, 0.0);
|
||||
f_coords = vec2(g_coords[0].s, 1.0);
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = position + vec4( size.x, size.y, 0.0, 0.0);
|
||||
f_coords = vec2(g_coords[0].t, 1.0);
|
||||
EmitVertex();
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
65
shaders/ogl46/mat_clouds.frag
Normal file
65
shaders/ogl46/mat_clouds.frag
Normal file
@@ -0,0 +1,65 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec3 apply_lights_clouds(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor)
|
||||
{
|
||||
if(lights_count == 0U)
|
||||
return fragcolor * texturecolor;
|
||||
|
||||
vec3 light_dir = normalize(-lights[0].dir);
|
||||
vec3 view_dir = normalize(vec3(0.0) - f_pos.xyz);
|
||||
vec3 halfway_dir = normalize(light_dir + view_dir);
|
||||
float diffuse_v = max(dot(fragnormal, light_dir), 0.0);
|
||||
float diffuseamount = (diffuse_v * param[1].x) * lights[0].intensity;
|
||||
fragcolor += lights[0].color * diffuseamount;
|
||||
fragcolor *= texturecolor;
|
||||
|
||||
return fragcolor;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
tex_color.a = clamp(tex_color.a * 1.25, 0.0, 1.0);
|
||||
if(tex_color.a < 0.01)
|
||||
discard;
|
||||
|
||||
vec3 fragcolor = param[0].rgb;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
|
||||
fragcolor = apply_lights_clouds(fragcolor, fragnormal, tex_color.rgb);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a);
|
||||
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
3
shaders/ogl46/mat_colored.frag
Normal file
3
shaders/ogl46/mat_colored.frag
Normal file
@@ -0,0 +1,3 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "mat_colored.glsl"
|
||||
55
shaders/ogl46/mat_colored.glsl
Normal file
55
shaders/ogl46/mat_colored.glsl
Normal file
@@ -0,0 +1,55 @@
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
// no texture: the diffuse colour alone makes the albedo, apply_lights() applies it to the white base
|
||||
vec4 tex_color = vec4(1.0, 1.0, 1.0, param[0].a);
|
||||
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z;
|
||||
float specularity = dot(pow(param[0].rgb, vec3(2.2)), vec3(0.5));
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
3
shaders/ogl46/mat_default.frag
Normal file
3
shaders/ogl46/mat_default.frag
Normal file
@@ -0,0 +1,3 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "mat_default.glsl"
|
||||
64
shaders/ogl46/mat_default.glsl
Normal file
64
shaders/ogl46/mat_default.glsl
Normal file
@@ -0,0 +1,64 @@
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
matte = true;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, 0.0, 0.0, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
/*
|
||||
float distance = dot(f_pos.xyz, f_pos.xyz);
|
||||
if( distance <= cascade_end.x ) { color.r += 0.25; }
|
||||
else if( distance <= cascade_end.y ) { color.g += 0.25; }
|
||||
else if( distance <= cascade_end.z ) { color.b += 0.25; }
|
||||
*/
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
3
shaders/ogl46/mat_default_0.frag
Normal file
3
shaders/ogl46/mat_default_0.frag
Normal file
@@ -0,0 +1,3 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "mat_colored.glsl"
|
||||
3
shaders/ogl46/mat_default_1.frag
Normal file
3
shaders/ogl46/mat_default_1.frag
Normal file
@@ -0,0 +1,3 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "mat_default.glsl"
|
||||
3
shaders/ogl46/mat_default_2.frag
Normal file
3
shaders/ogl46/mat_default_2.frag
Normal file
@@ -0,0 +1,3 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "mat_reflmap.glsl"
|
||||
72
shaders/ogl46/mat_default_detail.frag
Normal file
72
shaders/ogl46/mat_default_detail.frag
Normal file
@@ -0,0 +1,72 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma texture(detailnormalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D detailnormalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(detailnormalmap, f_coord * param[2].x).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
72
shaders/ogl46/mat_default_specgloss.frag
Normal file
72
shaders/ogl46/mat_default_specgloss.frag
Normal file
@@ -0,0 +1,72 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(specgloss, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D specgloss;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
/*
|
||||
float distance = dot(f_pos.xyz, f_pos.xyz);
|
||||
if( distance <= cascade_end.x ) { color.r += 0.25; }
|
||||
else if( distance <= cascade_end.y ) { color.g += 0.25; }
|
||||
else if( distance <= cascade_end.z ) { color.b += 0.25; }
|
||||
*/
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
84
shaders/ogl46/mat_detail_normalmap.frag
Normal file
84
shaders/ogl46/mat_detail_normalmap.frag
Normal file
@@ -0,0 +1,84 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
#pragma param(detail_height_scale, 2, 1, 1, one)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(detailnormalmap, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D detailnormalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec4 normal_map = texture(normalmap, f_coord);
|
||||
vec4 detailnormal_map = texture(detailnormalmap, f_coord * param[2].x);
|
||||
vec3 normal;
|
||||
vec3 normaldetail;
|
||||
|
||||
normaldetail.xy = detailnormal_map.rg* 2.0 - 1.0;
|
||||
normaldetail.z = sqrt(1.0 - clamp((dot(normaldetail.xy, normaldetail.xy)), 0.0, 1.0));
|
||||
normaldetail.xyz = normaldetail.xyz * param[2].y;
|
||||
normal.xy = normal_map.rg* 2.0 - 1.0;
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z)));
|
||||
|
||||
|
||||
float reflblend = (normal_map.a + detailnormal_map.a);
|
||||
float reflectivity = reflblend > 1.0 ? param[1].z * 1.0 : param[1].z * reflblend;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
91
shaders/ogl46/mat_detail_normalmap_specgloss.frag
Normal file
91
shaders/ogl46/mat_detail_normalmap_specgloss.frag
Normal file
@@ -0,0 +1,91 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
#pragma param(detail_height_scale, 2, 1, 1, one)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(detailnormalmap, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D detailnormalmap;
|
||||
|
||||
#pragma texture(specgloss, 3, RGBA)
|
||||
layout(binding = 3) uniform sampler2D specgloss;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec4 normal_map = texture(normalmap, f_coord);
|
||||
vec4 detailnormal_map = texture(detailnormalmap, f_coord * param[2].x);
|
||||
vec4 specgloss_map = texture(specgloss, f_coord);
|
||||
vec3 normal;
|
||||
vec3 normaldetail;
|
||||
|
||||
normaldetail.xy = detailnormal_map.rg* 2.0 - 1.0;
|
||||
normaldetail.z = sqrt(1.0 - clamp((dot(normaldetail.xy, normaldetail.xy)), 0.0, 1.0));
|
||||
normaldetail.xyz = normaldetail.xyz * param[2].y;
|
||||
normal.xy = normal_map.rg* 2.0 - 1.0;
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z)));
|
||||
|
||||
float reflblend = (normal_map.a + detailnormal_map.a);
|
||||
float reflectivity = reflblend > 1.0 ? param[1].z * 1.0 : param[1].z * reflblend;
|
||||
float specularity = specgloss_map.r;
|
||||
glossiness = specgloss_map.g * abs(param[1].w);
|
||||
metalic = specgloss_map.b;
|
||||
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
147
shaders/ogl46/mat_detail_parallax.frag
Normal file
147
shaders/ogl46/mat_detail_parallax.frag
Normal file
@@ -0,0 +1,147 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn; //tangent matrix nietransponowany; mnożyć przez f_tbn dla TangentLightPos; TangentViewPos; TangentFragPos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
layout(location = 6) in vec3 TangentFragPos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
#pragma param(detail_height_scale, 2, 1, 1, one)
|
||||
#pragma param(height_scale, 2, 2, 1, zero)
|
||||
#pragma param(height_offset, 2, 3, 1, zero)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(detailnormalmap, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D detailnormalmap;
|
||||
|
||||
#define PARALLAX
|
||||
// param[2].w is the height offset, not the weathering
|
||||
#define NO_WEATHERING
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir);
|
||||
|
||||
void main()
|
||||
{
|
||||
//parallax mapping
|
||||
vec3 viewDir = normalize(-TangentFragPos); //tangent view pos - tangent frag pos
|
||||
vec2 f_coord_p = ParallaxMapping(f_coord, viewDir);
|
||||
vec4 normal_map = texture(normalmap, f_coord_p);
|
||||
vec4 detailnormal_map = texture(detailnormalmap, f_coord_p * param[2].x);
|
||||
vec4 tex_color = texture(diffuse, f_coord_p);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
vec3 normaldetail;
|
||||
|
||||
normaldetail.xy = detailnormal_map.rg* 2.0 - 1.0;
|
||||
normaldetail.z = sqrt(1.0 - clamp((dot(normaldetail.xy, normaldetail.xy)), 0.0, 1.0));
|
||||
normaldetail.xyz = normaldetail.xyz * param[2].y;
|
||||
normal.xy = normal_map.rg* 2.0 - 1.0;
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z)));
|
||||
float reflectivity = param[1].z * normal_map.a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir)
|
||||
{
|
||||
float pos_len = length(f_pos.xyz);
|
||||
// gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way
|
||||
vec2 dcoorddx = dFdx(f_coord);
|
||||
vec2 dcoorddy = dFdy(f_coord);
|
||||
|
||||
if (pos_len > 100.0) {
|
||||
return f_coord;
|
||||
}
|
||||
// the shift fades out before the distance it stops at, so it doesn't switch off at once there
|
||||
float fade = 1.0 - smoothstep(80.0, 100.0, pos_len);
|
||||
|
||||
if (EXTRAEFFECTS_ENABLED)
|
||||
{
|
||||
const float minLayers = 8.0;
|
||||
const float maxLayers = 32.0;
|
||||
float LayersWeight = pos_len / 20.0;
|
||||
vec2 currentTexCoords = f_coord;
|
||||
float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b;
|
||||
LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight);
|
||||
float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers
|
||||
float layerDepth = 1.0 / numLayers; // calculate the size of each layer
|
||||
float currentLayerDepth = 0.0; // depth of current layer
|
||||
vec2 P = viewDir.xy * param[2].z * fade; // the amount to shift the texture coordinates per layer (from vector P)
|
||||
vec2 deltaTexCoords = P / numLayers;
|
||||
|
||||
|
||||
while(currentLayerDepth < currentDepthMapValue)
|
||||
{
|
||||
currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P
|
||||
currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates
|
||||
currentLayerDepth += layerDepth; // get depth of next layer
|
||||
}
|
||||
|
||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations)
|
||||
|
||||
float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation
|
||||
float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth;
|
||||
|
||||
float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates
|
||||
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
|
||||
|
||||
return finalTexCoords;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = texture(normalmap, f_coord).b;
|
||||
vec2 p = viewDir.xy / viewDir.z * (height * (param[2].z - param[2].w) * 0.2) * fade;
|
||||
return f_coord - p;
|
||||
}
|
||||
}
|
||||
153
shaders/ogl46/mat_detail_parallax_specgloss.frag
Normal file
153
shaders/ogl46/mat_detail_parallax_specgloss.frag
Normal file
@@ -0,0 +1,153 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn; //tangent matrix nietransponowany; mnożyć przez f_tbn dla TangentLightPos; TangentViewPos; TangentFragPos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
layout(location = 6) in vec3 TangentFragPos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(detail_scale, 2, 0, 1, one)
|
||||
#pragma param(detail_height_scale, 2, 1, 1, one)
|
||||
#pragma param(height_scale, 2, 2, 1, zero)
|
||||
#pragma param(height_offset, 2, 3, 1, zero)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D specgloss;
|
||||
|
||||
#pragma texture(detailnormalmap, 3, RGBA)
|
||||
layout(binding = 3) uniform sampler2D detailnormalmap;
|
||||
|
||||
|
||||
#define PARALLAX
|
||||
// param[2].w is the height offset, not the weathering
|
||||
#define NO_WEATHERING
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir);
|
||||
|
||||
void main()
|
||||
{
|
||||
//parallax mapping
|
||||
vec3 viewDir = normalize(vec3(0.0f, 0.0f, 0.0f) - TangentFragPos); //tangent view pos - tangent frag pos
|
||||
vec2 f_coord_p = ParallaxMapping(f_coord, viewDir);
|
||||
|
||||
vec4 normal_map = texture(normalmap, f_coord_p);
|
||||
vec4 detailnormal_map = texture(detailnormalmap, f_coord_p * param[2].x);
|
||||
vec4 tex_color = texture(diffuse, f_coord_p);
|
||||
vec4 specgloss_map = texture(specgloss, f_coord_p);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
vec3 normaldetail;
|
||||
|
||||
normaldetail.xy = detailnormal_map.rg* 2.0 - 1.0;
|
||||
normaldetail.z = sqrt(1.0 - clamp((dot(normaldetail.xy, normaldetail.xy)), 0.0, 1.0));
|
||||
normaldetail.xyz = normaldetail.xyz * param[2].y;
|
||||
normal.xy = normal_map.rg* 2.0 - 1.0;
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z)));
|
||||
float reflectivity = param[1].z * normal_map.a;
|
||||
float specularity = specgloss_map.r;
|
||||
glossiness = specgloss_map.g * abs(param[1].w);
|
||||
metalic = specgloss_map.b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir)
|
||||
{
|
||||
|
||||
float pos_len = length(f_pos.xyz);
|
||||
// gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way
|
||||
vec2 dcoorddx = dFdx(f_coord);
|
||||
vec2 dcoorddy = dFdy(f_coord);
|
||||
|
||||
if (pos_len > 100.0) {
|
||||
return f_coord;
|
||||
}
|
||||
// the shift fades out before the distance it stops at, so it doesn't switch off at once there
|
||||
float fade = 1.0 - smoothstep(80.0, 100.0, pos_len);
|
||||
|
||||
if (EXTRAEFFECTS_ENABLED)
|
||||
{
|
||||
const float minLayers = 8.0;
|
||||
const float maxLayers = 32.0;
|
||||
float LayersWeight = pos_len / 20.0;
|
||||
vec2 currentTexCoords = f_coord;
|
||||
float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b;
|
||||
LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight);
|
||||
float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers
|
||||
float layerDepth = 1.0 / numLayers; // calculate the size of each layer
|
||||
float currentLayerDepth = 0.0; // depth of current layer
|
||||
vec2 P = viewDir.xy * param[2].z * fade; // the amount to shift the texture coordinates per layer (from vector P)
|
||||
vec2 deltaTexCoords = P / numLayers;
|
||||
|
||||
|
||||
while(currentLayerDepth < currentDepthMapValue)
|
||||
{
|
||||
currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P
|
||||
currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates
|
||||
currentLayerDepth += layerDepth; // get depth of next layer
|
||||
}
|
||||
|
||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations)
|
||||
|
||||
float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation
|
||||
float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth;
|
||||
|
||||
float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates
|
||||
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
|
||||
|
||||
return finalTexCoords;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = texture(normalmap, f_coord).b;
|
||||
vec2 p = viewDir.xy / viewDir.z * (height * (param[2].z - param[2].w) * 0.2) * fade;
|
||||
return f_coord - p;
|
||||
}
|
||||
}
|
||||
24
shaders/ogl46/mat_invalid.frag
Normal file
24
shaders/ogl46/mat_invalid.frag
Normal file
@@ -0,0 +1,24 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 color = vec4(1.0, 0.0, 1.0, 1.0);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
71
shaders/ogl46/mat_normalmap.frag
Normal file
71
shaders/ogl46/mat_normalmap.frag
Normal file
@@ -0,0 +1,71 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
76
shaders/ogl46/mat_normalmap_specgloss.frag
Normal file
76
shaders/ogl46/mat_normalmap_specgloss.frag
Normal file
@@ -0,0 +1,76 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
132
shaders/ogl46/mat_parallax.frag
Normal file
132
shaders/ogl46/mat_parallax.frag
Normal file
@@ -0,0 +1,132 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn; //tangent matrix nietransponowany; mnożyć przez f_tbn dla TangentLightPos; TangentViewPos; TangentFragPos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
layout(location = 6) in vec3 TangentFragPos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(height_scale, 2, 1, 1, zero)
|
||||
#pragma param(height_offset, 2, 2, 1, zero)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#define PARALLAX
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir);
|
||||
|
||||
void main()
|
||||
{
|
||||
//parallax mapping
|
||||
vec3 viewDir = normalize(vec3(0.0f, 0.0f, 0.0f) - TangentFragPos); //tangent view pos - tangent frag pos
|
||||
vec2 f_coord_p = ParallaxMapping(f_coord, viewDir);
|
||||
|
||||
vec4 tex_color = texture(diffuse, f_coord_p);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord_p).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord_p).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir)
|
||||
{
|
||||
float pos_len = length(f_pos.xyz);
|
||||
// gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way
|
||||
vec2 dcoorddx = dFdx(f_coord);
|
||||
vec2 dcoorddy = dFdy(f_coord);
|
||||
|
||||
if (pos_len > 100.0) {
|
||||
return f_coord;
|
||||
}
|
||||
// the shift fades out before the distance it stops at, so it doesn't switch off at once there
|
||||
float fade = 1.0 - smoothstep(80.0, 100.0, pos_len);
|
||||
|
||||
if (EXTRAEFFECTS_ENABLED)
|
||||
{
|
||||
const float minLayers = 8.0;
|
||||
const float maxLayers = 32.0;
|
||||
float LayersWeight = pos_len / 20.0;
|
||||
vec2 currentTexCoords = f_coord;
|
||||
float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b;
|
||||
LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight);
|
||||
float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers
|
||||
float layerDepth = 1.0 / numLayers; // calculate the size of each layer
|
||||
float currentLayerDepth = 0.0; // depth of current layer
|
||||
vec2 P = viewDir.xy * param[2].y * fade; // the amount to shift the texture coordinates per layer (from vector P)
|
||||
vec2 deltaTexCoords = P / numLayers;
|
||||
|
||||
|
||||
while(currentLayerDepth < currentDepthMapValue)
|
||||
{
|
||||
currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P
|
||||
currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates
|
||||
currentLayerDepth += layerDepth; // get depth of next layer
|
||||
}
|
||||
|
||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations)
|
||||
|
||||
float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation
|
||||
float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth;
|
||||
|
||||
float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates
|
||||
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
|
||||
|
||||
return finalTexCoords;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = texture(normalmap, f_coord).b;
|
||||
vec2 p = viewDir.xy / viewDir.z * (height * (param[2].y - param[2].z) * 0.2) * fade;
|
||||
return f_coord - p;
|
||||
}
|
||||
}
|
||||
137
shaders/ogl46/mat_parallax_specgloss.frag
Normal file
137
shaders/ogl46/mat_parallax_specgloss.frag
Normal file
@@ -0,0 +1,137 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn; //tangent matrix nietransponowany; mnożyć przez f_tbn dla TangentLightPos; TangentViewPos; TangentFragPos;
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
layout(location = 6) in vec3 TangentFragPos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(height_scale, 2, 1, 1, zero)
|
||||
#pragma param(height_offset, 2, 2, 1, zero)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#define PARALLAX
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir);
|
||||
|
||||
void main()
|
||||
{
|
||||
//parallax mapping
|
||||
vec3 viewDir = normalize(vec3(0.0f, 0.0f, 0.0f) - TangentFragPos); //tangent view pos - tangent frag pos
|
||||
vec2 f_coord_p = ParallaxMapping(f_coord, viewDir);
|
||||
|
||||
vec4 tex_color = texture(diffuse, f_coord_p);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord_p).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord_p).a;
|
||||
float specularity = texture(specgloss, f_coord_p).r;
|
||||
glossiness = texture(specgloss, f_coord_p).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord_p).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir)
|
||||
{
|
||||
float pos_len = length(f_pos.xyz);
|
||||
// gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way
|
||||
vec2 dcoorddx = dFdx(f_coord);
|
||||
vec2 dcoorddy = dFdy(f_coord);
|
||||
|
||||
if (pos_len > 100.0) {
|
||||
return f_coord;
|
||||
}
|
||||
// the shift fades out before the distance it stops at, so it doesn't switch off at once there
|
||||
float fade = 1.0 - smoothstep(80.0, 100.0, pos_len);
|
||||
|
||||
if (EXTRAEFFECTS_ENABLED)
|
||||
{
|
||||
const float minLayers = 8.0;
|
||||
const float maxLayers = 32.0;
|
||||
float LayersWeight = pos_len / 20.0;
|
||||
vec2 currentTexCoords = f_coord;
|
||||
float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b;
|
||||
LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight);
|
||||
float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers
|
||||
float layerDepth = 1.0 / numLayers; // calculate the size of each layer
|
||||
float currentLayerDepth = 0.0; // depth of current layer
|
||||
vec2 P = viewDir.xy * param[2].y * fade; // the amount to shift the texture coordinates per layer (from vector P)
|
||||
vec2 deltaTexCoords = P / numLayers;
|
||||
|
||||
|
||||
while(currentLayerDepth < currentDepthMapValue)
|
||||
{
|
||||
currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P
|
||||
currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates
|
||||
currentLayerDepth += layerDepth; // get depth of next layer
|
||||
}
|
||||
|
||||
vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations)
|
||||
|
||||
float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation
|
||||
float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth;
|
||||
|
||||
float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates
|
||||
vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
|
||||
|
||||
return finalTexCoords;
|
||||
}
|
||||
else
|
||||
{
|
||||
float height = texture(normalmap, f_coord).b;
|
||||
vec2 p = viewDir.xy / viewDir.z * (height * (param[2].y - param[2].z) * 0.2) * fade;
|
||||
return f_coord - p;
|
||||
}
|
||||
}
|
||||
211
shaders/ogl46/mat_rain_windscreen.frag
Normal file
211
shaders/ogl46/mat_rain_windscreen.frag
Normal file
@@ -0,0 +1,211 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// Windshield rain effects
|
||||
// by @lcddisplay; wiper movement @MichauSto
|
||||
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
#pragma texture(raindropsatlas, 1, sRGB_A)
|
||||
#pragma texture(wipermask, 2, sRGB_A)
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(raindrop_grid_size, 2, 0, 1, one)
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
#include "random.glsl"
|
||||
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
layout(binding = 1) uniform sampler2D raindropsatlas;
|
||||
layout(binding = 2) uniform sampler2D wipermask;
|
||||
|
||||
const float specular_intensity = 1.0;
|
||||
const float wobble_strength = 0.002;
|
||||
const float wobble_speed = 30.0;
|
||||
|
||||
vec4 getDropTex(float choice, vec2 uv) {
|
||||
vec2 offset;
|
||||
if (choice < 0.25) offset = vec2(0.0, 0.0);
|
||||
else if (choice < 0.5) offset = vec2(0.5, 0.0);
|
||||
else if (choice < 0.75) offset = vec2(0.0, 0.5);
|
||||
else offset = vec2(0.5, 0.5);
|
||||
return texture(raindropsatlas, offset + uv * 0.5);
|
||||
}
|
||||
|
||||
float GetMixFactor(in vec2 co, out float side);
|
||||
|
||||
void main() {
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
if (tex_color.a < 0.01) discard;
|
||||
|
||||
vec2 rainCoord = f_coord;
|
||||
float gridSize = ceil(param[2].x);
|
||||
|
||||
const float numDrops = 20000.0;
|
||||
const float cycleDuration = 4.0;
|
||||
|
||||
float squareMin = 0.5 / gridSize;
|
||||
float squareMax = 1.2 / gridSize;
|
||||
|
||||
vec2 cell = floor(rainCoord * gridSize);
|
||||
|
||||
vec3 dropLayer = vec3(0.0);
|
||||
float dropMaskSum = 0.0;
|
||||
|
||||
// Grid of 9 droplets in immediate neighbourhood
|
||||
for (int oy = -1; oy <= 1; oy++) {
|
||||
for (int ox = -1; ox <= 1; ox++) {
|
||||
|
||||
vec2 neighborCell = cell + vec2(ox, oy);
|
||||
vec2 neighborCenter = (neighborCell + .5) / gridSize;
|
||||
|
||||
float side;
|
||||
float mixFactor = GetMixFactor(neighborCenter, side);
|
||||
|
||||
uint seed = Hash(uvec3(neighborCell, side));
|
||||
|
||||
if(mixFactor < RandF(seed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Show a percentage of droplets given by rain intensity param
|
||||
float activationSeed = RandF(seed);
|
||||
if (activationSeed > rain_params.x)
|
||||
continue; // kropla nieaktywna
|
||||
|
||||
// Randomly modulate droplet center & size
|
||||
vec2 dropCenter = (neighborCell + vec2(RandF(seed), RandF(seed))) / gridSize;
|
||||
float squareSize = mix(squareMin, squareMax, RandF(seed));
|
||||
|
||||
float lifeTime = time + RandF(seed) * cycleDuration;
|
||||
float phase = fract(lifeTime / cycleDuration);
|
||||
float actiwe = clamp(1.0 - phase, 0.0, 1.0);
|
||||
|
||||
// Gravity influence (TODO add vehicle speed & wind here!)
|
||||
float gravityStart = 0.5;
|
||||
float gravityPhase = smoothstep(gravityStart, 1.0, phase);
|
||||
float dropMass = mix(0.3, 1.2, RandF(seed));
|
||||
float gravitySpeed = 0.15 * dropMass;
|
||||
vec2 gravityOffset = vec2(0.0, gravityPhase * gravitySpeed * phase);
|
||||
|
||||
// Random wobble
|
||||
bool hasWobble = (RandF(seed) < 0.10);
|
||||
vec2 wobbleOffset = vec2(0.0);
|
||||
if (hasWobble && gravityPhase > 0.0) {
|
||||
float intensity = sin(time * wobble_speed + RandF(seed) * 100.) * wobble_strength * gravityPhase;
|
||||
wobbleOffset = vec2(intensity, 0.0);
|
||||
}
|
||||
|
||||
vec2 slideOffset = gravityOffset + wobbleOffset;
|
||||
|
||||
// Flatten droplets influenced by gravity
|
||||
float flattenAmount = smoothstep(0.1, 0.5, gravityPhase);
|
||||
float flattenX = mix(1.0, 0.4, flattenAmount);
|
||||
float stretchY = mix(1.0, 1.6, flattenAmount);
|
||||
|
||||
// Droplet local position & mask
|
||||
vec2 diff = (rainCoord + slideOffset) - dropCenter;
|
||||
diff.x *= 1.0 / flattenX;
|
||||
diff.y *= 1.0 / stretchY;
|
||||
float mask = smoothstep(squareSize * 0.5, squareSize * 0.45, max(abs(diff.x), abs(diff.y)));
|
||||
|
||||
if (mask > 0.001) {
|
||||
vec2 localUV = (diff + squareSize * 0.5) / squareSize;
|
||||
float choice = RandF(seed);
|
||||
vec4 dropTex = getDropTex(choice, localUV);
|
||||
float sharpAlpha = smoothstep(0.3, 0.9, dropTex.a);
|
||||
|
||||
float ambLum = clamp(dot(ambient, vec3(0.2126, 0.7152, 0.0722)), 0.0, 1.0);
|
||||
float sunFactor = pow(clamp(ambLum * 6.0, 0.0, 1.0), 0.5);
|
||||
float dynBright = mix(0.0, 1.0, sunFactor);
|
||||
|
||||
float colorLuma = length(dropTex.rgb);
|
||||
float alphaRange = smoothstep(0.1, 0.3, colorLuma);
|
||||
float blackAlpha = mix(0.25, 0.85, alphaRange);
|
||||
|
||||
float sparkle = 0.0;
|
||||
if (hasWobble) {
|
||||
sparkle = pow(abs(sin(f_coord.x * 8.0 + time * 2.0 + RandF(seed) * 6.2831)), 40.0)
|
||||
* mix(0.2, 1.0, sunFactor);
|
||||
}
|
||||
|
||||
vec3 specularColor = vec3(1.0) * specular_intensity * sparkle;
|
||||
vec3 dropLit = dropTex.rgb * dynBright + specularColor * sharpAlpha;
|
||||
|
||||
dropLayer += dropLit * sharpAlpha * actiwe * blackAlpha * mask;
|
||||
dropMaskSum += sharpAlpha * actiwe * blackAlpha * mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
vec3 finalMix = dropLayer;
|
||||
float alphaOut = clamp(dropMaskSum, 0.0, 1.0);
|
||||
out_color = vec4(finalMix, alphaOut);
|
||||
|
||||
{ // Overlay windshield texture with alpha
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(fragcolor, tex_color.a * alpha_mult);
|
||||
|
||||
out_color.xyz = apply_fog(mix(out_color.xyz, color.xyz, color.a));
|
||||
out_color.a = mix(out_color.a, 1., color.a);
|
||||
}
|
||||
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
float GetMixFactor(in vec2 co, out float side) {
|
||||
vec4 movePhase = wiper_pos;
|
||||
bvec4 is_out = lessThanEqual(movePhase, vec4(1.));
|
||||
movePhase = mix(vec4(2.) - movePhase, movePhase, is_out);
|
||||
|
||||
vec4 mask = texture(wipermask, co);
|
||||
|
||||
vec4 areaMask = step(.001, mask);
|
||||
|
||||
vec4 maskVal = mix(1. - mask, mask, is_out);
|
||||
vec4 wipeWidth = smoothstep(1., .9, movePhase) * .25;
|
||||
vec4 cleaned = smoothstep(movePhase - wipeWidth, movePhase, maskVal) * areaMask;
|
||||
vec4 side_v = step(maskVal, movePhase);
|
||||
cleaned *= side_v;
|
||||
side_v = mix(side_v, vec4(1.) - side_v, is_out);
|
||||
|
||||
// "regeneration", raindrops gradually returning after wiper pass:
|
||||
vec4 regenPhase = clamp((vec4(time) - mix(wiper_timer_out, wiper_timer_return, side_v) - vec4(.2)) / vec4(rain_params.y), vec4(0.), vec4(1.));
|
||||
|
||||
side_v = mix(vec4(0.), vec4(1.) - side_v, areaMask);
|
||||
|
||||
vec4 factor_v = mix(vec4(1.), regenPhase * (vec4(1.) - cleaned), areaMask);
|
||||
|
||||
side = 0.;
|
||||
float out_factor = 1.;
|
||||
|
||||
// Find out the wiper blade that influences given grid cell the most
|
||||
for(int i = 0; i < 4; ++i)
|
||||
{
|
||||
bool is_candidate = factor_v[i] < out_factor;
|
||||
out_factor = mix(out_factor, factor_v[i], is_candidate);
|
||||
side = mix(side, side_v[i], is_candidate);
|
||||
}
|
||||
|
||||
return out_factor;
|
||||
}
|
||||
3
shaders/ogl46/mat_reflmap.frag
Normal file
3
shaders/ogl46/mat_reflmap.frag
Normal file
@@ -0,0 +1,3 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "mat_reflmap.glsl"
|
||||
67
shaders/ogl46/mat_reflmap.glsl
Normal file
67
shaders/ogl46/mat_reflmap.glsl
Normal file
@@ -0,0 +1,67 @@
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(reflmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D reflmap;
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z * texture(reflmap, f_coord).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, 0.0, shadow_tone);
|
||||
|
||||
if(alphatestfail)
|
||||
fragcolor.r = 1.0;
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
74
shaders/ogl46/mat_reflmap_specgloss.frag
Normal file
74
shaders/ogl46/mat_reflmap_specgloss.frag
Normal file
@@ -0,0 +1,74 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(reflmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D reflmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
vec3 fragnormal = normalize(f_normal);
|
||||
float reflectivity = param[1].z * texture(reflmap, f_coord).a;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
if(alphatestfail)
|
||||
fragcolor.r = 1.0;
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
70
shaders/ogl46/mat_shadowlessnormalmap.frag
Normal file
70
shaders/ogl46/mat_shadowlessnormalmap.frag
Normal file
@@ -0,0 +1,70 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, 1.0);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
75
shaders/ogl46/mat_shadowlessnormalmap_specgloss.frag
Normal file
75
shaders/ogl46/mat_shadowlessnormalmap_specgloss.frag
Normal file
@@ -0,0 +1,75 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, 1.0);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
33
shaders/ogl46/mat_stars.frag
Normal file
33
shaders/ogl46/mat_stars.frag
Normal file
@@ -0,0 +1,33 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
layout(location = 1) flat in vec3 f_normal_raw;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
void main()
|
||||
{
|
||||
float x = (gl_PointCoord.x - 0.5f) * 2.0f;
|
||||
float y = (gl_PointCoord.y - 0.5f) * 2.0f;
|
||||
float dist2 = abs(x * x + y * y);
|
||||
if (dist2 > 0.5f * 0.5f)
|
||||
discard;
|
||||
|
||||
// color data space is shared with normals, ugh
|
||||
vec4 color = vec4(pow(f_normal_raw.rgb, vec3(2.2)), 1.0f);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
out_motion = vec4(0.0f);
|
||||
}
|
||||
}
|
||||
70
shaders/ogl46/mat_sunlessnormalmap.frag
Normal file
70
shaders/ogl46/mat_sunlessnormalmap.frag
Normal file
@@ -0,0 +1,70 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5;
|
||||
legacy_gloss();
|
||||
|
||||
fragcolor = apply_lights_sunless(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
75
shaders/ogl46/mat_sunlessnormalmap_specgloss.frag
Normal file
75
shaders/ogl46/mat_sunlessnormalmap_specgloss.frag
Normal file
@@ -0,0 +1,75 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, zero)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(normalmap, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(specgloss, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
#define NORMALMAP
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(diffuse, f_coord);
|
||||
|
||||
bool alphatestfail = ( opacity >= 0.0 ? (tex_color.a < opacity) : (tex_color.a >= -opacity) );
|
||||
if(alphatestfail)
|
||||
discard;
|
||||
// if (tex_color.a < opacity)
|
||||
// discard;
|
||||
|
||||
vec3 fragcolor = ambient;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, f_coord).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
|
||||
float specularity = texture(specgloss, f_coord).r;
|
||||
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
|
||||
metalic = texture(specgloss, f_coord).b;
|
||||
|
||||
fragcolor = apply_lights_sunless(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
3
shaders/ogl46/mat_terrain.frag
Normal file
3
shaders/ogl46/mat_terrain.frag
Normal file
@@ -0,0 +1,3 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "mat_terrain.glsl"
|
||||
153
shaders/ogl46/mat_terrain.glsl
Normal file
153
shaders/ogl46/mat_terrain.glsl
Normal file
@@ -0,0 +1,153 @@
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
// heightmap terrain: up to 8 materials (layers) painted over a chunk, blended by their weights.
|
||||
// the textures of all layers of the terrain are kept in texture arrays; the table of the chunk tells which layers it has:
|
||||
// row 0, per layer: index in the arrays, 1 / metres the textures repeat at, has normal map, has specular/gloss map
|
||||
// row 1, per layer: reflection, cosine and sine of the angle the textures are turned by
|
||||
// f_coord holds metres from the corner of the pack of chunks, along x and -z
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
// corner of the chunk (x, z) in texture coordinates, 1 / size of the chunk, paint samples along a side less one
|
||||
#pragma param(placement, 2, 0, 4, zero)
|
||||
|
||||
#pragma texture(opaque, 0, RGBA)
|
||||
layout(binding = 0) uniform sampler2D opaque;
|
||||
#pragma texture(weights0, 1, RGBA)
|
||||
layout(binding = 1) uniform sampler2D weights0;
|
||||
#pragma texture(weights1, 2, RGBA)
|
||||
layout(binding = 2) uniform sampler2D weights1;
|
||||
#pragma texture(layerdiffuse, 3, sRGB_A)
|
||||
layout(binding = 3) uniform highp sampler2DArray layerdiffuse;
|
||||
#pragma texture(layernormal, 4, RGBA)
|
||||
layout(binding = 4) uniform highp sampler2DArray layernormal;
|
||||
#pragma texture(layerspecgloss, 5, RGBA)
|
||||
layout(binding = 5) uniform highp sampler2DArray layerspecgloss;
|
||||
#pragma texture(layertable, 6, RGBA)
|
||||
layout(binding = 6) uniform highp sampler2D layertable;
|
||||
|
||||
#define NORMALMAP
|
||||
// param[2] holds the placement of the chunk, not the weathering
|
||||
#define NO_WEATHERING
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
// place within the chunk, 0..1 along x and z, and the paint samples around it
|
||||
vec2 local = clamp(vec2(f_coord.x - param[2].x, -f_coord.y - param[2].y) * param[2].z, 0.0, 1.0);
|
||||
#if TERRAIN_HOLES
|
||||
// the openings cut in the ground (mat_terrain_holes.frag): the mask led between its samples, cut over the half.
|
||||
// discarded at the end, the neighbours of the fragment still need it for their derivatives
|
||||
float holeside = float(textureSize(holes, 0).x);
|
||||
bool cut = (texture(holes, (local * (holeside - 1.0) + 0.5) / holeside).r > 0.5);
|
||||
#endif
|
||||
float samples = param[2].w;
|
||||
vec2 paintcoord = (local * samples + 0.5) / (samples + 1.0);
|
||||
vec4 w0 = texture(weights0, paintcoord);
|
||||
vec4 w1 = texture(weights1, paintcoord);
|
||||
float weights[8] = float[8](w0.r, w0.g, w0.b, w0.a, w1.r, w1.g, w1.b, w1.a);
|
||||
float painted = dot(w0, vec4(1.0)) + dot(w1, vec4(1.0));
|
||||
|
||||
// the layers are sampled only where they're painted, so the gradients are taken here, where every pixel gets them
|
||||
vec2 coorddx = dFdx(f_coord);
|
||||
vec2 coorddy = dFdy(f_coord);
|
||||
|
||||
vec3 albedo = vec3(0.0);
|
||||
vec3 normal = vec3(0.0);
|
||||
float reflectivity = 0.0;
|
||||
float specularity = 0.0;
|
||||
float gloss = 0.0;
|
||||
float metal = 0.0;
|
||||
float total = 0.0;
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
// a chunk without weights shows its first layer
|
||||
float weight = (painted < 0.004 ? (i == 0 ? 1.0 : 0.0) : weights[i]);
|
||||
if (weight < 0.004)
|
||||
continue;
|
||||
vec4 info = texelFetch(layertable, ivec2(i, 0), 0);
|
||||
vec4 extra = texelFetch(layertable, ivec2(i, 1), 0);
|
||||
// turned counterclockwise seen from above; f_coord runs along x and -z, so that's clockwise in it
|
||||
vec2 turn = (extra.y == 0.0 && extra.z == 0.0) ? vec2(1.0, 0.0) : extra.yz;
|
||||
mat2 rotation = mat2(turn.x, -turn.y, turn.y, turn.x);
|
||||
vec3 coord = vec3(rotation * f_coord * info.y, info.x);
|
||||
vec2 dx = rotation * coorddx * info.y;
|
||||
vec2 dy = rotation * coorddy * info.y;
|
||||
albedo += textureGrad(layerdiffuse, coord, dx, dy).rgb * weight;
|
||||
vec3 layernormal_ts = vec3(0.0, 0.0, 1.0);
|
||||
float layerreflection = extra.x;
|
||||
if (info.z > 0.5)
|
||||
{
|
||||
vec4 normalmap = textureGrad(layernormal, coord, dx, dy);
|
||||
layernormal_ts.xy = normalmap.rg * 2.0 - 1.0;
|
||||
layernormal_ts.z = sqrt(1.0 - clamp(dot(layernormal_ts.xy, layernormal_ts.xy), 0.0, 1.0));
|
||||
// the normal map leans along the turned texture; turned back to the axes of the terrain
|
||||
layernormal_ts.xy = layernormal_ts.xy * rotation;
|
||||
layerreflection *= normalmap.a;
|
||||
}
|
||||
normal += layernormal_ts * weight;
|
||||
if (info.w > 0.5)
|
||||
{
|
||||
vec4 specgloss = textureGrad(layerspecgloss, coord, dx, dy);
|
||||
specularity += specgloss.r * weight;
|
||||
gloss += specgloss.g * weight;
|
||||
metal += specgloss.b * weight;
|
||||
}
|
||||
else
|
||||
{
|
||||
// a layer without a specgloss map: a dielectric of the glossiness: of the material (a blinn-phong exponent)
|
||||
specularity += 0.5 * weight;
|
||||
gloss += (1.0 - phong_roughness(abs(param[1].w))) * weight;
|
||||
}
|
||||
reflectivity += layerreflection * weight;
|
||||
total += weight;
|
||||
}
|
||||
total = max(total, 0.004);
|
||||
albedo /= total;
|
||||
specularity /= total;
|
||||
metal /= total;
|
||||
reflectivity = param[1].z * reflectivity / total;
|
||||
glossiness = (gloss / total) * abs(param[1].w);
|
||||
metalic = metal;
|
||||
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal));
|
||||
vec3 fragcolor = ambient;
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, albedo, reflectivity, specularity, shadow_tone);
|
||||
|
||||
#if TERRAIN_HOLES
|
||||
if (cut)
|
||||
discard;
|
||||
#endif
|
||||
vec4 color = vec4(apply_fog(fragcolor), alpha_mult);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
11
shaders/ogl46/mat_terrain_holes.frag
Normal file
11
shaders/ogl46/mat_terrain_holes.frag
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// heightmap terrain with holes cut in it, for the portals of tunnels and the like (opengl46_terrain_materials): the cells cut
|
||||
// out entirely are left out of the mesh, the rest of the openings is cut here. the mask of the holes covers the chunk, 255
|
||||
// where the ground is cut; the fragments where the value led between its samples is over the half are discarded
|
||||
#define TERRAIN_HOLES 1
|
||||
|
||||
#pragma texture(holes, 7, R)
|
||||
layout(binding = 7) uniform sampler2D holes;
|
||||
|
||||
#include "mat_terrain.glsl"
|
||||
12
shaders/ogl46/mat_trackblend.frag
Normal file
12
shaders/ogl46/mat_trackblend.frag
Normal file
@@ -0,0 +1,12 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// the rails and the ballast of a track faded into those of the tracks joined at its ends, see trackblend.glsl
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
#pragma texture(diffuse1, 1, sRGB_A)
|
||||
layout(binding = 1) uniform sampler2D diffuse1;
|
||||
#pragma texture(diffuse2, 2, sRGB_A)
|
||||
layout(binding = 2) uniform sampler2D diffuse2;
|
||||
#pragma texture(diffuse3, 3, sRGB_A)
|
||||
layout(binding = 3) uniform sampler2D diffuse3;
|
||||
#include "trackblend.glsl"
|
||||
21
shaders/ogl46/mat_trackblend_normalmap.frag
Normal file
21
shaders/ogl46/mat_trackblend_normalmap.frag
Normal file
@@ -0,0 +1,21 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// the rails and the ballast of a track of a normal mapped material faded into those of the tracks joined at its ends, see trackblend.glsl
|
||||
#define TRACKBLEND_NORMALMAP 1
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
#pragma texture(diffuse1, 1, sRGB_A)
|
||||
layout(binding = 1) uniform sampler2D diffuse1;
|
||||
#pragma texture(diffuse2, 2, sRGB_A)
|
||||
layout(binding = 2) uniform sampler2D diffuse2;
|
||||
#pragma texture(diffuse3, 3, sRGB_A)
|
||||
layout(binding = 3) uniform sampler2D diffuse3;
|
||||
#pragma texture(normalmap, 4, RGBA)
|
||||
layout(binding = 4) uniform sampler2D normalmap;
|
||||
#pragma texture(normalmap1, 5, RGBA)
|
||||
layout(binding = 5) uniform sampler2D normalmap1;
|
||||
#pragma texture(normalmap2, 6, RGBA)
|
||||
layout(binding = 6) uniform sampler2D normalmap2;
|
||||
#pragma texture(normalmap3, 7, RGBA)
|
||||
layout(binding = 7) uniform sampler2D normalmap3;
|
||||
#include "trackblend.glsl"
|
||||
24
shaders/ogl46/mat_trackblend_parallax.frag
Normal file
24
shaders/ogl46/mat_trackblend_parallax.frag
Normal file
@@ -0,0 +1,24 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// the rails and the ballast of a track of a parallax material faded into those of the tracks joined at its ends, see trackblend.glsl
|
||||
#define TRACKBLEND_NORMALMAP 1
|
||||
#define TRACKBLEND_PARALLAX 1
|
||||
#pragma param(height_scale, 2, 1, 1, zero)
|
||||
#pragma param(height_offset, 2, 2, 1, zero)
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
#pragma texture(diffuse1, 1, sRGB_A)
|
||||
layout(binding = 1) uniform sampler2D diffuse1;
|
||||
#pragma texture(diffuse2, 2, sRGB_A)
|
||||
layout(binding = 2) uniform sampler2D diffuse2;
|
||||
#pragma texture(diffuse3, 3, sRGB_A)
|
||||
layout(binding = 3) uniform sampler2D diffuse3;
|
||||
#pragma texture(normalmap, 4, RGBA)
|
||||
layout(binding = 4) uniform sampler2D normalmap;
|
||||
#pragma texture(normalmap1, 5, RGBA)
|
||||
layout(binding = 5) uniform sampler2D normalmap1;
|
||||
#pragma texture(normalmap2, 6, RGBA)
|
||||
layout(binding = 6) uniform sampler2D normalmap2;
|
||||
#pragma texture(normalmap3, 7, RGBA)
|
||||
layout(binding = 7) uniform sampler2D normalmap3;
|
||||
#include "trackblend.glsl"
|
||||
20
shaders/ogl46/mat_trackblend_specgloss.frag
Normal file
20
shaders/ogl46/mat_trackblend_specgloss.frag
Normal file
@@ -0,0 +1,20 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// the rails and the ballast of a track of a material with normal and specgloss maps faded into those of the tracks joined at its ends, see trackblend.glsl
|
||||
#define TRACKBLEND_NORMALMAP 1
|
||||
#define TRACKBLEND_SPECGLOSS 1
|
||||
#pragma texture(diffuse, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D diffuse;
|
||||
#pragma texture(diffuse1, 1, sRGB_A)
|
||||
layout(binding = 1) uniform sampler2D diffuse1;
|
||||
#pragma texture(diffuse2, 2, sRGB_A)
|
||||
layout(binding = 2) uniform sampler2D diffuse2;
|
||||
#pragma texture(normalmap, 3, RGBA)
|
||||
layout(binding = 3) uniform sampler2D normalmap;
|
||||
#pragma texture(normalmap1, 4, RGBA)
|
||||
layout(binding = 4) uniform sampler2D normalmap1;
|
||||
#pragma texture(normalmap2, 5, RGBA)
|
||||
layout(binding = 5) uniform sampler2D normalmap2;
|
||||
#pragma texture(specgloss, 6, RGBA)
|
||||
layout(binding = 6) uniform sampler2D specgloss;
|
||||
#include "trackblend.glsl"
|
||||
93
shaders/ogl46/mat_water.frag
Normal file
93
shaders/ogl46/mat_water.frag
Normal file
@@ -0,0 +1,93 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(wave_strength, 2, 1, 1, one)
|
||||
#pragma param(wave_speed, 2, 2, 1, one)
|
||||
|
||||
#pragma texture(normalmap, 0, RGBA)
|
||||
layout(binding = 0) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(dudvmap, 1, RGB)
|
||||
layout(binding = 1) uniform sampler2D dudvmap;
|
||||
|
||||
#pragma texture(diffuse, 2, sRGB_A)
|
||||
layout(binding = 2) uniform sampler2D diffuse;
|
||||
|
||||
//wave distortion variables
|
||||
float move_factor = 0.0;
|
||||
|
||||
#define WATER
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
//wave distortion
|
||||
move_factor += (param[2].z * time);
|
||||
move_factor = mod(move_factor, 1.0);
|
||||
vec2 texture_coords = f_coord;
|
||||
vec2 distorted_tex_coord = texture(dudvmap, vec2(texture_coords.x + move_factor, texture_coords.y)).rg * 0.1;
|
||||
distorted_tex_coord = texture_coords + vec2(distorted_tex_coord.x , distorted_tex_coord.y + move_factor);
|
||||
vec2 total_distorted_tex_coord = (texture(dudvmap, distorted_tex_coord).rg * 2.0 - 1.0 ) * param[2].y;
|
||||
texture_coords += total_distorted_tex_coord;
|
||||
|
||||
vec4 tex_color = texture(diffuse, texture_coords);
|
||||
|
||||
vec3 fragcolor = ambient * 0.25;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, texture_coords).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, texture_coords ).a;
|
||||
// water reflects 2% at normal incidence
|
||||
float specularity = 0.25;
|
||||
glossiness = abs(param[1].w);
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
//fragcolor = mix(fragcolor, param[0].rgb, param[1].z);
|
||||
// fragcolor = (fragcolor * tex_color.rgb * param[1].z) + (fragcolor * (1.0 - param[1].z)); //multiply
|
||||
//fragcolor = ( max(fragcolor + param[0].rgb -1.0,0.0) * param[1].z + fragcolor * (1.0 - param[1].z)); //linear burn
|
||||
//fragcolor = ( min(param[0].rgb,fragcolor) * param[1].z + fragcolor * (1.0 - param[1].z)); //darken
|
||||
|
||||
// fresnel effect
|
||||
vec3 view_dir = normalize(vec3(0.0f, 0.0f, 0.0f) - f_pos.xyz);
|
||||
float fresnel = pow(max(dot(normalize(f_normal), view_dir), 0.0), 0.2);
|
||||
float fresnel_inv = ((fresnel - 1.0 ) * -1.0 );
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), clamp((fresnel_inv + param[0].a), 0.0, 1.0));
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
98
shaders/ogl46/mat_water_specgloss.frag
Normal file
98
shaders/ogl46/mat_water_specgloss.frag
Normal file
@@ -0,0 +1,98 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
layout(location = 0) in vec3 f_normal;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 8) in mat3 f_tbn;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
#pragma param(color, 0, 0, 4, diffuse)
|
||||
#pragma param(diffuse, 1, 0, 1, diffuse)
|
||||
#pragma param(specular, 1, 1, 1, specular)
|
||||
#pragma param(reflection, 1, 2, 1, one)
|
||||
#pragma param(glossiness, 1, 3, 1, glossiness)
|
||||
#pragma param(wave_strength, 2, 1, 1, one)
|
||||
#pragma param(wave_speed, 2, 2, 1, one)
|
||||
|
||||
#pragma texture(normalmap, 0, RGBA)
|
||||
layout(binding = 0) uniform sampler2D normalmap;
|
||||
|
||||
#pragma texture(dudvmap, 1, RGB)
|
||||
layout(binding = 1) uniform sampler2D dudvmap;
|
||||
|
||||
#pragma texture(diffuse, 2, sRGB_A)
|
||||
layout(binding = 2) uniform sampler2D diffuse;
|
||||
|
||||
#pragma texture(specgloss, 3, RGBA)
|
||||
layout(binding = 3) uniform sampler2D specgloss;
|
||||
|
||||
|
||||
//wave distortion variables
|
||||
float move_factor = 0.0;
|
||||
|
||||
#define WATER
|
||||
#include "light_common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
|
||||
void main()
|
||||
{
|
||||
//wave distortion
|
||||
move_factor += (param[2].z * time);
|
||||
move_factor = mod(move_factor, 1.0);
|
||||
vec2 texture_coords = f_coord;
|
||||
vec2 distorted_tex_coord = texture(dudvmap, vec2(texture_coords.x + move_factor, texture_coords.y)).rg * 0.1;
|
||||
distorted_tex_coord = texture_coords + vec2(distorted_tex_coord.x , distorted_tex_coord.y + move_factor);
|
||||
vec2 total_distorted_tex_coord = (texture(dudvmap, distorted_tex_coord).rg * 2.0 - 1.0 ) * param[2].y;
|
||||
texture_coords += total_distorted_tex_coord;
|
||||
|
||||
vec4 tex_color = texture(diffuse, texture_coords);
|
||||
|
||||
vec3 fragcolor = ambient * 0.25;
|
||||
|
||||
vec3 normal;
|
||||
normal.xy = (texture(normalmap, texture_coords).rg * 2.0 - 1.0);
|
||||
normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0));
|
||||
vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz));
|
||||
float reflectivity = param[1].z * texture(normalmap, texture_coords ).a;
|
||||
vec4 specgloss_map = texture(specgloss, texture_coords);
|
||||
float specularity = specgloss_map.r;
|
||||
glossiness = specgloss_map.g * abs(param[1].w);
|
||||
metalic = specgloss_map.b;
|
||||
|
||||
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
|
||||
|
||||
//fragcolor = mix(fragcolor, param[0].rgb, param[1].z);
|
||||
// fragcolor = (fragcolor * tex_color.rgb * param[1].z) + (fragcolor * (1.0 - param[1].z)); //multiply
|
||||
//fragcolor = ( max(fragcolor + param[0].rgb -1.0,0.0) * param[1].z + fragcolor * (1.0 - param[1].z)); //linear burn
|
||||
//fragcolor = ( min(param[0].rgb,fragcolor) * param[1].z + fragcolor * (1.0 - param[1].z)); //darken
|
||||
|
||||
// fresnel effect
|
||||
vec3 view_dir = normalize(vec3(0.0f, 0.0f, 0.0f) - f_pos.xyz);
|
||||
float fresnel = pow(max(dot(normalize(f_normal), view_dir), 0.0), 0.2);
|
||||
float fresnel_inv = ((fresnel - 1.0 ) * -1.0 );
|
||||
|
||||
vec4 color = vec4(apply_fog(fragcolor), clamp((fresnel_inv + param[0].a), 0.0, 1.0));
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
90
shaders/ogl46/particle_common.glsl
Normal file
90
shaders/ogl46/particle_common.glsl
Normal file
@@ -0,0 +1,90 @@
|
||||
// storage of the particles of the gpu (opengl46_gpu_particles, gl::particle_* in gl/ubo.h) for the compute passes
|
||||
|
||||
#ifndef PARTICLE_COMMON_GLSL
|
||||
#define PARTICLE_COMMON_GLSL
|
||||
|
||||
// a particle of smoke; it's dead (or its place was never used) when its opacity is 0 or less
|
||||
struct smoke_particle_s
|
||||
{
|
||||
vec4 position; // xyz: metres from the origin of the particles (world axes), w: rotation, radians
|
||||
vec4 velocity; // xyz: m/s, w: size of the billboard, metres
|
||||
vec4 state; // x: opacity, y: age, seconds
|
||||
};
|
||||
|
||||
// a source of smoke as the last update of the simulation left it (smoke_source::gpu_state)
|
||||
struct smoke_source_s
|
||||
{
|
||||
uvec4 pool; // x: first place of the source in the storage, y: number of its places, z: place of the first particle born in the update, w: particles born
|
||||
vec4 location; // xyz: emitter at the update, from the origin
|
||||
vec4 previous; // xyz: emitter at the update before, from the origin
|
||||
vec4 launch; // xy: inclination, radians, zw: speed, m/s
|
||||
vec4 initial; // xy: size, zw: opacity
|
||||
vec4 sizechange; // x: per second, y: min, z: max, w: per metre the particle goes through the air (spread:)
|
||||
vec4 opacitychange; // x: per second, y: min, z: max
|
||||
vec4 motion; // x: rise, y: damping (per second, of the vertical velocity), z: least vertical velocity
|
||||
vec4 settling; // x: velocity the particles settle at, m/s upwards, y: drag, per second, z: 1 when they settle (in place of motion), w: seconds they take to show up (fade_in:)
|
||||
vec4 color; // rgb: colour of the smoke, lit by the lights of the pass
|
||||
vec4 oldcolor; // rgb: colour lit as the cpu does it, without the lights (gfx.smoke.lighting off)
|
||||
};
|
||||
|
||||
// what a draw of a particle reads, as attributes of an instance
|
||||
struct particle_draw_s
|
||||
{
|
||||
vec4 place; // xyz: metres from the camera (world axes), w: size (smoke) or width (rain, snow)
|
||||
vec4 shape; // x: rotation (smoke); xyz: from the head of the streak to its tail (rain, snow)
|
||||
vec4 color; // rgb: lit colour, a: opacity
|
||||
};
|
||||
|
||||
// the indirect draw command of the particles; the compute passes count the instances
|
||||
layout(std430, binding = PARTICLE_COMMAND_SSBO_BINDING) buffer particle_command_ssbo
|
||||
{
|
||||
uint particle_draw_vertices;
|
||||
uint particle_draw_instances;
|
||||
uint particle_draw_first;
|
||||
uint particle_draw_base;
|
||||
};
|
||||
|
||||
layout(std430, binding = PARTICLE_OUTPUT_SSBO_BINDING) writeonly buffer particle_output_ssbo
|
||||
{
|
||||
particle_draw_s particle_draws[];
|
||||
};
|
||||
|
||||
// pcg hash
|
||||
uint particle_hash(uint value)
|
||||
{
|
||||
uint state = value * 747796405U + 2891336453U;
|
||||
uint word = ((state >> ((state >> 28U) + 4U)) ^ state) * 277803737U;
|
||||
return (word >> 22U) ^ word;
|
||||
}
|
||||
|
||||
// next of a sequence of random numbers 0-1
|
||||
float particle_random(inout uint state)
|
||||
{
|
||||
state = particle_hash(state);
|
||||
return float(state >> 8U) * (1.0 / 16777216.0);
|
||||
}
|
||||
|
||||
// whether a sphere (centre from the camera along the world axes) is within the planes of the view
|
||||
bool particle_visible(vec4 planes[5], vec3 centre, float radius)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
if (dot(planes[i].xyz, centre) + planes[i].w < -radius)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// adds a particle to the draw; Capacity: the places of the storage of the draws
|
||||
void particle_emit(vec3 place, float size, vec3 shape, vec3 color, float opacity, uint capacity)
|
||||
{
|
||||
uint index = atomicAdd(particle_draw_instances, 1U);
|
||||
if (index >= capacity)
|
||||
{
|
||||
atomicAdd(particle_draw_instances, 0xFFFFFFFFU);
|
||||
return;
|
||||
}
|
||||
particle_draws[index].place = vec4(place, size);
|
||||
particle_draws[index].shape = vec4(shape, 0.0);
|
||||
particle_draws[index].color = vec4(color, opacity);
|
||||
}
|
||||
|
||||
#endif
|
||||
147
shaders/ogl46/particle_light.glsl
Normal file
147
shaders/ogl46/particle_light.glsl
Normal file
@@ -0,0 +1,147 @@
|
||||
// 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
|
||||
|
||||
layout(binding = SHADOW_TEX) uniform sampler2DArrayShadow shadowmap;
|
||||
layout(binding = HEADLIGHT_TEX) 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
24
shaders/ogl46/particle_soft.glsl
Normal file
24
shaders/ogl46/particle_soft.glsl
Normal file
@@ -0,0 +1,24 @@
|
||||
// soft particles (opengl46_gpu_particles): a particle fades where it nears what is behind it, instead of being cut by it.
|
||||
// the resolved depth of the scene drawn so far (gl::SCENE_DEPTH_TEX), reversed with the far plane at infinity: the distance
|
||||
// along the axis of the view is the near plane over the depth, and the sky, which writes no depth, stays at 0
|
||||
|
||||
#ifndef PARTICLE_SOFT_GLSL
|
||||
#define PARTICLE_SOFT_GLSL
|
||||
|
||||
layout(binding = SCENE_DEPTH_TEX) uniform sampler2D particle_scene_depth;
|
||||
|
||||
// share of the particle left at the fragment, 0-1: Range (metres) is how far in front of the scene it fades out over, 0 for none.
|
||||
// Fragdistance: of the fragment along the axis of the view
|
||||
float particle_soft_fade(float range, float fragdistance)
|
||||
{
|
||||
if (range <= 0.0)
|
||||
return 1.0;
|
||||
float depth = texelFetch(particle_scene_depth, ivec2(gl_FragCoord.xy), 0).r;
|
||||
if (depth <= 0.0)
|
||||
return 1.0;
|
||||
float scene = projection[3][2] / depth;
|
||||
float share = clamp((scene - fragdistance) / range, 0.0, 1.0);
|
||||
return share * share * (3.0 - 2.0 * share);
|
||||
}
|
||||
|
||||
#endif
|
||||
77
shaders/ogl46/particles_precipitation.comp
Normal file
77
shaders/ogl46/particles_precipitation.comp
Normal file
@@ -0,0 +1,77 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// rain and snow of the gpu (opengl46_gpu_particles), in every colour pass which draws them: a thread per drop or flake. the
|
||||
// particles fill three boxes around the camera, a small dense one and two larger ones, each repeated along the world axes:
|
||||
// a particle keeps its place in the box, moved by how far the wind took them all and how far it fell, so nothing is kept
|
||||
// from frame to frame. what the camera sees is lit (particle_light.glsl) and drawn as a streak from where it is to where
|
||||
// it was a moment ago, seen from the moving camera (particles_precipitation.vert)
|
||||
|
||||
#include "common.glsl"
|
||||
#include "weather_common.glsl"
|
||||
#include "particle_light.glsl"
|
||||
#include "particle_common.glsl"
|
||||
|
||||
layout(local_size_x = PARTICLE_GROUP_SIZE) in;
|
||||
|
||||
// x, y: first particles of the second and of the third box, z: particles, w: 1 for snow, 0 for rain
|
||||
layout(location = 0) uniform uvec4 counts;
|
||||
// xyz: how far the wind took the particles less where the camera is, in each box (metres, 0 to the size of the box); w: how
|
||||
// far they fell in it, 0 to 16 sizes of the box
|
||||
layout(location = 1) uniform vec4 offsets[3];
|
||||
// xyz: size of each box, metres; w unused
|
||||
layout(location = 4) uniform vec4 sizes;
|
||||
// xyz: velocity of the wind less the one of the camera, m/s; w: speed of the fall
|
||||
layout(location = 5) uniform vec4 velocity;
|
||||
// x: seconds (for the flutter of the flakes), y: how long a streak lasts, seconds, z: opacity, w: places for the draws
|
||||
layout(location = 6) uniform vec4 timing;
|
||||
// the world axes around the camera to the view space of the pass
|
||||
layout(location = 7) uniform mat4 view;
|
||||
// y: how much a particle scatters the light onwards, z: brightness
|
||||
layout(location = 11) uniform vec4 options;
|
||||
layout(location = 12) uniform vec4 planes[5];
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_GlobalInvocationID.x;
|
||||
if (index >= counts.z)
|
||||
return;
|
||||
uint box = (index < counts.x) ? 0U : (index < counts.y) ? 1U : 2U;
|
||||
float size = sizes[box];
|
||||
bool snow = counts.w != 0U;
|
||||
|
||||
uint state = particle_hash(index * 3U + 0x9E3779B9U);
|
||||
vec3 home = vec3(particle_random(state), particle_random(state), particle_random(state)) * size;
|
||||
// the speed of the fall is (16 + k) / 16 of the common one, so a whole number of sizes of the box (16 of them) of the common
|
||||
// fall is a whole number of them for every particle, and the fall can be kept within 16 sizes
|
||||
float pace = (16.0 + floor(particle_random(state) * 7.0) - 3.0) / 16.0;
|
||||
float width = snow ? mix(0.004, 0.012, particle_random(state)) : mix(0.0012, 0.0025, particle_random(state));
|
||||
vec3 move = vec3(velocity.xyz) + vec3(0.0, -velocity.w * pace, 0.0);
|
||||
vec3 place = home + offsets[box].xyz - vec3(0.0, offsets[box].w * pace, 0.0);
|
||||
if (snow)
|
||||
{
|
||||
// the flakes flutter about their way
|
||||
float amplitude = mix(0.1, 0.35, particle_random(state));
|
||||
vec2 frequency = mix(vec2(1.5), vec2(5.0), vec2(particle_random(state), particle_random(state)));
|
||||
vec2 phase = vec2(particle_random(state), particle_random(state)) * 6.2831853;
|
||||
vec2 angle = frequency * timing.x + phase;
|
||||
place.xz += amplitude * sin(angle);
|
||||
move.xz += amplitude * frequency * cos(angle);
|
||||
}
|
||||
// within the box around the camera
|
||||
vec3 world = mod(place, size) - 0.5 * size;
|
||||
float opacity = timing.z;
|
||||
// fading at the walls of the box, where the particles wrap around, and right at the camera
|
||||
vec3 walls = abs(world) / (0.5 * size);
|
||||
opacity *= 1.0 - smoothstep(0.7, 1.0, max(max(walls.x, walls.y), walls.z));
|
||||
opacity *= smoothstep(0.15, 0.6, length(world));
|
||||
if (opacity <= 0.002)
|
||||
return;
|
||||
vec3 tail = -move * timing.y;
|
||||
float streak = length(tail);
|
||||
if (streak > 3.0)
|
||||
tail *= 3.0 / streak;
|
||||
if (false == particle_visible(planes, world + 0.5 * tail, 0.5 * length(tail) + width))
|
||||
return;
|
||||
vec3 color = particle_light((view * vec4(world, 1.0)).xyz, world, options.y);
|
||||
particle_emit(world, width, tail, color * options.z, opacity, uint(timing.w));
|
||||
}
|
||||
48
shaders/ogl46/particles_precipitation.frag
Normal file
48
shaders/ogl46/particles_precipitation.frag
Normal file
@@ -0,0 +1,48 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// rain and snow of the gpu (opengl46_gpu_particles, particles_precipitation.vert): a streak rounded at its ends, fainter towards
|
||||
// its tail, in the colour the compute pass lit it with, faded in front of the scene, fogged
|
||||
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 13) in vec4 f_color;
|
||||
layout(location = 17) in vec4 f_streak;
|
||||
layout(location = 16) in float f_soft;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
#include "particle_soft.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
void main()
|
||||
{
|
||||
// distance from the axis of the streak, in its radii
|
||||
float along = clamp(f_streak.y, 0.0, f_streak.w);
|
||||
float offaxis = length(vec2(f_streak.x, f_streak.y - along)) / max(f_streak.z, 1e-6);
|
||||
float alpha = f_color.a * (1.0 - smoothstep(0.35, 1.0, offaxis));
|
||||
alpha *= mix(1.0, 0.4, along / max(f_streak.w, 1e-6));
|
||||
alpha *= particle_soft_fade(f_soft, -f_pos.z);
|
||||
if (alpha <= 0.0)
|
||||
discard;
|
||||
vec4 color = vec4(apply_fog(f_color.rgb), alpha);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = color;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(color);
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, alpha * alpha_mult);
|
||||
}
|
||||
}
|
||||
60
shaders/ogl46/particles_precipitation.vert
Normal file
60
shaders/ogl46/particles_precipitation.vert
Normal file
@@ -0,0 +1,60 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
// rain and snow of the gpu (opengl46_gpu_particles): a streak per drop or flake the compute pass added to the draw, from where
|
||||
// it is to where it was a moment ago as the moving camera saw it, turned to face the camera, rounded at its ends. a particle
|
||||
// thinner than a pixel and a half is drawn that wide and as much fainter, so the distant ones don't flicker
|
||||
|
||||
layout(location = 0) in vec4 v_place; // xyz: the head, metres from the camera (world axes), w: width
|
||||
layout(location = 1) in vec4 v_shape; // xyz: from the head to the tail
|
||||
layout(location = 2) in vec4 v_color; // rgb: lit colour, a: opacity
|
||||
|
||||
layout(location = 3) out vec4 f_pos;
|
||||
layout(location = 13) out vec4 f_color;
|
||||
// x: across the streak, y: along it from the head, metres; z: its radius, w: its length
|
||||
layout(location = 17) out vec4 f_streak;
|
||||
layout(location = 16) out float f_soft;
|
||||
|
||||
layout(location = 4) out vec4 f_clip_pos;
|
||||
layout(location = 5) out vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
// x: metres it fades out over in front of the scene, 0: none
|
||||
layout(location = 0) uniform vec4 particle_soft;
|
||||
// x: width of a pixel one metre in front of the camera, metres
|
||||
layout(location = 1) uniform vec4 particle_pixel;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 head = (modelview * vec4(v_place.xyz, 1.0)).xyz;
|
||||
vec3 axis = mat3(modelview) * v_shape.xyz;
|
||||
float span = length(axis);
|
||||
vec3 direction = (span > 1e-5) ? axis / span : vec3(0.0, -1.0, 0.0);
|
||||
vec3 middle = head + 0.5 * axis;
|
||||
|
||||
float width = v_place.w;
|
||||
float alpha = v_color.a;
|
||||
float least = 1.5 * particle_pixel.x * max(-middle.z, 1e-3);
|
||||
if (width < least)
|
||||
{
|
||||
alpha *= width / least;
|
||||
width = least;
|
||||
}
|
||||
float radius = 0.5 * width;
|
||||
vec3 side = cross(direction, normalize(-middle));
|
||||
float sidelength = length(side);
|
||||
side = (sidelength > 1e-4) ? side / sidelength : vec3(1.0, 0.0, 0.0);
|
||||
|
||||
// a triangle strip of four corners: across, along
|
||||
vec2 corner = vec2(float(gl_VertexID & 1), float(gl_VertexID >> 1));
|
||||
float across = (corner.x * 2.0 - 1.0) * radius;
|
||||
float along = mix(-radius, span + radius, corner.y);
|
||||
f_pos = vec4(head + direction * along + side * across, 1.0);
|
||||
f_clip_pos = projection * f_pos;
|
||||
f_clip_future_pos = (projection * future) * f_pos;
|
||||
|
||||
gl_Position = f_clip_pos;
|
||||
f_streak = vec4(across, along, radius, span);
|
||||
f_color = vec4(v_color.rgb, alpha);
|
||||
f_soft = particle_soft.x;
|
||||
}
|
||||
44
shaders/ogl46/particles_smoke.frag
Normal file
44
shaders/ogl46/particles_smoke.frag
Normal file
@@ -0,0 +1,44 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
// the smoke of the gpu (opengl46_gpu_particles, particles_smoke.vert): the texture of the smoke in the colour the compute pass
|
||||
// lit the particle with, faded in front of the scene, fogged
|
||||
|
||||
layout(location = 13) in vec4 f_color;
|
||||
layout(location = 2) in vec2 f_coord;
|
||||
layout(location = 3) in vec4 f_pos;
|
||||
layout(location = 16) in float f_soft;
|
||||
|
||||
layout(location = 4) in vec4 f_clip_pos;
|
||||
layout(location = 5) in vec4 f_clip_future_pos;
|
||||
|
||||
#pragma texture(tex1, 0, sRGB_A)
|
||||
layout(binding = 0) uniform sampler2D tex1;
|
||||
|
||||
#include "common.glsl"
|
||||
#include "apply_fog.glsl"
|
||||
#include "tonemapping.glsl"
|
||||
#include "particle_soft.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
layout(location = 1) out vec4 out_motion;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 tex_color = texture(tex1, f_coord) * f_color;
|
||||
tex_color.a *= particle_soft_fade(f_soft, -f_pos.z);
|
||||
if (POSTFX_ENABLED)
|
||||
{
|
||||
out_color = vec4(apply_fog(tex_color.rgb), tex_color.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
out_color = tonemap(vec4(apply_fog(tex_color.rgb), tex_color.a));
|
||||
}
|
||||
if (MOTIONBLUR_ENABLED)
|
||||
{
|
||||
vec2 a = (f_clip_future_pos.xy / f_clip_future_pos.w) * 0.5 + 0.5;
|
||||
vec2 b = (f_clip_pos.xy / f_clip_pos.w) * 0.5 + 0.5;
|
||||
|
||||
out_motion = vec4(a - b, 0.0f, tex_color.a * alpha_mult);
|
||||
}
|
||||
}
|
||||
39
shaders/ogl46/particles_smoke.vert
Normal file
39
shaders/ogl46/particles_smoke.vert
Normal file
@@ -0,0 +1,39 @@
|
||||
#version 460 core
|
||||
#define STAGE_VERTEX 1
|
||||
// the smoke of the gpu (opengl46_gpu_particles): a billboard per particle the compute pass added to the draw, turned about the
|
||||
// axis of the view by the rotation of the particle (as opengl46_particles builds them on the cpu)
|
||||
|
||||
layout(location = 0) in vec4 v_place; // xyz: metres from the camera (world axes), w: size
|
||||
layout(location = 1) in vec4 v_shape; // x: rotation, radians
|
||||
layout(location = 2) in vec4 v_color; // rgb: lit colour, a: opacity
|
||||
|
||||
layout(location = 3) out vec4 f_pos;
|
||||
layout(location = 13) out vec4 f_color;
|
||||
layout(location = 2) out vec2 f_coord;
|
||||
// metres in front of the scene the particle fades out over, see particle_soft.glsl
|
||||
layout(location = 16) out float f_soft;
|
||||
|
||||
layout(location = 4) out vec4 f_clip_pos;
|
||||
layout(location = 5) out vec4 f_clip_future_pos;
|
||||
|
||||
#include "common.glsl"
|
||||
|
||||
// x: share of the size of the particle it fades out over in front of the scene (gfx.particles.soft), 0: none
|
||||
layout(location = 0) uniform vec4 particle_soft;
|
||||
|
||||
void main()
|
||||
{
|
||||
// a triangle strip of four corners
|
||||
vec2 corner = vec2(float(gl_VertexID & 1), float(gl_VertexID >> 1)) - 0.5;
|
||||
float c = cos(v_shape.x);
|
||||
float s = sin(v_shape.x);
|
||||
vec2 offset = vec2(c * corner.x - s * corner.y, s * corner.x + c * corner.y) * v_place.w;
|
||||
f_pos = modelview * vec4(v_place.xyz, 1.0) + vec4(offset, 0.0, 0.0);
|
||||
f_clip_pos = projection * f_pos;
|
||||
f_clip_future_pos = (projection * future) * f_pos;
|
||||
|
||||
gl_Position = f_clip_pos;
|
||||
f_coord = corner + 0.5;
|
||||
f_color = v_color;
|
||||
f_soft = particle_soft.x * 0.5 * v_place.w;
|
||||
}
|
||||
58
shaders/ogl46/particles_smoke_prepare.comp
Normal file
58
shaders/ogl46/particles_smoke_prepare.comp
Normal file
@@ -0,0 +1,58 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// the smoke of the gpu (opengl46_gpu_particles), in every colour pass which draws it: a thread per place of the storage. the
|
||||
// living particles the view sees are lit (particle_light.glsl) and added to the draw (particles_smoke.vert)
|
||||
|
||||
#include "common.glsl"
|
||||
#include "weather_common.glsl"
|
||||
#include "particle_light.glsl"
|
||||
#include "particle_common.glsl"
|
||||
|
||||
layout(local_size_x = PARTICLE_GROUP_SIZE) in;
|
||||
|
||||
layout(std430, binding = PARTICLE_SSBO_BINDING) readonly buffer particle_ssbo
|
||||
{
|
||||
smoke_particle_s particles[];
|
||||
};
|
||||
layout(std430, binding = PARTICLE_SOURCE_SSBO_BINDING) readonly buffer particle_source_ssbo
|
||||
{
|
||||
smoke_source_s sources[];
|
||||
};
|
||||
layout(std430, binding = PARTICLE_OWNER_SSBO_BINDING) readonly buffer particle_owner_ssbo
|
||||
{
|
||||
uint owners[];
|
||||
};
|
||||
|
||||
layout(location = 0) uniform uint places;
|
||||
// xyz: the origin of the particles less the camera, metres along the world axes
|
||||
layout(location = 1) uniform vec3 origin;
|
||||
// the world axes around the camera to the view space of the pass
|
||||
layout(location = 2) uniform mat4 view;
|
||||
// x: 1 when the smoke is lit by the lights of the pass, y: how much it scatters the light onwards, z: places for the draws
|
||||
layout(location = 3) uniform vec4 options;
|
||||
// planes of the view (world axes around the camera): a point is inside when dot(xyz, point) + w >= 0
|
||||
layout(location = 4) uniform vec4 planes[5];
|
||||
|
||||
void main()
|
||||
{
|
||||
uint place = gl_GlobalInvocationID.x;
|
||||
if (place >= places)
|
||||
return;
|
||||
smoke_particle_s particle = particles[place];
|
||||
if (particle.state.x <= 0.0)
|
||||
return;
|
||||
vec3 world = particle.position.xyz + origin;
|
||||
float size = particle.velocity.w;
|
||||
// the corners of the billboard turned anyhow
|
||||
if (false == particle_visible(planes, world, size * 0.7072))
|
||||
return;
|
||||
smoke_source_s source = sources[owners[place]];
|
||||
vec3 color = source.oldcolor.rgb;
|
||||
if (options.x > 0.5)
|
||||
color = source.color.rgb * particle_light((view * vec4(world, 1.0)).xyz, world, options.y);
|
||||
float opacity = clamp(particle.state.x, 0.0, 1.0);
|
||||
// showing up after the birth (smoke_source::fade_in())
|
||||
if (source.settling.w > 0.0)
|
||||
opacity *= smoothstep(0.0, source.settling.w, particle.state.y);
|
||||
particle_emit(world, size, vec3(particle.position.w, 0.0, 0.0), color, opacity, uint(options.z));
|
||||
}
|
||||
97
shaders/ogl46/particles_smoke_simulate.comp
Normal file
97
shaders/ogl46/particles_smoke_simulate.comp
Normal file
@@ -0,0 +1,97 @@
|
||||
#version 460 core
|
||||
#define STAGE_COMPUTE 1
|
||||
// the smoke of the gpu (opengl46_gpu_particles), once per update of the simulation: a thread per place of the storage. the
|
||||
// particles the sources had born in the update take the places after the ones of the last update (each source keeps its
|
||||
// places as a ring, the oldest give way), spread along the way the emitter went since the update before; the living ones
|
||||
// change as smoke_source::update() changes them on the cpu
|
||||
|
||||
#include "common.glsl"
|
||||
#include "particle_common.glsl"
|
||||
|
||||
layout(local_size_x = PARTICLE_GROUP_SIZE) in;
|
||||
|
||||
layout(std430, binding = PARTICLE_SSBO_BINDING) buffer particle_ssbo
|
||||
{
|
||||
smoke_particle_s particles[];
|
||||
};
|
||||
layout(std430, binding = PARTICLE_SOURCE_SSBO_BINDING) readonly buffer particle_source_ssbo
|
||||
{
|
||||
smoke_source_s sources[];
|
||||
};
|
||||
// the source of every place
|
||||
layout(std430, binding = PARTICLE_OWNER_SSBO_BINDING) readonly buffer particle_owner_ssbo
|
||||
{
|
||||
uint owners[];
|
||||
};
|
||||
|
||||
layout(location = 0) uniform uint places;
|
||||
// x: seconds of the update, yzw: added to the positions, as the origin moved
|
||||
layout(location = 1) uniform vec4 step;
|
||||
// xyz: the wind, m/s; w unused
|
||||
layout(location = 2) uniform vec4 wind;
|
||||
// x: number of the update, for the random numbers
|
||||
layout(location = 3) uniform uint seed;
|
||||
|
||||
// smoke_source::update( Particle ); false when the particle is dead
|
||||
bool advance(inout smoke_particle_s particle, smoke_source_s source, float seconds)
|
||||
{
|
||||
particle.state.x = clamp(particle.state.x + source.opacitychange.x * seconds, source.opacitychange.y, source.opacitychange.z);
|
||||
if (particle.state.x <= 0.0)
|
||||
return false;
|
||||
particle.velocity.w = clamp(particle.velocity.w + source.sizechange.x * seconds, source.sizechange.y, source.sizechange.z);
|
||||
// the jet widens with the way it goes
|
||||
if (source.sizechange.w > 0.0)
|
||||
particle.velocity.w = min(particle.velocity.w + source.sizechange.w * length(particle.velocity.xyz) * seconds, source.sizechange.z);
|
||||
if (source.settling.z > 0.5)
|
||||
{
|
||||
// the drag of the air takes the particle towards the velocity it settles at
|
||||
particle.velocity.xyz += (vec3(0.0, source.settling.x, 0.0) - particle.velocity.xyz) * (1.0 - exp(-source.settling.y * seconds));
|
||||
}
|
||||
else
|
||||
{
|
||||
// crude smoke dispersion
|
||||
particle.velocity.y += source.motion.x * particle.velocity.y * seconds;
|
||||
particle.velocity.y -= source.motion.y * particle.velocity.y * seconds;
|
||||
particle.velocity.y = max(source.motion.z, particle.velocity.y);
|
||||
}
|
||||
particle.position.xyz += particle.velocity.xyz * seconds;
|
||||
particle.position.xyz += 0.1 * particle.state.y * wind.xyz * seconds;
|
||||
particle.state.y += seconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint place = gl_GlobalInvocationID.x;
|
||||
if (place >= places)
|
||||
return;
|
||||
smoke_source_s source = sources[owners[place]];
|
||||
smoke_particle_s particle = particles[place];
|
||||
particle.position.xyz += step.yzw;
|
||||
|
||||
uint size = source.pool.y;
|
||||
uint order = (place - source.pool.x + size - source.pool.z) % size;
|
||||
if (order < source.pool.w)
|
||||
{
|
||||
// born in the update: smoke_source::particle_emitter::initialize()
|
||||
uint state = particle_hash(place ^ particle_hash(seed));
|
||||
float polar = mix(source.launch.x, source.launch.y, particle_random(state));
|
||||
float azimuth = radians(mix(-180.0, 180.0, particle_random(state)));
|
||||
vec3 direction = vec3(-sin(polar) * sin(azimuth), cos(polar), sin(polar) * cos(azimuth));
|
||||
particle.velocity.xyz = direction * mix(source.launch.z, source.launch.w, particle_random(state));
|
||||
particle.position.w = radians(mix(0.0, 360.0, particle_random(state)));
|
||||
particle.velocity.w = mix(source.initial.x, source.initial.y, particle_random(state));
|
||||
particle.state = vec4(mix(source.initial.z, source.initial.w, particle_random(state)), 0.0, 0.0, 0.0);
|
||||
// the births spread over the update: where the emitter was then, and the time since
|
||||
float share = (float(order) + 0.5) / float(source.pool.w);
|
||||
particle.position.xyz = mix(source.previous.xyz, source.location.xyz, share);
|
||||
if (false == advance(particle, source, step.x * (1.0 - share)))
|
||||
particle.state.x = 0.0;
|
||||
}
|
||||
else if (particle.state.x > 0.0)
|
||||
{
|
||||
if (false == advance(particle, source, step.x))
|
||||
particle.state.x = 0.0;
|
||||
}
|
||||
particles[place] = particle;
|
||||
}
|
||||
10
shaders/ogl46/pick.frag
Normal file
10
shaders/ogl46/pick.frag
Normal file
@@ -0,0 +1,10 @@
|
||||
#version 460 core
|
||||
#define STAGE_FRAGMENT 1
|
||||
#include "common.glsl"
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
out_color = vec4(param[0].rgb, 1.0);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user