17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-30 14:20:28 +01:00

Take the ambient occlusion into the lighting and add a probe of the cab

This commit is contained in:
2026-10-27 13:23:52 +01:00
parent ac0b4e5f15
commit 9dde5b0e5e
43 changed files with 2402 additions and 45 deletions

View File

@@ -0,0 +1,20 @@
#version 460 core
#define STAGE_FRAGMENT 1
// the probe of the occupied cab (gfx::cab_probe), the materials without the alpha test (cabprobe.glsl)
layout(location = 0) in vec3 f_normal;
layout(location = 2) in vec2 f_coord;
layout(location = 3) in vec4 f_pos;
#include "common.glsl"
#pragma texture(diffuse, 0, sRGB_A)
layout(binding = 0) uniform sampler2D diffuse;
// the cube shadow map of the cab lamp, see gl::LOCAL_SHADOW_TEX
layout(binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow probe_shadowmap;
#include "cabprobe.glsl"
void main()
{
probe_shade(texture(diffuse, f_coord));
}

View File

@@ -0,0 +1,52 @@
#version 460 core
#define STAGE_GEOMETRY 1
// draws the cab into the six faces of its probe at once, one invocation per face (as shadowcube.geom draws the cube shadow maps of
// the lamps). the input is in the space of the probe; the faces go in the order of the faces of a cube map (+x, -x, +y, -y, +z,
// -z), each looks the way glm::lookAt() with the usual up vectors of cube maps sets it, with a 90 degree projection of infinite
// reach and reversed depth like the one of the scene: the depth is near / d, d the distance along the axis of the face
layout(triangles, invocations = 6) in;
layout(triangle_strip, max_vertices = 3) out;
layout(location = 20) in vec3 g_pos[];
layout(location = 21) in vec3 g_normal[];
layout(location = 18) in vec2 g_coord[];
layout(location = 0) out vec3 f_normal;
layout(location = 2) out vec2 f_coord;
layout(location = 3) out vec4 f_pos;
// near plane of the probe, metres
const float PROBE_NEAR = 0.05;
// right, up and forward axes of the faces
const vec3 face_right[6] = vec3[](vec3(0.0, 0.0, -1.0), vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0));
const vec3 face_up[6] = vec3[](vec3(0.0, -1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0), vec3(0.0, -1.0, 0.0), vec3(0.0, -1.0, 0.0));
const vec3 face_forward[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0));
void main()
{
int face = gl_InvocationID;
vec4 clip[3];
for (int i = 0; i < 3; i++)
clip[i] = vec4(dot(g_pos[i], face_right[face]), dot(g_pos[i], face_up[face]), PROBE_NEAR, dot(g_pos[i], face_forward[face]));
// triangles wholly outside of the face are not sent further
if ((clip[0].w < PROBE_NEAR) && (clip[1].w < PROBE_NEAR) && (clip[2].w < PROBE_NEAR))
return;
if ((clip[0].x > clip[0].w) && (clip[1].x > clip[1].w) && (clip[2].x > clip[2].w))
return;
if ((clip[0].x < -clip[0].w) && (clip[1].x < -clip[1].w) && (clip[2].x < -clip[2].w))
return;
if ((clip[0].y > clip[0].w) && (clip[1].y > clip[1].w) && (clip[2].y > clip[2].w))
return;
if ((clip[0].y < -clip[0].w) && (clip[1].y < -clip[1].w) && (clip[2].y < -clip[2].w))
return;
for (int i = 0; i < 3; i++)
{
gl_Layer = face;
gl_Position = clip[i];
f_normal = g_normal[i];
f_coord = g_coord[i];
f_pos = vec4(g_pos[i], 1.0);
EmitVertex();
}
EndPrimitive();
}

View File

@@ -0,0 +1,61 @@
// the shading of the probe of the occupied cab (gfx::cab_probe), for cabprobe.frag and cabprobe_alpha.frag: what the cab shows
// towards the middle of its box lit by the cab lamp alone, with the cube shadow map of the lamp (and what glows in it, the lights
// of the desk), and its albedo, each with 1 in the alpha: the probe is cleared to nothing, so the alpha of a direction tells how
// much of it the cab fills, and both are premultiplied by it. the light of the cab lamp is the one the shading gives it
// (calc_local_light() in light_common.glsl); the ambient light and the sun are left out, the cab gets those as before. the
// parameters of the lamp are in the block of the scene (gl::scene_ubs::cab_probe_lamp), in the space of the probe
layout(location = 0) out vec4 out_light;
layout(location = 1) out vec4 out_albedo;
// closest distance (squared) the light of the lamp is computed for, as LOCAL_LIGHT_MIN_DISTANCE2 of light_common.glsl
const float PROBE_LIGHT_MIN_DISTANCE2 = 0.01;
// the diffuse colour of the submodel or shape, as material_diffuse() of light_common.glsl
vec3 probe_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));
}
// share of the light of the cab lamp held back before the point of the space of its cube shadow map (origin at the lamp), 0..1, as
// local_light_shadow() of light_common.glsl takes it with a single sample
float probe_lamp_shadow(vec3 position, vec3 normal)
{
vec4 weights = cab_probe_lamp_shadow;
float distance = length(position);
float texel = 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)));
vec3 axes = abs(position);
float major = max(axes.x, max(axes.y, axes.z));
float reference = weights.y / max(major - texel * 1.5, weights.y);
return texture(probe_shadowmap, vec4(position, weights.x), reference);
}
void probe_shade(vec4 tex_color)
{
vec3 albedo = tex_color.rgb * probe_material_diffuse();
vec3 position = f_pos.xyz;
vec3 normal = normalize(f_normal);
// the walls are drawn from both sides: the side the probe sees
if (dot(normal, position) > 0.0)
normal = -normal;
vec3 light = vec3(emission);
if (cab_probe_lamp.w > 0.0)
{
vec3 tolight = cab_probe_lamp.xyz - position;
float distance2 = dot(tolight, tolight);
vec3 light_dir = tolight * inversesqrt(max(distance2, 1e-8));
float edge = clamp(1.0 - distance2 * distance2 * (cab_probe_lamp.w * cab_probe_lamp.w), 0.0, 1.0);
float atten = edge * edge / max(distance2, PROBE_LIGHT_MIN_DISTANCE2);
float NdotL = max(dot(normal, light_dir), 0.0);
if ((atten > 0.0) && (NdotL > 0.0) && (cab_probe_lamp_color.a > 0.5))
atten *= 1.0 - probe_lamp_shadow(-tolight, normal);
light += cab_probe_lamp_color.rgb * (NdotL * atten);
}
out_light = vec4(albedo * light, 1.0);
out_albedo = vec4(albedo, 1.0);
}

