mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-05 20:39:09 +01:00
Replace cubemap scenery reflections with SSR, add Vulkan ray traced reflections
This commit is contained in:
@@ -95,6 +95,7 @@
|
||||
#define CLOUD_COVERAGE_TEX 21
|
||||
#define CLOUD_HISTORY_TEX 22
|
||||
#define SCENE_DEPTH_TEX 23
|
||||
#define SSR_HISTORY_TEX 24
|
||||
#define WEATHER_UBO_BINDING 5
|
||||
#define WEATHER_SCRATCH_SSBO_BINDING 9
|
||||
#define MAX_CLOUD_LAYERS 4
|
||||
@@ -289,6 +290,13 @@ layout(std140, binding = SCENE_UBO_BINDING) uniform scene_ubo
|
||||
vec4 contact_shadow_lamps;
|
||||
// cascades of the sun shadows fitted to the depth of the view, see gl::scene_ubs
|
||||
vec4 shadow_fit;
|
||||
// screen space reflections and the reflection of the surroundings, see gl::scene_ubs
|
||||
mat4 ssr_reprojection;
|
||||
mat4 ssr_reprojection_cab;
|
||||
mat4 ssr_cab_box;
|
||||
vec4 ssr_params;
|
||||
vec4 ssr_params2;
|
||||
vec4 env_params;
|
||||
};
|
||||
|
||||
#ifdef STAGE_VERTEX
|
||||
|
||||
@@ -1,66 +1,49 @@
|
||||
// the environment map: the sky and the clouds around the camera (the reflection pass draws nothing else into it since the
|
||||
// screen space reflections took over the scenery), with its mipmaps and the levels prefiltered with the GGX lobe
|
||||
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 )
|
||||
// the world space direction of the reflection of the view off a surface of the normal (view space)
|
||||
vec3 envmap_direction(vec3 normal)
|
||||
{
|
||||
vec3 refvec = reflect(f_pos.xyz, normal); // view space
|
||||
return vec3(inv_view * vec4(refvec, 0.0)); // world space
|
||||
}
|
||||
|
||||
// the map in a world space direction, at a level of its mipmaps
|
||||
vec3 envmap_sample_lod(vec3 direction, float lod)
|
||||
{
|
||||
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;
|
||||
vec3 envcolor = textureLod(envmap, direction, lod).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.
|
||||
// face render and on NVIDIA can sample as undefined), and the mipmap
|
||||
// generation propagates a single NaN texel across the whole mip chain.
|
||||
// Without this guard a single bad texel 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));
|
||||
return max(envcolor, vec3(0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
envcolor = vec3(0.5);
|
||||
return 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 map in a world space direction for a surface of the given perceptual roughness (0 mirror, 1 matte)
|
||||
vec3 envmap_sample_rough(vec3 direction, 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);
|
||||
vec3 boxcolor = envmap_sample_lod(direction, 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;
|
||||
vec3 ggxcolor = textureLod(envmap_ggx, direction, 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
|
||||
@@ -71,3 +54,21 @@ vec3 envmap_color_rough(vec3 fragnormal, float roughness)
|
||||
return boxcolor;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 envmap_color( vec3 normal )
|
||||
{
|
||||
return envmap_sample_lod(envmap_direction(normal), 0.0);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
return envmap_sample_lod(envmap_direction(fragnormal), lod);
|
||||
}
|
||||
|
||||
// reflection of the surroundings in a surface of the given perceptual roughness (0 mirror, 1 matte)
|
||||
vec3 envmap_color_rough(vec3 fragnormal, float roughness)
|
||||
{
|
||||
return envmap_sample_rough(envmap_direction(fragnormal), roughness);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ layout(binding = HEADLIGHT_SHADOW_TEX) uniform sampler2DArrayShadow headlight_sh
|
||||
layout(binding = SCENE_DEPTH_TEX) uniform sampler2D scene_depth;
|
||||
|
||||
#include "envmapping.glsl"
|
||||
#include "ssr.glsl"
|
||||
#include "conversion.glsl"
|
||||
#include "weather_common.glsl"
|
||||
|
||||
@@ -657,6 +658,55 @@ vec3 add_sky_ambient(vec3 basecolor, vec3 fragnormal)
|
||||
return max(basecolor + sky_ambient_directional(worldnormal) * (sky_ambient_factor * share), vec3(0.0));
|
||||
}
|
||||
|
||||
// the light of the ground below the horizon of the environment map, which holds the sky alone: the ground of the albedo
|
||||
// env_params.x lit by the sky and the sun, as a surface facing up
|
||||
vec3 reflection_ground()
|
||||
{
|
||||
vec3 light = ambient;
|
||||
if (sky_ambient_factor != 0.0)
|
||||
light = max(light + sky_ambient_directional(vec3(0.0, 1.0, 0.0)) * sky_ambient_factor, vec3(0.0));
|
||||
light *= AMBIENT_SCALE;
|
||||
if (lights_count > 0U)
|
||||
{
|
||||
vec3 sun = -normalize(mat3(inv_view) * lights[0].dir);
|
||||
light += lights[0].color * (SUN_DIFFUSE_SCALE * lights[0].intensity * max(sun.y, 0.0));
|
||||
}
|
||||
return light * env_params.x;
|
||||
}
|
||||
|
||||
// the reflection of the surroundings the scene doesn't give (the screen space reflections, the ray tracing): the environment
|
||||
// map above the horizon (the sky and the clouds), the ground below it
|
||||
vec3 reflection_surroundings(vec3 fragnormal, float roughness)
|
||||
{
|
||||
vec3 direction = normalize(envmap_direction(fragnormal));
|
||||
vec3 sky = envmap_sample_rough(normalize(vec3(direction.x, max(direction.y, 0.0), direction.z)), roughness);
|
||||
float below = smoothstep(0.0, 0.25, -direction.y);
|
||||
if ((below <= 0.0) || (false == ENVMAP_ENABLED))
|
||||
return sky;
|
||||
return mix(sky, reflection_ground(), below);
|
||||
}
|
||||
|
||||
// the least weight of the reflection of the surroundings (its specular colour times its share) a surface traces the scene for
|
||||
const float REFLECTION_SCENE_WEIGHT = 0.004;
|
||||
|
||||
// the reflection of the scene in a surface of the given perceptual roughness: the colour times how much of the reflection the
|
||||
// scene gives (rgb), and that share (a), the rest is left to reflection_surroundings()
|
||||
vec4 reflection_scene(vec3 fragnormal, float roughness)
|
||||
{
|
||||
vec3 direction = normalize(reflect(f_pos.xyz, fragnormal));
|
||||
vec4 result = ssr_trace(f_pos.xyz, normalize(f_normal), direction, roughness);
|
||||
#ifdef REFLECTION_RAYS_ACTIVE
|
||||
// what the screen doesn't hold is traced through the scenery (shaders/vk14/rt_reflections.glsl)
|
||||
if ((result.a < 0.99) && (ssr_params2.z > 0.0) && (roughness <= ssr_params.z))
|
||||
{
|
||||
vec4 traced = rt_trace(f_pos.xyz, normalize(f_normal), direction, roughness);
|
||||
result.rgb += traced.rgb * (1.0 - result.a);
|
||||
result.a += traced.a * (1.0 - result.a);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -734,13 +784,22 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
|
||||
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));
|
||||
// the reflection of the surroundings: the scene where the screen (or the ray tracing) gives it, the sky and the ground of the
|
||||
// environment map elsewhere
|
||||
vec3 envcolor = vec3(0.0);
|
||||
vec4 scenecolor = vec4(0.0);
|
||||
if (!matte)
|
||||
{
|
||||
envcolor = reflection_surroundings(fragnormal, env_roughness);
|
||||
if (((ssr_params.x > 0.0) || (ssr_params2.z > 0.0)) && (cubemap_share * dot(cubemap_spec, vec3(1.0 / 3.0)) > REFLECTION_SCENE_WEIGHT))
|
||||
scenecolor = reflection_scene(fragnormal, env_roughness);
|
||||
}
|
||||
|
||||
if (lights_count == 0U)
|
||||
return fragcolor * diffuse_albedo
|
||||
+ emissioncolor * texturecolor
|
||||
+ fragcolor * env_spec * ambient_share
|
||||
+ envcolor * cubemap_spec * cubemap_share;
|
||||
+ (scenecolor.rgb + envcolor * (1.0 - scenecolor.a)) * cubemap_spec * cubemap_share;
|
||||
|
||||
vec3 light_dir = normalize(-lights[0].dir);
|
||||
float NdotL = max(dot(fragnormal, light_dir), 0.0);
|
||||
@@ -757,7 +816,8 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
|
||||
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
|
||||
// the environment map knows nothing of what shades the fragment: it is dimmed a little in the shadow of the sun (the scene the
|
||||
// reflection finds is lit as it is)
|
||||
float cubemap_shadow = 1.0;
|
||||
if (sunless)
|
||||
{
|
||||
@@ -771,7 +831,7 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
|
||||
|
||||
// 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);
|
||||
+ (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * cubemap_shadow)) * cubemap_spec * cubemap_share;
|
||||
|
||||
// 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
|
||||
|
||||
155
shaders/ogl46/ssr.glsl
Normal file
155
shaders/ogl46/ssr.glsl
Normal file
@@ -0,0 +1,155 @@
|
||||
// screen space reflections (gfx.ssr) of the colour pass of the main view, included by light_common.glsl. the reflection of a
|
||||
// surface is traced as a ray through the depth of the opaque scene drawn before the colour (scene_depth: the reverse z of a
|
||||
// projection with the far plane at infinity, a depth d is a surface n / d away along the axis of the view), and its colour is
|
||||
// taken from the image of the last frame (ssr_history) at the place the point hit had in that frame. what the screen doesn't
|
||||
// hold (off the image, behind something nearer, too far, too rough) is left to the caller: the alpha of the result says how much
|
||||
// of the reflection the screen gave. scene_ubs.ssr_params is set only for the main view, zero elsewhere
|
||||
layout(binding = SSR_HISTORY_TEX) uniform sampler2D ssr_history;
|
||||
|
||||
// the steps of a ray that found something are refined this many times, halving the step
|
||||
const int SSR_REFINE_STEPS = 5;
|
||||
|
||||
// a value 0 .. 1 different for the neighbouring pixels (interleaved gradient noise), and from frame to frame with the temporal
|
||||
// anti-aliasing, which blends the steps of the rays into a smooth image
|
||||
float ssr_noise()
|
||||
{
|
||||
vec2 pixel = gl_FragCoord.xy + 5.588238 * ssr_params.w;
|
||||
return fract(52.9829189 * fract(dot(pixel, vec2(0.06711056, 0.00583715))));
|
||||
}
|
||||
|
||||
// the distance along the axis of the view of the surface in the depth at a place of the image (0 .. 1), 0: nothing there (the sky)
|
||||
float ssr_surface(vec2 coords, vec2 size, float near)
|
||||
{
|
||||
float depth = texelFetch(scene_depth, clamp(ivec2(coords * size), ivec2(0), ivec2(size) - 1), 0).r;
|
||||
return (depth > 0.0 ? near / depth : 0.0);
|
||||
}
|
||||
|
||||
// the reflection the screen holds, of a surface at Position (view space) with the geometric normal Normal (view space, either
|
||||
// side), for a ray along Direction (view space, normalized) and the perceptual roughness Roughness: the colour (rgb) times how
|
||||
// much of it the screen holds (a)
|
||||
vec4 ssr_trace(vec3 position, vec3 normal, vec3 direction, float roughness)
|
||||
{
|
||||
float steps = ssr_params.x;
|
||||
if ((steps < 1.0) || (roughness > ssr_params.z))
|
||||
return vec4(0.0);
|
||||
// the side of the surface the camera sees; a ray going into it (a normal map turned far from the face) finds nothing
|
||||
if (dot(normal, position) > 0.0)
|
||||
normal = -normal;
|
||||
if (dot(normal, direction) <= 0.0)
|
||||
return vec4(0.0);
|
||||
vec2 size = vec2(textureSize(scene_depth, 0));
|
||||
float near = projection[3][2];
|
||||
float pixelscale = 2.0 / (abs(projection[1][1]) * size.y);
|
||||
// the ray starts two pixels off the surface, so the depth of the surface itself doesn't stop it
|
||||
vec4 start = projection * vec4(position, 1.0);
|
||||
vec3 origin = position + normal * (start.w * pixelscale * 2.0);
|
||||
vec4 c0 = projection * vec4(origin, 1.0);
|
||||
// the distance along the axis of the view changes with the ray: it ends in front of the near plane
|
||||
float dw = (projection * vec4(direction, 0.0)).w;
|
||||
float raylength = ssr_params.y;
|
||||
if (dw < 0.0)
|
||||
raylength = min(raylength, (c0.w - near * 2.0) / -dw);
|
||||
if (raylength <= 0.0)
|
||||
return vec4(0.0);
|
||||
vec3 end = origin + direction * raylength;
|
||||
vec4 c1 = projection * vec4(end, 1.0);
|
||||
// the points of the ray are interpolated across the image (u) with 1 / w, which is linear there
|
||||
float k0 = 1.0 / c0.w;
|
||||
float k1 = 1.0 / c1.w;
|
||||
vec2 s0 = c0.xy * k0;
|
||||
vec2 s1 = c1.xy * k1;
|
||||
vec2 delta = s1 - s0;
|
||||
// the part of the ray over the image
|
||||
float umax = 1.0;
|
||||
if (abs(delta.x) > 1e-6)
|
||||
umax = min(umax, ((delta.x > 0.0 ? 1.0 : -1.0) - s0.x) / delta.x);
|
||||
if (abs(delta.y) > 1e-6)
|
||||
umax = min(umax, ((delta.y > 0.0 ? 1.0 : -1.0) - s0.y) / delta.y);
|
||||
if (umax <= 0.0)
|
||||
return vec4(0.0);
|
||||
// not more steps than the pixels the ray crosses
|
||||
float pixels = length(delta * umax * size * 0.5);
|
||||
float count = clamp(floor(pixels), 1.0, steps);
|
||||
float jitter = ssr_noise();
|
||||
|
||||
float uprev = 0.0;
|
||||
float wprev = c0.w;
|
||||
float uhit = -1.0;
|
||||
for (float i = 0.0; i < count; i += 1.0)
|
||||
{
|
||||
float u = umax * min((i + jitter) / count, 1.0);
|
||||
float w = 1.0 / mix(k0, k1, u);
|
||||
vec2 coords = mix(s0, s1, u) * 0.5 + 0.5;
|
||||
float surface = ssr_surface(coords, size, near);
|
||||
float bias = w * pixelscale * 0.5;
|
||||
// the things in the depth are taken to be this thick behind their faces, with the length of the step on top
|
||||
float thickness = max(0.25, surface * 0.02) + abs(w - wprev);
|
||||
if ((surface > 0.0) && (w - surface > bias) && (w - surface < thickness))
|
||||
{
|
||||
// refined between the last step in front of the surface and this one behind it
|
||||
float lo = uprev;
|
||||
float hi = u;
|
||||
for (int r = 0; r < SSR_REFINE_STEPS; ++r)
|
||||
{
|
||||
float mid = 0.5 * (lo + hi);
|
||||
float wmid = 1.0 / mix(k0, k1, mid);
|
||||
float smid = ssr_surface(mix(s0, s1, mid) * 0.5 + 0.5, size, near);
|
||||
if ((smid > 0.0) && (wmid - smid > 0.0))
|
||||
hi = mid;
|
||||
else
|
||||
lo = mid;
|
||||
}
|
||||
// what the ray went behind has to be close to it there: otherwise the ray went behind something thin, and goes on
|
||||
float whit = 1.0 / mix(k0, k1, hi);
|
||||
float shit = ssr_surface(mix(s0, s1, hi) * 0.5 + 0.5, size, near);
|
||||
if ((shit > 0.0) && (abs(whit - shit) < max(0.25, shit * 0.02) + whit * pixelscale * 2.0))
|
||||
{
|
||||
uhit = hi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
uprev = u;
|
||||
wprev = w;
|
||||
}
|
||||
if (uhit < 0.0)
|
||||
return vec4(0.0);
|
||||
|
||||
// the point hit, view space
|
||||
vec3 hit = mix(origin * k0, end * k1, uhit) / mix(k0, k1, uhit);
|
||||
vec2 hitcoords = mix(s0, s1, uhit) * 0.5 + 0.5;
|
||||
// where it was in the last frame: the cab moves with the camera, the world stands still
|
||||
mat4 reprojection = ssr_reprojection;
|
||||
if (ssr_params2.y > 0.0)
|
||||
{
|
||||
vec3 box = (ssr_cab_box * vec4(hit, 1.0)).xyz;
|
||||
if (all(lessThanEqual(abs(box), vec3(1.0))))
|
||||
reprojection = ssr_reprojection_cab;
|
||||
}
|
||||
vec4 previous = reprojection * vec4(hit, 1.0);
|
||||
if (previous.w <= 0.0)
|
||||
return vec4(0.0);
|
||||
vec2 coords = (previous.xy / previous.w) * 0.5 + 0.5;
|
||||
|
||||
float share = 1.0;
|
||||
// faded towards the edges of the image, where it was hit and where it was in the last frame
|
||||
vec2 edges = min(min(hitcoords, 1.0 - hitcoords), min(coords, 1.0 - coords));
|
||||
share *= smoothstep(0.0, 0.04, min(edges.x, edges.y));
|
||||
// and towards the end of the ray
|
||||
float travelled = length(hit - origin);
|
||||
share *= 1.0 - smoothstep(0.75, 1.0, travelled / ssr_params.y);
|
||||
// the rougher surfaces hand over to the environment map
|
||||
share *= 1.0 - smoothstep(ssr_params.z * 0.7, ssr_params.z, roughness);
|
||||
// a ray towards the camera meets the backs of what the image shows
|
||||
share *= 1.0 - smoothstep(0.4, 0.9, direction.z);
|
||||
if (share <= 0.0)
|
||||
return vec4(0.0);
|
||||
|
||||
// the lobe of a rough surface covers more of the image the farther the point hit is: a blurrier level of the history
|
||||
float alpha = roughness * roughness;
|
||||
float hitpixel = max((projection * vec4(hit, 1.0)).w, near) * pixelscale;
|
||||
float lod = clamp(log2(max(2.0 * travelled * alpha / hitpixel, 1.0)), 0.0, ssr_params2.x);
|
||||
vec3 color = textureLod(ssr_history, coords, lod).rgb;
|
||||
if (any(isnan(color)) || any(isinf(color)))
|
||||
return vec4(0.0);
|
||||
return vec4(max(color, vec3(0.0)) * share, share);
|
||||
}
|
||||
@@ -59,7 +59,8 @@
|
||||
#define LOCAL_SHADOW_LAMPS_TEX 22
|
||||
#define HEADLIGHT_SHADOW_TEX 23
|
||||
#define SCENE_DEPTH_TEX 24
|
||||
#define PASS_BINDING_COUNT 25
|
||||
#define SSR_HISTORY_TEX 25
|
||||
#define PASS_BINDING_COUNT 26
|
||||
#define MATERIAL_TEXTURE_FIRST 0
|
||||
#define CL_CLUSTER_UBO 0
|
||||
#define CL_AABB_SSBO 1
|
||||
@@ -72,6 +73,10 @@
|
||||
#define POST_TEXTURES 4
|
||||
#define POST_EXPOSURE_SSBO 4
|
||||
#define COMPUTE_SET 1
|
||||
#define RT_SET 2
|
||||
#define RT_SCENE 0
|
||||
#define RT_RECORDS 1
|
||||
#define RT_TEXTURES 2
|
||||
#define PUSH_CONSTANTS_SIZE 112
|
||||
#define SPEC_OUTPUT_GAMMA 0
|
||||
#define SPEC_SHADOWMAP_ENABLED 1
|
||||
@@ -259,6 +264,12 @@ layout(std140, set = PASS_SET, binding = SCENE_UBO) uniform scene_ubo
|
||||
vec4 contact_shadow_params;
|
||||
vec4 contact_shadow_lamps;
|
||||
vec4 shadow_fit;
|
||||
mat4 ssr_reprojection;
|
||||
mat4 ssr_reprojection_cab;
|
||||
mat4 ssr_cab_box;
|
||||
vec4 ssr_params;
|
||||
vec4 ssr_params2;
|
||||
vec4 env_params;
|
||||
};
|
||||
|
||||
#ifndef LAYOUT_COMPUTE
|
||||
|
||||
@@ -1,66 +1,49 @@
|
||||
// the environment map: the sky and the clouds around the camera (the reflection pass draws nothing else into it since the
|
||||
// screen space reflections took over the scenery), with its mipmaps and the levels prefiltered with the GGX lobe
|
||||
layout(set = PASS_SET, binding = ENV_TEX) uniform samplerCube envmap;
|
||||
// the same map prefiltered with the GGX lobe (envmap_prefilter.comp), used when env_ggx is 1
|
||||
layout(set = PASS_SET, binding = ENV_GGX_TEX) uniform samplerCube envmap_ggx;
|
||||
|
||||
vec3 envmap_color( vec3 normal )
|
||||
// the world space direction of the reflection of the view off a surface of the normal (view space)
|
||||
vec3 envmap_direction(vec3 normal)
|
||||
{
|
||||
vec3 refvec = reflect(f_pos.xyz, normal); // view space
|
||||
return vec3(inv_view * vec4(refvec, 0.0)); // world space
|
||||
}
|
||||
|
||||
// the map in a world space direction, at a level of its mipmaps
|
||||
vec3 envmap_sample_lod(vec3 direction, float lod)
|
||||
{
|
||||
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;
|
||||
vec3 envcolor = textureLod(envmap, direction, lod).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.
|
||||
// face render and on NVIDIA can sample as undefined), and the mipmap
|
||||
// generation propagates a single NaN texel across the whole mip chain.
|
||||
// Without this guard a single bad texel 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));
|
||||
return max(envcolor, vec3(0.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
envcolor = vec3(0.5);
|
||||
return 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 map in a world space direction for a surface of the given perceptual roughness (0 mirror, 1 matte)
|
||||
vec3 envmap_sample_rough(vec3 direction, 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);
|
||||
vec3 boxcolor = envmap_sample_lod(direction, 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;
|
||||
vec3 ggxcolor = textureLod(envmap_ggx, direction, 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
|
||||
@@ -71,3 +54,21 @@ vec3 envmap_color_rough(vec3 fragnormal, float roughness)
|
||||
return boxcolor;
|
||||
}
|
||||
}
|
||||
|
||||
vec3 envmap_color( vec3 normal )
|
||||
{
|
||||
return envmap_sample_lod(envmap_direction(normal), 0.0);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
return envmap_sample_lod(envmap_direction(fragnormal), lod);
|
||||
}
|
||||
|
||||
// reflection of the surroundings in a surface of the given perceptual roughness (0 mirror, 1 matte)
|
||||
vec3 envmap_color_rough(vec3 fragnormal, float roughness)
|
||||
{
|
||||
return envmap_sample_rough(envmap_direction(fragnormal), roughness);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ layout(set = PASS_SET, binding = HEADLIGHT_SHADOW_TEX) uniform sampler2DArraySha
|
||||
layout(set = PASS_SET, binding = SCENE_DEPTH_TEX) uniform sampler2D scene_depth;
|
||||
|
||||
#include "envmapping.glsl"
|
||||
#include "ssr.glsl"
|
||||
#include "conversion.glsl"
|
||||
#include "weather_common.glsl"
|
||||
|
||||
@@ -661,6 +662,61 @@ vec3 add_sky_ambient(vec3 basecolor, vec3 fragnormal)
|
||||
return max(basecolor + sky_ambient_directional(worldnormal) * (sky_ambient_factor * share), vec3(0.0));
|
||||
}
|
||||
|
||||
// the light of the ground below the horizon of the environment map, which holds the sky alone: the ground of the albedo
|
||||
// env_params.x lit by the sky and the sun, as a surface facing up
|
||||
vec3 reflection_ground()
|
||||
{
|
||||
vec3 light = ambient;
|
||||
if (sky_ambient_factor != 0.0)
|
||||
light = max(light + sky_ambient_directional(vec3(0.0, 1.0, 0.0)) * sky_ambient_factor, vec3(0.0));
|
||||
light *= AMBIENT_SCALE;
|
||||
if (lights_count > 0U)
|
||||
{
|
||||
vec3 sun = -normalize(mat3(inv_view) * lights[0].dir);
|
||||
light += lights[0].color * (SUN_DIFFUSE_SCALE * lights[0].intensity * max(sun.y, 0.0));
|
||||
}
|
||||
return light * env_params.x;
|
||||
}
|
||||
|
||||
// the reflection of the surroundings the scene doesn't give (the screen space reflections, the ray tracing): the environment
|
||||
// map above the horizon (the sky and the clouds), the ground below it
|
||||
vec3 reflection_surroundings(vec3 fragnormal, float roughness)
|
||||
{
|
||||
vec3 direction = normalize(envmap_direction(fragnormal));
|
||||
vec3 sky = envmap_sample_rough(normalize(vec3(direction.x, max(direction.y, 0.0), direction.z)), roughness);
|
||||
float below = smoothstep(0.0, 0.25, -direction.y);
|
||||
if ((below <= 0.0) || (false == ENVMAP_ENABLED))
|
||||
return sky;
|
||||
return mix(sky, reflection_ground(), below);
|
||||
}
|
||||
|
||||
// the ray traced reflections, in the fragment shaders of the scene (the layout with the set of the ray tracing)
|
||||
#if defined(REFLECTION_RAYS) && defined(STAGE_FRAGMENT) && !defined(LAYOUT_COMPUTE) && !defined(LAYOUT_POST) && !defined(LAYOUT_CLUSTER) && !defined(LAYOUT_UI)
|
||||
#define REFLECTION_RAYS_ACTIVE 1
|
||||
#include "rt_reflections.glsl"
|
||||
#endif
|
||||
|
||||
// the least weight of the reflection of the surroundings (its specular colour times its share) a surface traces the scene for
|
||||
const float REFLECTION_SCENE_WEIGHT = 0.004;
|
||||
|
||||
// the reflection of the scene in a surface of the given perceptual roughness: the colour times how much of the reflection the
|
||||
// scene gives (rgb), and that share (a), the rest is left to reflection_surroundings()
|
||||
vec4 reflection_scene(vec3 fragnormal, float roughness)
|
||||
{
|
||||
vec3 direction = normalize(reflect(f_pos.xyz, fragnormal));
|
||||
vec4 result = ssr_trace(f_pos.xyz, normalize(f_normal), direction, roughness);
|
||||
#ifdef REFLECTION_RAYS_ACTIVE
|
||||
// what the screen doesn't hold is traced through the scenery (shaders/vk14/rt_reflections.glsl)
|
||||
if ((result.a < 0.99) && (ssr_params2.z > 0.0) && (roughness <= ssr_params.z))
|
||||
{
|
||||
vec4 traced = rt_trace(f_pos.xyz, normalize(f_normal), direction, roughness);
|
||||
result.rgb += traced.rgb * (1.0 - result.a);
|
||||
result.a += traced.a * (1.0 - result.a);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -738,13 +794,22 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
|
||||
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));
|
||||
// the reflection of the surroundings: the scene where the screen (or the ray tracing) gives it, the sky and the ground of the
|
||||
// environment map elsewhere
|
||||
vec3 envcolor = vec3(0.0);
|
||||
vec4 scenecolor = vec4(0.0);
|
||||
if (!matte)
|
||||
{
|
||||
envcolor = reflection_surroundings(fragnormal, env_roughness);
|
||||
if (((ssr_params.x > 0.0) || (ssr_params2.z > 0.0)) && (cubemap_share * dot(cubemap_spec, vec3(1.0 / 3.0)) > REFLECTION_SCENE_WEIGHT))
|
||||
scenecolor = reflection_scene(fragnormal, env_roughness);
|
||||
}
|
||||
|
||||
if (lights_count == 0U)
|
||||
return fragcolor * diffuse_albedo
|
||||
+ emissioncolor * texturecolor
|
||||
+ fragcolor * env_spec * ambient_share
|
||||
+ envcolor * cubemap_spec * cubemap_share;
|
||||
+ (scenecolor.rgb + envcolor * (1.0 - scenecolor.a)) * cubemap_spec * cubemap_share;
|
||||
|
||||
vec3 light_dir = normalize(-lights[0].dir);
|
||||
float NdotL = max(dot(fragnormal, light_dir), 0.0);
|
||||
@@ -761,7 +826,8 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
|
||||
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
|
||||
// the environment map knows nothing of what shades the fragment: it is dimmed a little in the shadow of the sun (the scene the
|
||||
// reflection finds is lit as it is)
|
||||
float cubemap_shadow = 1.0;
|
||||
if (sunless)
|
||||
{
|
||||
@@ -775,7 +841,7 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
|
||||
|
||||
// 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);
|
||||
+ (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * cubemap_shadow)) * cubemap_spec * cubemap_share;
|
||||
|
||||
// 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
|
||||
|
||||
147
shaders/vk14/rt_reflections.glsl
Normal file
147
shaders/vk14/rt_reflections.glsl
Normal file
@@ -0,0 +1,147 @@
|
||||
// the ray traced reflections of the vulkan renderer (gfx.raytracing, vk14::ray_tracing), included by light_common.glsl in the
|
||||
// fragment shaders of the scene when the renderer puts REFLECTION_RAYS before the sources (with the extensions: GL_EXT_ray_query for
|
||||
// the rays, GL_EXT_buffer_reference2 and _uvec2 for the vertices read by their device addresses, GL_EXT_nonuniform_qualifier for the
|
||||
// textures picked by the instance hit). what the screen space reflections don't find is traced through the scenery around the camera
|
||||
// the renderer gathered for the frame (the walk of the scene in its reflection mode: the sections, the models and the vehicles within
|
||||
// gfx.raytracing.range, as gfx.reflections.fidelity says), and the point hit is lit by the sky, the sun with a ray of its own towards
|
||||
// it and its own emission. the space of the rays is the world around the camera: the axes of the world, the camera at the origin
|
||||
|
||||
layout(set = RT_SET, binding = RT_SCENE) uniform accelerationStructureEXT rt_scene;
|
||||
|
||||
// what the renderer wrote for an instance (vk14::ray_tracing::record), picked by its custom index
|
||||
struct rt_record
|
||||
{
|
||||
uvec2 vertices; // the device address of the first vertex of the chunk (gfx::basic_vertex, 12 floats)
|
||||
uvec2 indices; // of its first index, zero: the vertices go one after another
|
||||
uint texindex; // in rt_textures, 0: none (white)
|
||||
uint flags; // 1: the texture is alpha tested with cutoff
|
||||
float cutoff; // the alpha test, as opacity of the model block
|
||||
float glow; // the light of its own, as emission of the model block
|
||||
vec4 colour; // the diffuse colour of the material, linear, as material_diffuse() makes it
|
||||
};
|
||||
|
||||
layout(std430, set = RT_SET, binding = RT_RECORDS) readonly buffer rt_record_ssbo
|
||||
{
|
||||
rt_record rt_records[];
|
||||
};
|
||||
|
||||
layout(set = RT_SET, binding = RT_TEXTURES) uniform sampler2D rt_textures[];
|
||||
|
||||
layout(buffer_reference, std430, buffer_reference_align = 4) readonly buffer rt_vertex_data
|
||||
{
|
||||
float rt_vertex[];
|
||||
};
|
||||
|
||||
layout(buffer_reference, std430, buffer_reference_align = 4) readonly buffer rt_index_data
|
||||
{
|
||||
uint rt_index[];
|
||||
};
|
||||
|
||||
const uint RT_FLAG_ALPHA = 1U;
|
||||
const uint RT_VERTEX_FLOATS = 12U;
|
||||
|
||||
// the vertices of a triangle of the record
|
||||
uvec3 rt_triangle(rt_record record, uint primitive)
|
||||
{
|
||||
if ((record.indices.x == 0U) && (record.indices.y == 0U))
|
||||
return uvec3(primitive * 3U) + uvec3(0U, 1U, 2U);
|
||||
rt_index_data indices = rt_index_data(record.indices);
|
||||
return uvec3(indices.rt_index[primitive * 3U], indices.rt_index[primitive * 3U + 1U], indices.rt_index[primitive * 3U + 2U]);
|
||||
}
|
||||
|
||||
// the texture coordinates of a point of a triangle, its barycentric coordinates Weights
|
||||
vec2 rt_coords(rt_record record, uvec3 triangle, vec2 weights)
|
||||
{
|
||||
rt_vertex_data vertices = rt_vertex_data(record.vertices);
|
||||
vec2 a = vec2(vertices.rt_vertex[triangle.x * RT_VERTEX_FLOATS + 6U], vertices.rt_vertex[triangle.x * RT_VERTEX_FLOATS + 7U]);
|
||||
vec2 b = vec2(vertices.rt_vertex[triangle.y * RT_VERTEX_FLOATS + 6U], vertices.rt_vertex[triangle.y * RT_VERTEX_FLOATS + 7U]);
|
||||
vec2 c = vec2(vertices.rt_vertex[triangle.z * RT_VERTEX_FLOATS + 6U], vertices.rt_vertex[triangle.z * RT_VERTEX_FLOATS + 7U]);
|
||||
return a * (1.0 - weights.x - weights.y) + b * weights.x + c * weights.y;
|
||||
}
|
||||
|
||||
vec3 rt_normal(rt_record record, uvec3 triangle, vec2 weights)
|
||||
{
|
||||
rt_vertex_data vertices = rt_vertex_data(record.vertices);
|
||||
vec3 a = vec3(vertices.rt_vertex[triangle.x * RT_VERTEX_FLOATS + 3U], vertices.rt_vertex[triangle.x * RT_VERTEX_FLOATS + 4U], vertices.rt_vertex[triangle.x * RT_VERTEX_FLOATS + 5U]);
|
||||
vec3 b = vec3(vertices.rt_vertex[triangle.y * RT_VERTEX_FLOATS + 3U], vertices.rt_vertex[triangle.y * RT_VERTEX_FLOATS + 4U], vertices.rt_vertex[triangle.y * RT_VERTEX_FLOATS + 5U]);
|
||||
vec3 c = vec3(vertices.rt_vertex[triangle.z * RT_VERTEX_FLOATS + 3U], vertices.rt_vertex[triangle.z * RT_VERTEX_FLOATS + 4U], vertices.rt_vertex[triangle.z * RT_VERTEX_FLOATS + 5U]);
|
||||
return a * (1.0 - weights.x - weights.y) + b * weights.x + c * weights.y;
|
||||
}
|
||||
|
||||
// the reflection the scenery gives, of a surface at Position (view space) with the geometric normal Normal (view space, either side),
|
||||
// for a ray along Direction (view space, normalized) and the perceptual roughness Roughness: the colour (rgb, times a) and whether
|
||||
// the ray hit something (a)
|
||||
vec4 rt_trace(vec3 position, vec3 normal, vec3 direction, float roughness)
|
||||
{
|
||||
mat3 toworld = mat3(inv_view);
|
||||
vec3 point = toworld * position;
|
||||
vec3 face = normalize(toworld * normal);
|
||||
// the side of the surface the camera sees, the ray starting a little off it
|
||||
if (dot(face, point) > 0.0)
|
||||
face = -face;
|
||||
vec3 ray = normalize(toworld * direction);
|
||||
vec3 origin = point + face * (0.02 + length(point) * 0.0005);
|
||||
|
||||
rayQueryEXT query;
|
||||
rayQueryInitializeEXT(query, rt_scene, gl_RayFlagsNoneEXT, 0xFF, origin, 0.0, ray, ssr_params2.w);
|
||||
while (rayQueryProceedEXT(query))
|
||||
{
|
||||
// the instances with alpha tested textures aren't opaque: the texture tells whether the triangle is hit there
|
||||
if (rayQueryGetIntersectionTypeEXT(query, false) == gl_RayQueryCandidateIntersectionTriangleEXT)
|
||||
{
|
||||
rt_record record = rt_records[rayQueryGetIntersectionInstanceCustomIndexEXT(query, false)];
|
||||
if ((record.flags & RT_FLAG_ALPHA) == 0U)
|
||||
{
|
||||
rayQueryConfirmIntersectionEXT(query);
|
||||
continue;
|
||||
}
|
||||
uvec3 triangle = rt_triangle(record, uint(rayQueryGetIntersectionPrimitiveIndexEXT(query, false)));
|
||||
vec2 coords = rt_coords(record, triangle, rayQueryGetIntersectionBarycentricsEXT(query, false));
|
||||
float alpha = textureLod(rt_textures[nonuniformEXT(record.texindex)], coords, 0.0).a;
|
||||
if (alpha >= record.cutoff)
|
||||
rayQueryConfirmIntersectionEXT(query);
|
||||
}
|
||||
}
|
||||
if (rayQueryGetIntersectionTypeEXT(query, true) != gl_RayQueryCommittedIntersectionTriangleEXT)
|
||||
return vec4(0.0);
|
||||
|
||||
float distance = rayQueryGetIntersectionTEXT(query, true);
|
||||
rt_record record = rt_records[rayQueryGetIntersectionInstanceCustomIndexEXT(query, true)];
|
||||
uvec3 triangle = rt_triangle(record, uint(rayQueryGetIntersectionPrimitiveIndexEXT(query, true)));
|
||||
vec2 weights = rayQueryGetIntersectionBarycentricsEXT(query, true);
|
||||
mat4x3 objecttoworld = rayQueryGetIntersectionObjectToWorldEXT(query, true);
|
||||
vec3 hitnormal = normalize(transpose(inverse(mat3(objecttoworld))) * rt_normal(record, triangle, weights));
|
||||
if (dot(hitnormal, ray) > 0.0)
|
||||
hitnormal = -hitnormal;
|
||||
vec3 hit = origin + ray * distance;
|
||||
|
||||
// the colour of the surface: the farther and the rougher, the blurrier the level of the texture
|
||||
vec3 albedo = record.colour.rgb;
|
||||
if (record.texindex != 0U)
|
||||
{
|
||||
vec2 coords = rt_coords(record, triangle, weights);
|
||||
float lod = clamp(log2(1.0 + distance * 0.05) * 2.0 + roughness * 6.0, 0.0, 12.0);
|
||||
albedo *= textureLod(rt_textures[nonuniformEXT(record.texindex)], coords, lod).rgb;
|
||||
}
|
||||
// the light of the sky, the sun where nothing stands between the point and it, and the light of its own
|
||||
vec3 light = max(ambient + sky_ambient_directional(hitnormal) * sky_ambient_factor, vec3(0.0)) * AMBIENT_SCALE;
|
||||
if (lights_count > 0U)
|
||||
{
|
||||
vec3 sun = -normalize(toworld * lights[0].dir);
|
||||
float facing = dot(hitnormal, sun);
|
||||
if (facing > 0.0)
|
||||
{
|
||||
rayQueryEXT shadow;
|
||||
rayQueryInitializeEXT(shadow, rt_scene, gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT, 0xFF, hit + hitnormal * (0.02 + distance * 0.0005), 0.0, sun, ssr_params2.w);
|
||||
while (rayQueryProceedEXT(shadow))
|
||||
{
|
||||
}
|
||||
if (rayQueryGetIntersectionTypeEXT(shadow, true) == gl_RayQueryCommittedIntersectionNoneEXT)
|
||||
light += lights[0].color * (SUN_DIFFUSE_SCALE * lights[0].intensity * cloud_shadow(hit) * pow(facing, SUN_NDOTL_SHARPNESS));
|
||||
}
|
||||
}
|
||||
vec3 color = albedo * (light + vec3(record.glow));
|
||||
if (any(isnan(color)) || any(isinf(color)))
|
||||
return vec4(0.0);
|
||||
return vec4(max(color, vec3(0.0)), 1.0);
|
||||
}
|
||||
155
shaders/vk14/ssr.glsl
Normal file
155
shaders/vk14/ssr.glsl
Normal file
@@ -0,0 +1,155 @@
|
||||
// screen space reflections (gfx.ssr) of the colour pass of the main view, included by light_common.glsl. the reflection of a
|
||||
// surface is traced as a ray through the depth of the opaque scene drawn before the colour (scene_depth: the reverse z of a
|
||||
// projection with the far plane at infinity, a depth d is a surface n / d away along the axis of the view), and its colour is
|
||||
// taken from the image of the last frame (ssr_history) at the place the point hit had in that frame. what the screen doesn't
|
||||
// hold (off the image, behind something nearer, too far, too rough) is left to the caller: the alpha of the result says how much
|
||||
// of the reflection the screen gave. scene_ubs.ssr_params is set only for the main view, zero elsewhere
|
||||
layout(set = PASS_SET, binding = SSR_HISTORY_TEX) uniform sampler2D ssr_history;
|
||||
|
||||
// the steps of a ray that found something are refined this many times, halving the step
|
||||
const int SSR_REFINE_STEPS = 5;
|
||||
|
||||
// a value 0 .. 1 different for the neighbouring pixels (interleaved gradient noise), and from frame to frame with the temporal
|
||||
// anti-aliasing, which blends the steps of the rays into a smooth image
|
||||
float ssr_noise()
|
||||
{
|
||||
vec2 pixel = gl_FragCoord.xy + 5.588238 * ssr_params.w;
|
||||
return fract(52.9829189 * fract(dot(pixel, vec2(0.06711056, 0.00583715))));
|
||||
}
|
||||
|
||||
// the distance along the axis of the view of the surface in the depth at a place of the image (0 .. 1), 0: nothing there (the sky)
|
||||
float ssr_surface(vec2 coords, vec2 size, float near)
|
||||
{
|
||||
float depth = texelFetch(scene_depth, clamp(ivec2(coords * size), ivec2(0), ivec2(size) - 1), 0).r;
|
||||
return (depth > 0.0 ? near / depth : 0.0);
|
||||
}
|
||||
|
||||
// the reflection the screen holds, of a surface at Position (view space) with the geometric normal Normal (view space, either
|
||||
// side), for a ray along Direction (view space, normalized) and the perceptual roughness Roughness: the colour (rgb) times how
|
||||
// much of it the screen holds (a)
|
||||
vec4 ssr_trace(vec3 position, vec3 normal, vec3 direction, float roughness)
|
||||
{
|
||||
float steps = ssr_params.x;
|
||||
if ((steps < 1.0) || (roughness > ssr_params.z))
|
||||
return vec4(0.0);
|
||||
// the side of the surface the camera sees; a ray going into it (a normal map turned far from the face) finds nothing
|
||||
if (dot(normal, position) > 0.0)
|
||||
normal = -normal;
|
||||
if (dot(normal, direction) <= 0.0)
|
||||
return vec4(0.0);
|
||||
vec2 size = vec2(textureSize(scene_depth, 0));
|
||||
float near = projection[3][2];
|
||||
float pixelscale = 2.0 / (abs(projection[1][1]) * size.y);
|
||||
// the ray starts two pixels off the surface, so the depth of the surface itself doesn't stop it
|
||||
vec4 start = projection * vec4(position, 1.0);
|
||||
vec3 origin = position + normal * (start.w * pixelscale * 2.0);
|
||||
vec4 c0 = projection * vec4(origin, 1.0);
|
||||
// the distance along the axis of the view changes with the ray: it ends in front of the near plane
|
||||
float dw = (projection * vec4(direction, 0.0)).w;
|
||||
float raylength = ssr_params.y;
|
||||
if (dw < 0.0)
|
||||
raylength = min(raylength, (c0.w - near * 2.0) / -dw);
|
||||
if (raylength <= 0.0)
|
||||
return vec4(0.0);
|
||||
vec3 end = origin + direction * raylength;
|
||||
vec4 c1 = projection * vec4(end, 1.0);
|
||||
// the points of the ray are interpolated across the image (u) with 1 / w, which is linear there
|
||||
float k0 = 1.0 / c0.w;
|
||||
float k1 = 1.0 / c1.w;
|
||||
vec2 s0 = c0.xy * k0;
|
||||
vec2 s1 = c1.xy * k1;
|
||||
vec2 delta = s1 - s0;
|
||||
// the part of the ray over the image
|
||||
float umax = 1.0;
|
||||
if (abs(delta.x) > 1e-6)
|
||||
umax = min(umax, ((delta.x > 0.0 ? 1.0 : -1.0) - s0.x) / delta.x);
|
||||
if (abs(delta.y) > 1e-6)
|
||||
umax = min(umax, ((delta.y > 0.0 ? 1.0 : -1.0) - s0.y) / delta.y);
|
||||
if (umax <= 0.0)
|
||||
return vec4(0.0);
|
||||
// not more steps than the pixels the ray crosses
|
||||
float pixels = length(delta * umax * size * 0.5);
|
||||
float count = clamp(floor(pixels), 1.0, steps);
|
||||
float jitter = ssr_noise();
|
||||
|
||||
float uprev = 0.0;
|
||||
float wprev = c0.w;
|
||||
float uhit = -1.0;
|
||||
for (float i = 0.0; i < count; i += 1.0)
|
||||
{
|
||||
float u = umax * min((i + jitter) / count, 1.0);
|
||||
float w = 1.0 / mix(k0, k1, u);
|
||||
vec2 coords = mix(s0, s1, u) * 0.5 + 0.5;
|
||||
float surface = ssr_surface(coords, size, near);
|
||||
float bias = w * pixelscale * 0.5;
|
||||
// the things in the depth are taken to be this thick behind their faces, with the length of the step on top
|
||||
float thickness = max(0.25, surface * 0.02) + abs(w - wprev);
|
||||
if ((surface > 0.0) && (w - surface > bias) && (w - surface < thickness))
|
||||
{
|
||||
// refined between the last step in front of the surface and this one behind it
|
||||
float lo = uprev;
|
||||
float hi = u;
|
||||
for (int r = 0; r < SSR_REFINE_STEPS; ++r)
|
||||
{
|
||||
float mid = 0.5 * (lo + hi);
|
||||
float wmid = 1.0 / mix(k0, k1, mid);
|
||||
float smid = ssr_surface(mix(s0, s1, mid) * 0.5 + 0.5, size, near);
|
||||
if ((smid > 0.0) && (wmid - smid > 0.0))
|
||||
hi = mid;
|
||||
else
|
||||
lo = mid;
|
||||
}
|
||||
// what the ray went behind has to be close to it there: otherwise the ray went behind something thin, and goes on
|
||||
float whit = 1.0 / mix(k0, k1, hi);
|
||||
float shit = ssr_surface(mix(s0, s1, hi) * 0.5 + 0.5, size, near);
|
||||
if ((shit > 0.0) && (abs(whit - shit) < max(0.25, shit * 0.02) + whit * pixelscale * 2.0))
|
||||
{
|
||||
uhit = hi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
uprev = u;
|
||||
wprev = w;
|
||||
}
|
||||
if (uhit < 0.0)
|
||||
return vec4(0.0);
|
||||
|
||||
// the point hit, view space
|
||||
vec3 hit = mix(origin * k0, end * k1, uhit) / mix(k0, k1, uhit);
|
||||
vec2 hitcoords = mix(s0, s1, uhit) * 0.5 + 0.5;
|
||||
// where it was in the last frame: the cab moves with the camera, the world stands still
|
||||
mat4 reprojection = ssr_reprojection;
|
||||
if (ssr_params2.y > 0.0)
|
||||
{
|
||||
vec3 box = (ssr_cab_box * vec4(hit, 1.0)).xyz;
|
||||
if (all(lessThanEqual(abs(box), vec3(1.0))))
|
||||
reprojection = ssr_reprojection_cab;
|
||||
}
|
||||
vec4 previous = reprojection * vec4(hit, 1.0);
|
||||
if (previous.w <= 0.0)
|
||||
return vec4(0.0);
|
||||
vec2 coords = (previous.xy / previous.w) * 0.5 + 0.5;
|
||||
|
||||
float share = 1.0;
|
||||
// faded towards the edges of the image, where it was hit and where it was in the last frame
|
||||
vec2 edges = min(min(hitcoords, 1.0 - hitcoords), min(coords, 1.0 - coords));
|
||||
share *= smoothstep(0.0, 0.04, min(edges.x, edges.y));
|
||||
// and towards the end of the ray
|
||||
float travelled = length(hit - origin);
|
||||
share *= 1.0 - smoothstep(0.75, 1.0, travelled / ssr_params.y);
|
||||
// the rougher surfaces hand over to the environment map
|
||||
share *= 1.0 - smoothstep(ssr_params.z * 0.7, ssr_params.z, roughness);
|
||||
// a ray towards the camera meets the backs of what the image shows
|
||||
share *= 1.0 - smoothstep(0.4, 0.9, direction.z);
|
||||
if (share <= 0.0)
|
||||
return vec4(0.0);
|
||||
|
||||
// the lobe of a rough surface covers more of the image the farther the point hit is: a blurrier level of the history
|
||||
float alpha = roughness * roughness;
|
||||
float hitpixel = max((projection * vec4(hit, 1.0)).w, near) * pixelscale;
|
||||
float lod = clamp(log2(max(2.0 * travelled * alpha / hitpixel, 1.0)), 0.0, ssr_params2.x);
|
||||
vec3 color = textureLod(ssr_history, coords, lod).rgb;
|
||||
if (any(isnan(color)) || any(isinf(color)))
|
||||
return vec4(0.0);
|
||||
return vec4(max(color, vec3(0.0)) * share, share);
|
||||
}
|
||||
Reference in New Issue
Block a user