View File

@@ -0,0 +1,21 @@
#version 460 core
#define STAGE_VERTEX 1
// the occupied cab drawn into the six faces of its probe at once (gfx::cab_probe, cabprobe.geom picks the faces). the modelview
// takes the cab model to the space of the probe: the axes of the model, the origin in the middle of the box of the cab
layout(location = 0) in vec3 v_vert;
layout(location = 1) in vec3 v_normal;
layout(location = 2) in vec2 v_coord;
layout(location = 20) out vec3 g_pos;
layout(location = 21) out vec3 g_normal;
layout(location = 18) out vec2 g_coord;
#include "common.glsl"
void main()
{
g_pos = (effective_modelview() * vec4(v_vert, 1.0)).xyz;
g_normal = effective_modelviewnormal() * v_normal;
g_coord = v_coord;
gl_Position = vec4(g_pos, 1.0);
}

View File

@@ -0,0 +1,24 @@
#version 460 core
#define STAGE_FRAGMENT 1
// the probe of the occupied cab (gfx::cab_probe), the materials whose colour shader tests the alpha of the diffuse texture against
// the opacity of the material, with the test of depthprepass_alpha.frag (cabprobe.glsl)
layout(location = 0) in vec3 f_normal;
layout(location = 2) in vec2 f_coord;
layout(location = 3) in vec4 f_pos;
#include "common.glsl"
#pragma texture(diffuse, 0, sRGB_A)
layout(binding = 0) uniform sampler2D diffuse;
// the cube shadow map of the cab lamp, see gl::LOCAL_SHADOW_TEX
layout(binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow probe_shadowmap;
#include "cabprobe.glsl"
void main()
{
vec4 tex_color = texture(diffuse, f_coord);
if (tex_color.a < opacity + 1.0 / 255.0)
discard;
probe_shade(tex_color);
}

View File

@@ -0,0 +1,93 @@
#version 460 core
#define STAGE_COMPUTE 1
// the light the occupied cab lights itself with again, from its probe (gfx::cab_probe), in one work group: the light of its lamps the
// cab reflects towards the middle of its box, as spherical harmonics of bands 0..2, convolved with the cosine lobe and divided by
// pi, so their value for a direction is the light a white lambertian surface facing it gets, in the units of the ambient light
// (light_common.glsl, cab_probe_bounce()). the light is taken from a level of the probe of at most CAB_PROBE_SH_SIZE pixels a
// face, each pixel weighed by its solid angle. the basis is the one of the sky (gl::sky_data)
#include "common.glsl"
layout(local_size_x = CAB_PROBE_SH_GROUP_SIZE) in;
layout(binding = 0) uniform samplerCube probe_light;
layout(std430, binding = CAB_PROBE_SSBO_BINDING) writeonly buffer cab_probe_result_ssbo
{
vec4 result_sh[9];
};
// x: the level of the probe read, y: its size, pixels
layout(location = 0) uniform ivec2 level;
shared vec3 partial[CAB_PROBE_SH_GROUP_SIZE][9];
shared float partialweight[CAB_PROBE_SH_GROUP_SIZE];
// the direction through the middle of the pixel (s, t) of the face, -1..1 across it, in the order of the faces of a cube map
vec3 face_direction(int face, float s, float t)
{
switch (face)
{
case 0: return vec3(1.0, -t, -s);
case 1: return vec3(-1.0, -t, s);
case 2: return vec3(s, 1.0, t);
case 3: return vec3(s, -1.0, -t);
case 4: return vec3(s, -t, 1.0);
default: return vec3(-s, -t, -1.0);
}
}
void main()
{
uint invocation = gl_LocalInvocationID.x;
int size = max(level.y, 1);
int texels = 6 * size * size;
vec3 sum[9];
for (int i = 0; i < 9; ++i)
sum[i] = vec3(0.0);
float weights = 0.0;
for (int index = int(invocation); index < texels; index += CAB_PROBE_SH_GROUP_SIZE)
{
int face = index / (size * size);
int texel = index - face * size * size;
float s = (float(texel % size) + 0.5) / float(size) * 2.0 - 1.0;
float t = (float(texel / size) + 0.5) / float(size) * 2.0 - 1.0;
vec3 direction = face_direction(face, s, t);
// the solid angle of the pixel
float lengthsquared = dot(direction, direction);
float weight = 1.0 / (lengthsquared * sqrt(lengthsquared));
vec3 n = direction * inversesqrt(lengthsquared);
vec3 light = textureLod(probe_light, n, float(level.x)).rgb * weight;
sum[0] += light * 0.282095;
sum[1] += light * (0.488603 * n.y);
sum[2] += light * (0.488603 * n.z);
sum[3] += light * (0.488603 * n.x);
sum[4] += light * (1.092548 * n.x * n.y);
sum[5] += light * (1.092548 * n.y * n.z);
sum[6] += light * (0.315392 * (3.0 * n.z * n.z - 1.0));
sum[7] += light * (1.092548 * n.x * n.z);
sum[8] += light * (0.546274 * (n.x * n.x - n.y * n.y));
weights += weight;
}
for (int i = 0; i < 9; ++i)
partial[invocation][i] = sum[i];
partialweight[invocation] = weights;
barrier();
for (uint stride = CAB_PROBE_SH_GROUP_SIZE / 2; stride > 0U; stride /= 2U)
{
if (invocation < stride)
{
for (int i = 0; i < 9; ++i)
partial[invocation][i] += partial[invocation + stride][i];
partialweight[invocation] += partialweight[invocation + stride];
}
barrier();
}
if (invocation < 9U)
{
// the weights add up to the whole sphere, 4 pi; the cosine lobe over pi: 1 for band 0, 2/3 for band 1, 1/4 for band 2
float sphere = 12.566371 / max(partialweight[0], 1e-6);
float band = (invocation == 0U) ? 1.0 : (invocation < 4U) ? (2.0 / 3.0) : 0.25;
result_sh[invocation] = vec4(partial[0][invocation] * (sphere * band), 0.0);
}
}

View File

@@ -7,7 +7,7 @@
// 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
// 17 f_streak, 18 g_coord, 19 g_coords, 20 g_pos, 21 g_normal
#ifndef COMMON_GLSL
#define COMMON_GLSL
@@ -106,6 +106,12 @@
#define CLOUD_HISTORY_TEX 22
#define SCENE_DEPTH_TEX 23
#define SSR_HISTORY_TEX 24
#define SCENE_AO_TEX 26
#define CAB_PROBE_TEX 27
#define CAB_PROBE_ALBEDO_TEX 28
#define CAB_PROBE_SSBO_BINDING 17
#define CAB_PROBE_SH_SIZE 16
#define CAB_PROBE_SH_GROUP_SIZE 64
#define WEATHER_UBO_BINDING 5
#define WEATHER_SCRATCH_SSBO_BINDING 9
#define MAX_CLOUD_LAYERS 4
@@ -239,6 +245,8 @@ struct model_draw_s
float fog_density;
float alpha_mult;
float shadow_tone;
// 1 for the parts of the occupied cab, see gl::model_ubs
float cab_interior;
};
layout(std430, binding = MODEL_DRAW_SSBO_BINDING) readonly buffer model_draw_ssbo
@@ -265,6 +273,7 @@ layout(location = 11) flat in uint f_model_draw;
#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
#define cab_interior model_draws[MODEL_DRAW].cab_interior
#else
layout(std140, binding = MODEL_UBO_BINDING) uniform model_ubo
{
@@ -278,6 +287,8 @@ layout(std140, binding = MODEL_UBO_BINDING) uniform model_ubo
float fog_density;
float alpha_mult;
float shadow_tone;
// 1 for the parts of the occupied cab, see gl::model_ubs
float cab_interior;
};
#endif
@@ -309,6 +320,15 @@ layout(std140, binding = SCENE_UBO_BINDING) uniform scene_ubo
vec4 env_params;
// the air against the vehicle of the camera, see gl::scene_ubs
vec4 rain_wind;
// the ambient occlusion in the light of the surfaces, see gl::scene_ubs
vec4 ao_params;
// the probe of the occupied cab, and the lamp it's drawn with, see gl::scene_ubs
mat4 cab_probe_view;
vec4 cab_probe_box;
vec4 cab_probe_params;
vec4 cab_probe_lamp;
vec4 cab_probe_lamp_color;
vec4 cab_probe_lamp_shadow;
};
#ifdef STAGE_VERTEX

View File

@@ -8,6 +8,17 @@ layout(binding = LOCAL_SHADOW_LAMPS_TEX) uniform samplerCubeArrayShadow local_sh
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;
// the ambient occlusion of the main view, half its size, for the ambient light (gl::scene_ubs::ao_params)
layout(binding = SCENE_AO_TEX) uniform sampler2D scene_ao;
// the probe of the occupied cab (gl::scene_ubs::cab_probe_box, gfx::cab_probe): the light of its lamps the cab reflects and its
// albedo, each premultiplied by the share of the direction the cab fills, which is in their alpha, and the light the cab lights
// itself with again (gl::cab_probe_data)
layout(binding = CAB_PROBE_TEX) uniform samplerCube cab_probe_light;
layout(binding = CAB_PROBE_ALBEDO_TEX) uniform samplerCube cab_probe_albedo;
layout(std430, binding = CAB_PROBE_SSBO_BINDING) readonly buffer cab_probe_ssbo
{
vec4 cab_probe_sh[9];
};
#include "envmapping.glsl"
#include "ssr.glsl"
@@ -45,6 +56,10 @@ float length2(vec3 v)
return dot(v, v);
}
// the largest difference of the distance of a texel of the ambient occlusion from the one of the fragment (a share of it) the
// texel is taken to be of the surface of the fragment at
const float AO_SURFACE_MATCH = 0.05;
// how many more lamps and headlights the fragment traces contact shadows to, see add_headlights()
int contact_shadow_budget = 0;
@@ -707,6 +722,103 @@ vec4 reflection_scene(vec3 fragnormal, float roughness)
return result;
}
// the ambient occlusion of the view in the ambient light of the fragment, 0..1 (1: none): the occlusion of the main view worked out
// from the depth of the opaque scene drawn before its colour (gfx.postfx.ao.lighting), half its size. the four nearest texels are
// weighed by how close they are and by how close their distance is to the one of the fragment (as in postfx_ssao_apply.frag); where
// none of them is of the surface of the fragment (what the depth before the colour doesn't hold: the blended surfaces, the grass,
// the cab drawn after the precipitation, the edges of objects) there's no occlusion. the distance is the one along the axis of the
// view, near / depth with the far plane at infinity
float ambient_occlusion()
{
if (ao_params.x <= 0.0)
return 1.0;
ivec2 full_size = textureSize(scene_depth, 0);
ivec2 half_size = textureSize(scene_ao, 0);
ivec2 pixel = ivec2(gl_FragCoord.xy);
float distance = max(-f_pos.z, 1e-3);
vec2 position = vec2(pixel) * 0.5;
ivec2 base = ivec2(floor(position));
vec2 fraction = position - vec2(base);
float sum = 0.0;
float weights = 0.0;
float closest = 1e30;
for (int y = 0; y < 2; ++y)
{
for (int x = 0; x < 2; ++x)
{
ivec2 texel = clamp(base + ivec2(x, y), ivec2(0), half_size - 1);
float depth = texelFetch(scene_depth, min(texel * 2, full_size - 1), 0).r;
float difference = (depth > 0.0) ? abs(projection[3][2] / depth - distance) / distance : 1e30;
closest = min(closest, difference);
float bilinear = (x == 0 ? 1.0 - fraction.x : fraction.x) * (y == 0 ? 1.0 - fraction.y : fraction.y);
float weight = (bilinear + 1e-3) / (1e-3 + difference);
sum += texelFetch(scene_ao, texel, 0).r * weight;
weights += weight;
}
}
if (closest > AO_SURFACE_MATCH)
return 1.0;
return pow(clamp(sum / max(weights, 1e-6), 0.0, 1.0), ao_params.y);
}
// the light which comes back after more than one bounce off surfaces of the given albedo, for the occlusion of the ambient light
// (Jimenez et al. 2016), for each channel
vec3 ao_multi_bounce(float visibility, vec3 albedo)
{
vec3 a = 2.0404 * albedo - 0.3324;
vec3 b = -4.7951 * albedo + 0.6417;
vec3 c = 2.7552 * albedo + 0.6903;
return max(vec3(visibility), ((visibility * a + b) * visibility + c) * visibility);
}
// the share of the reflection of the surroundings the occlusion leaves to a surface of the perceptual roughness seen at the angle
// NdotV (Lagarde and de Rousiers 2014)
float ao_specular(float visibility, float NdotV, float roughness)
{
return clamp(pow(NdotV + visibility, exp2(-16.0 * roughness - 1.0)) - 1.0 + visibility, 0.0, 1.0);
}
// whether the fragment is a part of the occupied cab with its probe (gl::scene_ubs::cab_probe_box, gl::model_ubs::cab_interior)
bool cab_probe_active()
{
return (cab_probe_box.w > 0.0) && (cab_interior > 0.5);
}
// the light of the lamps the cab reflects which reaches a surface of the cab facing the view space Fragnormal, in the units of the
// ambient light: the spherical harmonics cabprobe_sh.comp worked out from the probe, in the space of the probe
vec3 cab_probe_bounce(vec3 fragnormal)
{
vec3 n = normalize(mat3(cab_probe_view) * fragnormal);
vec3 light = cab_probe_sh[0].rgb * 0.282095
+ cab_probe_sh[1].rgb * (0.488603 * n.y)
+ cab_probe_sh[2].rgb * (0.488603 * n.z)
+ cab_probe_sh[3].rgb * (0.488603 * n.x)
+ cab_probe_sh[4].rgb * (1.092548 * n.x * n.y)
+ cab_probe_sh[5].rgb * (1.092548 * n.y * n.z)
+ cab_probe_sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0))
+ cab_probe_sh[7].rgb * (1.092548 * n.x * n.z)
+ cab_probe_sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y));
return max(light, vec3(0.0)) * cab_probe_params.x;
}
// the reflection of the cab in a surface of the cab of the perceptual roughness, over the reflection of the surroundings, where the
// probe holds the cab: the reflected ray from the fragment to the box of the cab, looked up from the middle of the box, where the
// probe was drawn from (the box stands for the walls of the cab). the cab shows the light of its lamps it reflects (the probe) and
// its albedo lit by the ambient light of the fragment (Ambientlight), the light the cab gets from the outside
vec3 cab_probe_reflection(vec3 surroundings, vec3 fragnormal, float roughness, vec3 ambientlight)
{
vec3 position = (cab_probe_view * vec4(f_pos.xyz, 1.0)).xyz;
vec3 direction = normalize(mat3(cab_probe_view) * reflect(normalize(f_pos.xyz), fragnormal));
vec3 invdir = (step(0.0, direction) * 2.0 - 1.0) / max(abs(direction), vec3(1e-5));
vec3 far = max((cab_probe_box.xyz - position) * invdir, (-cab_probe_box.xyz - position) * invdir);
float reach = max(min(min(far.x, far.y), far.z), 0.0);
vec3 lookup = position + direction * reach;
float level = roughness * cab_probe_params.z;
vec4 light = textureLod(cab_probe_light, lookup, level);
vec4 albedo = textureLod(cab_probe_albedo, lookup, level);
return surroundings * (1.0 - albedo.a) + light.rgb + albedo.rgb * ambientlight;
}
// 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
@@ -761,6 +873,23 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
spec_f0 = f0;
spec_roughness = roughness;
// the light of the lamps the cab reflects, which lights the cab again (the probe of the cab), joins the ambient light
bool cabprobe = cab_probe_active();
vec3 cabambient = fragcolor;
if (cabprobe)
fragcolor += cab_probe_bounce(fragnormal);
// the ambient occlusion of the view holds back the ambient light of the diffuse layer (with the light coming back from more
// bounces off surfaces of the albedo) and the reflection of the surroundings; not the light of the sun and the lamps, nor the
// reflection of the scene, which the rays find as it is
float occlusion = ambient_occlusion();
vec3 diffuse_occlusion = vec3(1.0);
float specular_occlusion = 1.0;
if (occlusion < 1.0)
{
diffuse_occlusion = ao_multi_bounce(occlusion, texturecolor);
specular_occlusion = ao_specular(occlusion, NdotV, 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
@@ -791,15 +920,18 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
if (!matte)
{
envcolor = reflection_surroundings(fragnormal, env_roughness);
// inside the cab the cab is what the surroundings are, where the probe holds it
if (cabprobe && (cab_probe_params.y > 0.0))
envcolor = cab_probe_reflection(envcolor, fragnormal, env_roughness, cabambient);
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
return fragcolor * diffuse_occlusion * diffuse_albedo
+ emissioncolor * texturecolor
+ fragcolor * env_spec * ambient_share
+ (scenecolor.rgb + envcolor * (1.0 - scenecolor.a)) * cubemap_spec * cubemap_share;
+ fragcolor * env_spec * ambient_share * specular_occlusion
+ (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * specular_occlusion)) * cubemap_spec * cubemap_share;
vec3 light_dir = normalize(-lights[0].dir);
float NdotL = max(dot(fragnormal, light_dir), 0.0);
@@ -830,8 +962,10 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
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
+ (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * cubemap_shadow)) * cubemap_spec * cubemap_share;
vec3 env_reflection = fragcolor * env_spec * ambient_share * specular_occlusion
+ (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * cubemap_shadow * specular_occlusion)) * cubemap_spec * cubemap_share;
// the occlusion of the ambient light, before the light of the sun and the lamps joins it
fragcolor *= diffuse_occlusion;
// 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

View File

@@ -0,0 +1,19 @@
#version 460 core
#define STAGE_FRAGMENT 1
#include "common.glsl"
// the probe of the occupied cab (gfx::cab_probe), the materials without the alpha test (cabprobe.glsl)
layout(location = 0) in vec3 f_normal;
layout(location = 2) in vec2 f_coord;
layout(location = 3) in vec4 f_pos;
#pragma texture(diffuse, 0, sRGB_A)
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
// the cube shadow map of the cab lamp
layout(set = PASS_SET, binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow probe_shadowmap;
#include "cabprobe.glsl"
void main()
{
probe_shade(texture(diffuse, f_coord));
}

View File

@@ -0,0 +1,61 @@
// the shading of the probe of the occupied cab (gfx::cab_probe, as shaders/ogl46/cabprobe.glsl), for cabprobe.frag and cabprobe_alpha.frag: what the cab shows
// towards the middle of its box lit by the cab lamp alone, with the cube shadow map of the lamp (and what glows in it, the lights
// of the desk), and its albedo, each with 1 in the alpha: the probe is cleared to nothing, so the alpha of a direction tells how
// much of it the cab fills, and both are premultiplied by it. the light of the cab lamp is the one the shading gives it
// (calc_local_light() in light_common.glsl); the ambient light and the sun are left out, the cab gets those as before. the
// parameters of the lamp are in the block of the scene (gl::scene_ubs::cab_probe_lamp), in the space of the probe
layout(location = 0) out vec4 out_light;
layout(location = 1) out vec4 out_albedo;
// closest distance (squared) the light of the lamp is computed for, as LOCAL_LIGHT_MIN_DISTANCE2 of light_common.glsl
const float PROBE_LIGHT_MIN_DISTANCE2 = 0.01;
// the diffuse colour of the submodel or shape, as material_diffuse() of light_common.glsl
vec3 probe_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));
}
// share of the light of the cab lamp held back before the point of the space of its cube shadow map (origin at the lamp), 0..1, as
// local_light_shadow() of light_common.glsl takes it with a single sample
float probe_lamp_shadow(vec3 position, vec3 normal)
{
vec4 weights = cab_probe_lamp_shadow;
float distance = length(position);
float texel = 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)));
vec3 axes = abs(position);
float major = max(axes.x, max(axes.y, axes.z));
float reference = weights.y / max(major - texel * 1.5, weights.y);
return texture(probe_shadowmap, vec4(position, weights.x), reference);
}
void probe_shade(vec4 tex_color)
{
vec3 albedo = tex_color.rgb * probe_material_diffuse();
vec3 position = f_pos.xyz;
vec3 normal = normalize(f_normal);
// the walls are drawn from both sides: the side the probe sees
if (dot(normal, position) > 0.0)
normal = -normal;
vec3 light = vec3(emission);
if (cab_probe_lamp.w > 0.0)
{
vec3 tolight = cab_probe_lamp.xyz - position;
float distance2 = dot(tolight, tolight);
vec3 light_dir = tolight * inversesqrt(max(distance2, 1e-8));
float edge = clamp(1.0 - distance2 * distance2 * (cab_probe_lamp.w * cab_probe_lamp.w), 0.0, 1.0);
float atten = edge * edge / max(distance2, PROBE_LIGHT_MIN_DISTANCE2);
float NdotL = max(dot(normal, light_dir), 0.0);
if ((atten > 0.0) && (NdotL > 0.0) && (cab_probe_lamp_color.a > 0.5))
atten *= 1.0 - probe_lamp_shadow(-tolight, normal);
light += cab_probe_lamp_color.rgb * (NdotL * atten);
}
out_light = vec4(albedo * light, 1.0);
out_albedo = vec4(albedo, 1.0);
}

View File

@@ -0,0 +1,38 @@
#version 460 core
#extension GL_EXT_multiview : require
#define STAGE_VERTEX 1
// the occupied cab drawn into the six faces of its probe at once (gfx::cab_probe), a view of the multiview rendering per face (the
// geometry shader cabprobe.geom of the opengl renderer). the modelview takes the cab model to the space of the probe: the axes of
// the model, the origin in the middle of the box of the cab. the faces go in the order of the faces of a cube map (+x, -x, +y, -y,
// +z, -z), each looks the way glm::lookAt() with the usual up vectors of cube maps sets it, with a 90 degree projection of infinite
// reach and reversed depth like the one of the scene: the depth is near / d, d the distance along the axis of the face
#include "common.glsl"
layout(location = 0) in vec3 v_vert;
layout(location = 1) in vec3 v_normal;
layout(location = 2) in vec2 v_coord;
layout(location = 0) out vec3 f_normal;
layout(location = 2) out vec2 f_coord;
layout(location = 3) out vec4 f_pos;
// near plane of the probe, metres
const float PROBE_NEAR = 0.05;
// right, up and forward axes of the faces
const vec3 face_right[6] = vec3[](vec3(0.0, 0.0, -1.0), vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0));
const vec3 face_up[6] = vec3[](vec3(0.0, -1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0), vec3(0.0, -1.0, 0.0), vec3(0.0, -1.0, 0.0));
const vec3 face_forward[6] = vec3[](vec3(1.0, 0.0, 0.0), vec3(-1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, -1.0, 0.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, -1.0));
void main()
{
vec3 position = (effective_modelview() * vec4(v_vert, 1.0)).xyz;
int face = int(gl_ViewIndex);
gl_Position = vec4(dot(position, face_right[face]), dot(position, face_up[face]), PROBE_NEAR, dot(position, face_forward[face]));
f_normal = effective_modelviewnormal() * v_normal;
f_coord = v_coord;
f_pos = vec4(position, 1.0);
#ifdef MODEL_DRAWS
// which block of the multi draw the fragments read (common.glsl)
f_model_draw = MODEL_DRAW;
#endif
}

View File

@@ -0,0 +1,23 @@
#version 460 core
#define STAGE_FRAGMENT 1
#include "common.glsl"
// the probe of the occupied cab (gfx::cab_probe), the materials whose colour shader tests the alpha of the diffuse texture against
// the opacity of the material, with the test of depthprepass_alpha.frag (cabprobe.glsl)
layout(location = 0) in vec3 f_normal;
layout(location = 2) in vec2 f_coord;
layout(location = 3) in vec4 f_pos;
#pragma texture(diffuse, 0, sRGB_A)
layout(set = MATERIAL_SET, binding = MATERIAL_TEXTURE_FIRST + 0) uniform sampler2D diffuse;
// the cube shadow map of the cab lamp
layout(set = PASS_SET, binding = LOCAL_SHADOW_TEX) uniform samplerCubeArrayShadow probe_shadowmap;
#include "cabprobe.glsl"
void main()
{
vec4 tex_color = texture(diffuse, f_coord);
if (tex_color.a < opacity + 1.0 / 255.0)
discard;
probe_shade(tex_color);
}

View File

@@ -0,0 +1,96 @@
#version 460 core
#define STAGE_COMPUTE 1
#define LAYOUT_COMPUTE 1
#include "common.glsl"
// the light the occupied cab lights itself with again, from its probe (gfx::cab_probe), in one work group: the light of its lamps the
// cab reflects towards the middle of its box, as spherical harmonics of bands 0..2, convolved with the cosine lobe and divided by
// pi, so their value for a direction is the light a white lambertian surface facing it gets, in the units of the ambient light
// (light_common.glsl, cab_probe_bounce()). the light is taken from a level of the probe of at most CAB_PROBE_SH_SIZE pixels a
// face, each pixel weighed by its solid angle. the basis is the one of the sky (gl::sky_data)
layout(local_size_x = CAB_PROBE_SH_GROUP_SIZE) in;
layout(set = COMPUTE_SET, binding = 0) uniform samplerCube probe_light;
layout(std430, set = COMPUTE_SET, binding = 1) writeonly buffer cab_probe_result_ssbo
{
vec4 result_sh[9];
};
// the parameters of the kernel (push constants): x the level of the probe read, y its size, pixels
layout(push_constant) uniform kernel_constants
{
ivec2 level;
};
shared vec3 partial[CAB_PROBE_SH_GROUP_SIZE][9];
shared float partialweight[CAB_PROBE_SH_GROUP_SIZE];
// the direction through the middle of the pixel (s, t) of the face, -1..1 across it, in the order of the faces of a cube map
vec3 face_direction(int face, float s, float t)
{
switch (face)
{
case 0: return vec3(1.0, -t, -s);
case 1: return vec3(-1.0, -t, s);
case 2: return vec3(s, 1.0, t);
case 3: return vec3(s, -1.0, -t);
case 4: return vec3(s, -t, 1.0);
default: return vec3(-s, -t, -1.0);
}
}
void main()
{
uint invocation = gl_LocalInvocationID.x;
int size = max(level.y, 1);
int texels = 6 * size * size;
vec3 sum[9];
for (int i = 0; i < 9; ++i)
sum[i] = vec3(0.0);
float weights = 0.0;
for (int index = int(invocation); index < texels; index += CAB_PROBE_SH_GROUP_SIZE)
{
int face = index / (size * size);
int texel = index - face * size * size;
float s = (float(texel % size) + 0.5) / float(size) * 2.0 - 1.0;
float t = (float(texel / size) + 0.5) / float(size) * 2.0 - 1.0;
vec3 direction = face_direction(face, s, t);
// the solid angle of the pixel
float lengthsquared = dot(direction, direction);
float weight = 1.0 / (lengthsquared * sqrt(lengthsquared));
vec3 n = direction * inversesqrt(lengthsquared);
vec3 light = textureLod(probe_light, n, float(level.x)).rgb * weight;
sum[0] += light * 0.282095;
sum[1] += light * (0.488603 * n.y);
sum[2] += light * (0.488603 * n.z);
sum[3] += light * (0.488603 * n.x);
sum[4] += light * (1.092548 * n.x * n.y);
sum[5] += light * (1.092548 * n.y * n.z);
sum[6] += light * (0.315392 * (3.0 * n.z * n.z - 1.0));
sum[7] += light * (1.092548 * n.x * n.z);
sum[8] += light * (0.546274 * (n.x * n.x - n.y * n.y));
weights += weight;
}
for (int i = 0; i < 9; ++i)
partial[invocation][i] = sum[i];
partialweight[invocation] = weights;
barrier();
for (uint stride = CAB_PROBE_SH_GROUP_SIZE / 2; stride > 0U; stride /= 2U)
{
if (invocation < stride)
{
for (int i = 0; i < 9; ++i)
partial[invocation][i] += partial[invocation + stride][i];
partialweight[invocation] += partialweight[invocation + stride];
}
barrier();
}
if (invocation < 9U)
{
// the weights add up to the whole sphere, 4 pi; the cosine lobe over pi: 1 for band 0, 2/3 for band 1, 1/4 for band 2
float sphere = 12.566371 / max(partialweight[0], 1e-6);
float band = (invocation == 0U) ? 1.0 : (invocation < 4U) ? (2.0 / 3.0) : 0.25;
result_sh[invocation] = vec4(partial[0][invocation] * (sphere * band), 0.0);
}
}

View File

@@ -61,7 +61,11 @@
#define SCENE_DEPTH_TEX 24
#define SSR_HISTORY_TEX 25
#define WATER_SSBO 26
#define PASS_BINDING_COUNT 27
#define SCENE_AO_TEX 27
#define CAB_PROBE_TEX 28
#define CAB_PROBE_ALBEDO_TEX 29
#define CAB_PROBE_SSBO 30
#define PASS_BINDING_COUNT 31
#define MATERIAL_TEXTURE_FIRST 0
#define CL_CLUSTER_UBO 0
#define CL_AABB_SSBO 1
@@ -129,6 +133,8 @@
#define WATER_TRAIL 1024
#define WATER_MAP 256
#define WATER_GROUP_SIZE 64
#define CAB_PROBE_SH_SIZE 16
#define CAB_PROBE_SH_GROUP_SIZE 64
#endif
#include "settings.glsl"
@@ -281,6 +287,15 @@ layout(std140, set = PASS_SET, binding = SCENE_UBO) uniform scene_ubo
vec4 env_params;
// the air against the vehicle of the camera, see gl::scene_ubs
vec4 rain_wind;
// the ambient occlusion in the light of the surfaces, see gl::scene_ubs
vec4 ao_params;
// the probe of the occupied cab, and the lamp it's drawn with, see gl::scene_ubs
mat4 cab_probe_view;
vec4 cab_probe_box;
vec4 cab_probe_params;
vec4 cab_probe_lamp;
vec4 cab_probe_lamp_color;
vec4 cab_probe_lamp_shadow;
};
#ifndef LAYOUT_COMPUTE
@@ -298,6 +313,8 @@ struct model_draw_s
float fog_density;
float alpha_mult;
float shadow_tone;
// 1 for the parts of the occupied cab, see gl::model_ubs
float cab_interior;
};
layout(std430, set = PASS_SET, binding = DRAW_SSBO) readonly buffer model_draw_ssbo
@@ -331,6 +348,7 @@ layout(location = 11) flat in uint f_model_draw;
#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
#define cab_interior model_draws[MODEL_DRAW].cab_interior
// per-instance modelview matrices of the instanced draws, element 0 the identity. a draw which isn't instanced is drawn with
// firstInstance 0, an instanced one from its base instance on, with modelview set to the submodel-local chain (computed from

View File

@@ -12,6 +12,17 @@ layout(set = PASS_SET, binding = LOCAL_SHADOW_LAMPS_TEX) uniform samplerCubeArra
layout(set = PASS_SET, 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(set = PASS_SET, binding = SCENE_DEPTH_TEX) uniform sampler2D scene_depth;
// the ambient occlusion of the main view, half its size, for the ambient light (gl::scene_ubs::ao_params)
layout(set = PASS_SET, binding = SCENE_AO_TEX) uniform sampler2D scene_ao;
// the probe of the occupied cab (gl::scene_ubs::cab_probe_box, gfx::cab_probe): the light of its lamps the cab reflects and its
// albedo, each premultiplied by the share of the direction the cab fills, which is in their alpha, and the light the cab lights
// itself with again (gl::cab_probe_data)
layout(set = PASS_SET, binding = CAB_PROBE_TEX) uniform samplerCube cab_probe_light;
layout(set = PASS_SET, binding = CAB_PROBE_ALBEDO_TEX) uniform samplerCube cab_probe_albedo;
layout(std430, set = PASS_SET, binding = CAB_PROBE_SSBO) readonly buffer cab_probe_ssbo
{
vec4 cab_probe_sh[9];
};
#include "envmapping.glsl"
#include "ssr.glsl"
@@ -49,6 +60,10 @@ float length2(vec3 v)
return dot(v, v);
}
// the largest difference of the distance of a texel of the ambient occlusion from the one of the fragment (a share of it) the
// texel is taken to be of the surface of the fragment at
const float AO_SURFACE_MATCH = 0.05;
// how many more lamps and headlights the fragment traces contact shadows to, see add_headlights()
int contact_shadow_budget = 0;
@@ -720,6 +735,103 @@ vec4 reflection_scene(vec3 fragnormal, float roughness, float weight)
return result;
}
// the ambient occlusion of the view in the ambient light of the fragment, 0..1 (1: none): the occlusion of the main view worked out
// from the depth of the opaque scene drawn before its colour (gfx.postfx.ao.lighting), half its size. the four nearest texels are
// weighed by how close they are and by how close their distance is to the one of the fragment (as in postfx_ssao_apply.frag); where
// none of them is of the surface of the fragment (what the depth before the colour doesn't hold: the blended surfaces, the grass,
// the cab drawn after the precipitation, the edges of objects) there's no occlusion. the distance is the one along the axis of the
// view, near / depth with the far plane at infinity
float ambient_occlusion()
{
if (ao_params.x <= 0.0)
return 1.0;
ivec2 full_size = textureSize(scene_depth, 0);
ivec2 half_size = textureSize(scene_ao, 0);
ivec2 pixel = ivec2(gl_FragCoord.xy);
float distance = max(-f_pos.z, 1e-3);
vec2 position = vec2(pixel) * 0.5;
ivec2 base = ivec2(floor(position));
vec2 fraction = position - vec2(base);
float sum = 0.0;
float weights = 0.0;
float closest = 1e30;
for (int y = 0; y < 2; ++y)
{
for (int x = 0; x < 2; ++x)
{
ivec2 texel = clamp(base + ivec2(x, y), ivec2(0), half_size - 1);
float depth = texelFetch(scene_depth, min(texel * 2, full_size - 1), 0).r;
float difference = (depth > 0.0) ? abs(projection[3][2] / depth - distance) / distance : 1e30;
closest = min(closest, difference);
float bilinear = (x == 0 ? 1.0 - fraction.x : fraction.x) * (y == 0 ? 1.0 - fraction.y : fraction.y);
float weight = (bilinear + 1e-3) / (1e-3 + difference);
sum += texelFetch(scene_ao, texel, 0).r * weight;
weights += weight;
}
}
if (closest > AO_SURFACE_MATCH)
return 1.0;
return pow(clamp(sum / max(weights, 1e-6), 0.0, 1.0), ao_params.y);
}
// the light which comes back after more than one bounce off surfaces of the given albedo, for the occlusion of the ambient light
// (Jimenez et al. 2016), for each channel
vec3 ao_multi_bounce(float visibility, vec3 albedo)
{
vec3 a = 2.0404 * albedo - 0.3324;
vec3 b = -4.7951 * albedo + 0.6417;
vec3 c = 2.7552 * albedo + 0.6903;
return max(vec3(visibility), ((visibility * a + b) * visibility + c) * visibility);
}
// the share of the reflection of the surroundings the occlusion leaves to a surface of the perceptual roughness seen at the angle
// NdotV (Lagarde and de Rousiers 2014)
float ao_specular(float visibility, float NdotV, float roughness)
{
return clamp(pow(NdotV + visibility, exp2(-16.0 * roughness - 1.0)) - 1.0 + visibility, 0.0, 1.0);
}
// whether the fragment is a part of the occupied cab with its probe (gl::scene_ubs::cab_probe_box, gl::model_ubs::cab_interior)
bool cab_probe_active()
{
return (cab_probe_box.w > 0.0) && (cab_interior > 0.5);
}
// the light of the lamps the cab reflects which reaches a surface of the cab facing the view space Fragnormal, in the units of the
// ambient light: the spherical harmonics cabprobe_sh.comp worked out from the probe, in the space of the probe
vec3 cab_probe_bounce(vec3 fragnormal)
{
vec3 n = normalize(mat3(cab_probe_view) * fragnormal);
vec3 light = cab_probe_sh[0].rgb * 0.282095
+ cab_probe_sh[1].rgb * (0.488603 * n.y)
+ cab_probe_sh[2].rgb * (0.488603 * n.z)
+ cab_probe_sh[3].rgb * (0.488603 * n.x)
+ cab_probe_sh[4].rgb * (1.092548 * n.x * n.y)
+ cab_probe_sh[5].rgb * (1.092548 * n.y * n.z)
+ cab_probe_sh[6].rgb * (0.315392 * (3.0 * n.z * n.z - 1.0))
+ cab_probe_sh[7].rgb * (1.092548 * n.x * n.z)
+ cab_probe_sh[8].rgb * (0.546274 * (n.x * n.x - n.y * n.y));
return max(light, vec3(0.0)) * cab_probe_params.x;
}
// the reflection of the cab in a surface of the cab of the perceptual roughness, over the reflection of the surroundings, where the
// probe holds the cab: the reflected ray from the fragment to the box of the cab, looked up from the middle of the box, where the
// probe was drawn from (the box stands for the walls of the cab). the cab shows the light of its lamps it reflects (the probe) and
// its albedo lit by the ambient light of the fragment (Ambientlight), the light the cab gets from the outside
vec3 cab_probe_reflection(vec3 surroundings, vec3 fragnormal, float roughness, vec3 ambientlight)
{
vec3 position = (cab_probe_view * vec4(f_pos.xyz, 1.0)).xyz;
vec3 direction = normalize(mat3(cab_probe_view) * reflect(normalize(f_pos.xyz), fragnormal));
vec3 invdir = (step(0.0, direction) * 2.0 - 1.0) / max(abs(direction), vec3(1e-5));
vec3 far = max((cab_probe_box.xyz - position) * invdir, (-cab_probe_box.xyz - position) * invdir);
float reach = max(min(min(far.x, far.y), far.z), 0.0);
vec3 lookup = position + direction * reach;
float level = roughness * cab_probe_params.z;
vec4 light = textureLod(cab_probe_light, lookup, level);
vec4 albedo = textureLod(cab_probe_albedo, lookup, level);
return surroundings * (1.0 - albedo.a) + light.rgb + albedo.rgb * ambientlight;
}
// 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
@@ -774,6 +886,23 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
spec_f0 = f0;
spec_roughness = roughness;
// the light of the lamps the cab reflects, which lights the cab again (the probe of the cab), joins the ambient light
bool cabprobe = cab_probe_active();
vec3 cabambient = fragcolor;
if (cabprobe)
fragcolor += cab_probe_bounce(fragnormal);
// the ambient occlusion of the view holds back the ambient light of the diffuse layer (with the light coming back from more
// bounces off surfaces of the albedo) and the reflection of the surroundings; not the light of the sun and the lamps, nor the
// reflection of the scene, which the rays find as it is
float occlusion = ambient_occlusion();
vec3 diffuse_occlusion = vec3(1.0);
float specular_occlusion = 1.0;
if (occlusion < 1.0)
{
diffuse_occlusion = ao_multi_bounce(occlusion, texturecolor);
specular_occlusion = ao_specular(occlusion, NdotV, 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
@@ -804,16 +933,19 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
if (!matte)
{
envcolor = reflection_surroundings(fragnormal, env_roughness);
// inside the cab the cab is what the surroundings are, where the probe holds it
if (cabprobe && (cab_probe_params.y > 0.0))
envcolor = cab_probe_reflection(envcolor, fragnormal, env_roughness, cabambient);
float sceneweight = cubemap_share * dot(cubemap_spec, vec3(1.0 / 3.0));
if (((ssr_params.x > 0.0) || (ssr_params2.z > 0.0)) && (sceneweight > REFLECTION_SCENE_WEIGHT))
scenecolor = reflection_scene(fragnormal, env_roughness, sceneweight);
}
if (lights_count == 0U)
return fragcolor * diffuse_albedo
return fragcolor * diffuse_occlusion * diffuse_albedo
+ emissioncolor * texturecolor
+ fragcolor * env_spec * ambient_share
+ (scenecolor.rgb + envcolor * (1.0 - scenecolor.a)) * cubemap_spec * cubemap_share;
+ fragcolor * env_spec * ambient_share * specular_occlusion
+ (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * specular_occlusion)) * cubemap_spec * cubemap_share;
vec3 light_dir = normalize(-lights[0].dir);
float NdotL = max(dot(fragnormal, light_dir), 0.0);
@@ -844,8 +976,10 @@ vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float ref
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
+ (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * cubemap_shadow)) * cubemap_spec * cubemap_share;
vec3 env_reflection = fragcolor * env_spec * ambient_share * specular_occlusion
+ (scenecolor.rgb + envcolor * ((1.0 - scenecolor.a) * cubemap_shadow * specular_occlusion)) * cubemap_spec * cubemap_share;
// the occlusion of the ambient light, before the light of the sun and the lamps joins it
fragcolor *= diffuse_occlusion;
// 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