From 7d0f76aaa9d885c50e036f680e4b753943e697ad Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Tue, 20 Oct 2026 18:39:31 +0200 Subject: [PATCH] Make the material shaders physically based --- shaders/grass.frag | 2 +- shaders/light_common.glsl | 306 ++++++++++++++------ shaders/mat_colored.frag | 2 +- shaders/mat_default.frag | 2 +- shaders/mat_default_detail.frag | 2 +- shaders/mat_detail_normalmap.frag | 1 + shaders/mat_detail_parallax.frag | 20 +- shaders/mat_detail_parallax_specgloss.frag | 17 +- shaders/mat_normalmap.frag | 1 + shaders/mat_parallax.frag | 18 +- shaders/mat_parallax_specgloss.frag | 15 +- shaders/mat_rain_windscreen.frag | 2 +- shaders/mat_reflmap.frag | 2 +- shaders/mat_shadowlessnormalmap.frag | 2 +- shaders/mat_sunlessnormalmap.frag | 53 +--- shaders/mat_sunlessnormalmap_specgloss.frag | 71 ----- shaders/mat_terrain.frag | 6 +- shaders/mat_water.frag | 7 +- shaders/mat_water_specgloss.frag | 11 +- shaders/vertex.vert | 6 +- 20 files changed, 288 insertions(+), 258 deletions(-) diff --git a/shaders/grass.frag b/shaders/grass.frag index 49128796..cb883ab8 100644 --- a/shaders/grass.frag +++ b/shaders/grass.frag @@ -20,7 +20,7 @@ layout(location = 1) out vec4 out_motion; void main() { vec3 fragnormal = normalize(f_normal); - glossiness = 6.0; + glossiness = phong_glossiness(6.0); metalic = 0.0; vec3 fragcolor = apply_lights(ambient, fragnormal, f_albedo, 0.0, 0.2, shadow_tone); diff --git a/shaders/light_common.glsl b/shaders/light_common.glsl index eb06c1bf..d9c3edc8 100644 --- a/shaders/light_common.glsl +++ b/shaders/light_common.glsl @@ -216,13 +216,81 @@ float G_SchlickGGX(float NdotX, float roughness) return NdotX / (NdotX * (1.0 - k) + k); } -// Height-correlated Smith geometry term +// Separable Smith geometry term (not height-correlated) // G(N,V,L) = G_SchlickGGX(NdotV) · G_SchlickGGX(NdotL) float G_Smith(float NdotV, float NdotL, float roughness) { return G_SchlickGGX(NdotV, roughness) * G_SchlickGGX(NdotL, roughness); } +const float BRDF_PI = 3.14159265359; +// the smoothest surface the lights are reflected by: a mirror would make a highlight smaller than a pixel +const float MIN_ROUGHNESS = 0.045; +// cap of the brdf for the lamps and headlights (see specular_light()): a tiny light next to a polished surface +const float LOCAL_LIGHT_BRDF_LIMIT = 16.0; +// no cap for the sun +const float SUN_BRDF_LIMIT = 1.0e6; + +// the specular reflectance at normal incidence and the perceptual roughness the lights are reflected with, worked out by +// apply_lights() for the fragment before it adds the lamps (add_local_lights()) +vec3 spec_f0 = vec3(0.04); +float spec_roughness = 0.5; + +// light of a light from Light_dir reflected by the surface, in the units of the diffuse shading (a white lambertian surface +// facing the light reflects 1 of it): pi times the cook-torrance brdf with schlick's fresnel of F0, times n.l. Limit caps the +// brdf without the n.l +vec3 specular_light(vec3 light_dir, vec3 fragnormal, vec3 f0, float roughness, float limit) +{ + float NdotL = dot(fragnormal, light_dir); + if (NdotL <= 0.0) + return vec3(0.0); + vec3 V = normalize(-f_pos.xyz); + vec3 H = normalize(light_dir + V); + float NdotV = max(dot(fragnormal, V), 1e-4); + float NdotH = max(dot(fragnormal, H), 0.0); + float VdotH = max(dot(V, H), 0.0); + float brdf = D_GGX(NdotH, roughness) * G_Smith(NdotV, NdotL, roughness) / (4.0 * NdotL * NdotV); + vec3 fresnel = f0 + (1.0 - f0) * pow(1.0 - VdotH, 5.0); + return fresnel * (min(brdf, limit) * NdotL * BRDF_PI); +} + +// perceptual roughness raised where the normal changes much from a pixel to the next (normal maps, tight curves), so the +// highlight of a detail smaller than a pixel spreads over the pixel instead of flickering. Kaplanyan, Tokuyoshi, "Improved +// geometric specular antialiasing", 2019 +float specular_antialiasing(vec3 fragnormal, float roughness) +{ + vec3 dndx = dFdx(fragnormal); + vec3 dndy = dFdy(fragnormal); + float variance = 0.25 * (dot(dndx, dndx) + dot(dndy, dndy)); + float kernel = min(2.0 * variance, 0.18); + float a2 = roughness * roughness * roughness * roughness; + return sqrt(sqrt(clamp(a2 + kernel, 0.0, 1.0))); +} + +// the materials without a specgloss map give glossiness: (param[1].w, 10 by default) as the exponent of the blinn-phong +// highlight of the old renderer. a lobe of exponent n is about a ggx one of alpha sqrt(2 / (n + 2)) +float phong_roughness(float exponent) +{ + return sqrt(sqrt(2.0 / (max(exponent, 0.0) + 2.0))); +} + +// glossiness which apply_lights() turns into the roughness of a blinn-phong exponent (it takes 1 - glossiness / param[1].w) +float phong_glossiness(float exponent) +{ + return (1.0 - phong_roughness(exponent)) * max(abs(param[1].w), 1.0); +} + +// the reflection of the surroundings of a material without a specgloss map is a smooth varnish over the rough surface: the +// old renderer showed it as a sharp reflection, as much of it as reflection: (and the masks of the material) asked for +bool legacy_coat = false; + +// set up the gloss of a material without a specgloss map, before apply_lights() +void legacy_gloss() +{ + glossiness = phong_glossiness(abs(param[1].w)); + legacy_coat = true; +} + // Returns vec2(diffuse, specular) for a single punctual light. // // diffuse – Lambert N·L (Fresnel-weighted diffuse is handled per-material @@ -380,7 +448,8 @@ vec2 calc_headlights(light_s light, vec3 fragnormal, vec3 dpdx, vec3 dpdy) if ((contact_shadow_budget > 0) && (lightintensity > 0.0) && (part.x > 0.0)) lightintensity *= 1.0 - contact_shadow_lamp(light_dir, distance, part.x * atten * lightintensity * light.intensity * max(light.color.r, max(light.color.g, light.color.b))); - return part * atten * lightintensity; + // x the diffuse light, y the light the specular reflection takes (specular_light() brings its own n.l) + return vec2(part.x, 1.0) * (atten * lightintensity); } // closest distance (squared) the light of a lamp is computed for: nearer the inverse square would grow without a limit @@ -436,7 +505,7 @@ float local_light_shadow(light_s light, vec3 fragnormal) } // light of a lamp (point or spot light: the lamps of models and vehicles, the cab lamp) reaching the fragment, x the diffuse -// and y the specular part. it falls off with the square of the distance, and smoothly to nothing at the range of the light, +// part and y the light the specular reflection takes (specular_light() brings its own n.l). it falls off with the square of the distance, and smoothly to nothing at the range of the light, // 1 / sqrt(quadratic), past which the culling of the clusters does not take it. a spot light fades from its inner cone to the // outer one. a lamp with a shadow map is held back by what is between it and the fragment vec2 calc_local_light(light_s light, vec3 fragnormal) @@ -455,9 +524,7 @@ vec2 calc_local_light(light_s light, vec3 fragnormal) atten * max(dot(fragnormal, light_dir), 0.0) * light.intensity * max(light.color.r, max(light.color.g, light.color.b))); if (atten <= 0.0) return vec2(0.0); - vec2 part = calc_light(light_dir, fragnormal); - // the highlight of a tiny light on a polished surface is kept within what the image can take - return vec2(part.x, min(part.y, 16.0) * part.x) * atten; + return vec2(max(dot(fragnormal, light_dir), 0.0), 1.0) * atten; } // index of the light cluster the fragment is in, see gl/cluster_grid.h: its screen tile and slice of depth. the depth is the clip @@ -471,10 +538,21 @@ uint fragment_cluster() return uint(tile.x + CLUSTER_GRID_X * (tile.y + CLUSTER_GRID_Y * slice)); } -// adds the light of the headlights and lamps which reach the fragment to its colour. these are the ones in the list of its cluster -// when the lights are clustered (the sun is not on the list, the callers take care of it), and the first lights_count - 1 of -// lights[] otherwise, which are headlights only -vec3 add_headlights(vec3 fragcolor, vec3 fragnormal) +// adds the light of a headlight or lamp to the diffuse and the specular light of the fragment +void add_local_light(light_s light, vec2 part, vec3 fragnormal, inout vec3 diffuse, inout vec3 specular) +{ + if (part.y <= 0.0) + return; + vec3 color = light.color * light.intensity; + diffuse += color * part.x; + specular += color * (part.y * param[1].y) * specular_light(normalize(light.pos - f_pos.xyz), fragnormal, spec_f0, spec_roughness, LOCAL_LIGHT_BRDF_LIMIT); +} + +// adds the light of the headlights and lamps which reach the fragment to its diffuse and specular light. these are the ones in +// the list of its cluster when the lights are clustered (the sun is not on the list, the callers take care of it), and the +// first lights_count - 1 of lights[] otherwise, which are headlights only. the specular light is reflected with spec_f0 and +// spec_roughness, and is already coloured by the fresnel term +void add_local_lights(vec3 fragnormal, inout vec3 diffuse, inout vec3 specular) { // worked out before the loops, which the fragments of a quad do not leave together vec3 dpdx = dFdx(f_pos.xyz); @@ -488,7 +566,7 @@ vec3 add_headlights(vec3 fragcolor, vec3 fragnormal) { light_s light = lights[cluster_light_indices[range.offset + i]]; vec2 part = (light.type == LIGHT_HEADLIGHTS) ? calc_headlights(light, fragnormal, dpdx, dpdy) : calc_local_light(light, fragnormal); - fragcolor += light.color * (part.x + part.y * param[1].y) * light.intensity; + add_local_light(light, part, fragnormal, diffuse, specular); } } else @@ -496,11 +574,19 @@ vec3 add_headlights(vec3 fragcolor, vec3 fragnormal) for (uint i = 1U; i < lights_count; i++) { light_s light = lights[i]; - vec2 part = calc_headlights(light, fragnormal, dpdx, dpdy); - fragcolor += light.color * (part.x + part.y * param[1].y) * light.intensity; + add_local_light(light, calc_headlights(light, fragnormal, dpdx, dpdy), fragnormal, diffuse, specular); } } - return fragcolor; +} + +// the light of the headlights and lamps added to Fragcolor, diffuse and specular together (for shaders which don't tell +// the albedo from the reflection) +vec3 add_headlights(vec3 fragcolor, vec3 fragnormal) +{ + vec3 diffuse = vec3(0.0); + vec3 specular = vec3(0.0); + add_local_lights(fragnormal, diffuse, specular); + return fragcolor + diffuse + specular; } // ----------------------------------------------------------------------- @@ -565,110 +651,140 @@ vec3 add_sky_ambient(vec3 basecolor, vec3 fragnormal) return max(basecolor + sky_ambient_directional(worldnormal) * (sky_ambient_factor * share), vec3(0.0)); } -// [0] - diffuse, [1] - specular -// do magic here -vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone) +// lights the surface: the ambient light, the sun (the moon), the headlights and lamps and the reflection of the surroundings. +// the units are the ones of the ambient light, the irradiance over pi: a white lambertian surface lit by 1 of it shows 1. +// Texturecolor is the albedo (the base colour of a metal), Specularity the specular level of a dielectric (0.5 is the +// reflectance of 4% at normal incidence), Reflectivity how much of the reflected surroundings is the cubemap rather than the +// ambient light, Shadowtone the share of the sun left in its shadow (1 for the materials which take no shadows). +// a Sunless surface (the interior of a cab) gets no light of the sun, its shadow only darkens the ambient light +vec3 shade_surface(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone, bool sunless) { // Diffuse colour is applied once, to the albedo; the light terms below are not scaled by it again texturecolor *= material_diffuse(); - // the sky lights the faces turned up and towards the bright part of the sky more than the ones turned to the ground - fragcolor = add_sky_ambient(fragcolor, fragnormal); - float weathered = (param[2].w > 0.0 ? clamp(param[2].w * UserData.x, 0.0, 1.0) : 0.0); - float tinted = (param[2].w < 0.0 ? clamp(-param[2].w * UserData.x, 0.0, 1.0) : 0.0); - // a tint only scales the colour of the texture, the steel keeps its own polish - float polished = weathered * smoothstep(0.03, 0.12, dot(texturecolor, vec3(0.2126, 0.7152, 0.0722))); - float weatheredluma = min(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 0.05); - vec3 unused = texturecolor * (weatheredluma / max(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 1e-4)); - unused = mix(unused, vec3(1.0, 0.82, 0.74) * (weatheredluma / 0.8525), 0.5); - texturecolor = mix(texturecolor, unused, smoothstep(0.0, 0.4, weathered)); - texturecolor = (tinted > 0.0 ? texturecolor * mix(vec3(1.0), param[2].rgb, tinted) : texturecolor); - specularity = mix(specularity, min(specularity, 0.03), smoothstep(0.0, 0.4, polished)); - metalic = mix(metalic, 0.0, polished); - glossiness = mix(glossiness, min(glossiness, 0.06 * abs(param[1].w)), polished); - reflectivity = mix(reflectivity, min(reflectivity, 0.08 * param[1].z), smoothstep(0.0, 0.4, polished)); - // Scale ambient before it gets tinted by the texture. - // Sun, headlights and emission are added afterwards so they are NOT - // attenuated by AMBIENT_SCALE - this only dims the indirect term. - fragcolor *= AMBIENT_SCALE; + float polished = 0.0; + if (sunless) + { + // Cab interior: dim ambient less than the exterior path - ambient + // is the dominant indoor illumination, but it was still too bright. + fragcolor *= 0.80; + } + else + { + // the sky lights the faces turned up and towards the bright part of the sky more than the ones turned to the ground + fragcolor = add_sky_ambient(fragcolor, fragnormal); +#ifndef NO_WEATHERING + float weathered = (param[2].w > 0.0 ? clamp(param[2].w * UserData.x, 0.0, 1.0) : 0.0); + float tinted = (param[2].w < 0.0 ? clamp(-param[2].w * UserData.x, 0.0, 1.0) : 0.0); + // a tint only scales the colour of the texture, the steel keeps its own polish + polished = weathered * smoothstep(0.03, 0.12, dot(texturecolor, vec3(0.2126, 0.7152, 0.0722))); + float weatheredluma = min(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 0.05); + vec3 unused = texturecolor * (weatheredluma / max(dot(texturecolor, vec3(0.2126, 0.7152, 0.0722)), 1e-4)); + unused = mix(unused, vec3(1.0, 0.82, 0.74) * (weatheredluma / 0.8525), 0.5); + texturecolor = mix(texturecolor, unused, smoothstep(0.0, 0.4, weathered)); + texturecolor = (tinted > 0.0 ? texturecolor * mix(vec3(1.0), param[2].rgb, tinted) : texturecolor); + specularity = mix(specularity, min(specularity, 0.03), smoothstep(0.0, 0.4, polished)); + metalic = mix(metalic, 0.0, polished); + glossiness = mix(glossiness, min(glossiness, 0.06 * abs(param[1].w)), polished); + reflectivity = mix(reflectivity, min(reflectivity, 0.08 * param[1].z), smoothstep(0.0, 0.4, polished)); +#endif + // Scale ambient before it gets tinted by the texture. + // Sun, headlights and emission are added afterwards so they are NOT + // attenuated by AMBIENT_SCALE - this only dims the indirect term. + fragcolor *= AMBIENT_SCALE; + } vec3 emissioncolor = vec3(emission); - vec3 view_dir = normalize(-f_pos.xyz); - float NdotV = max(dot(fragnormal, view_dir), 0.0); - vec3 F0 = mix(vec3(0.04), texturecolor, metalic); - vec3 fresnel = F0 + (1.0 - F0) * pow(1.0 - NdotV, 5.0); + float NdotV = max(dot(fragnormal, view_dir), 1e-4); - float env_roughness = 1.0 - clamp(glossiness / max(abs(param[1].w), 1.0), 0.0, 1.0); + // glossiness == param[1].w -> mirror, glossiness == 0 -> fully rough + float roughness = clamp(1.0 - glossiness / max(abs(param[1].w), 1.0), 0.0, 1.0); + roughness = max(specular_antialiasing(fragnormal, roughness), MIN_ROUGHNESS); + // metals reflect with their base colour, dielectrics with the specular level (0.5 -> 0.04) + vec3 f0 = mix(vec3(0.08 * specularity), texturecolor, metalic); + spec_f0 = f0; + spec_roughness = roughness; + + // Pre-integrated env BRDF: how much of the surroundings the surface reflects, for its F0, roughness and the view angle. + // the surroundings are the cubemap where the material asks for it (reflectivity) and the ambient light elsewhere, so a + // surface reflects them always, the rough one barely + vec3 env_spec = EnvBRDFApprox(f0, roughness, NdotV); + float env_roughness = roughness; + vec3 cubemap_spec = env_spec; + float cubemap_share = max(reflectivity, 0.0); + float ambient_share = clamp(1.0 - reflectivity, 0.0, 1.0); + // what the diffuse layer is left with + vec3 diffuse_albedo = texturecolor * (1.0 - metalic) * (1.0 - env_spec); + if (legacy_coat) + { + // the varnish: a smooth dielectric layer reflecting the cubemap, worn off where the material is weathered + env_roughness = specular_antialiasing(fragnormal, roughness * smoothstep(0.0, 0.4, polished)); + cubemap_spec = EnvBRDFApprox(mix(vec3(0.04), texturecolor, metalic), env_roughness, NdotV); + ambient_share = 1.0; + diffuse_albedo *= 1.0 - cubemap_spec * min(cubemap_share, 1.0); + } vec3 envcolor = envmap_color_rough(fragnormal, env_roughness); - // Pre-integrated env BRDF: roughness/F0/view-dependent specular scale. - // Replaces the old raw `fresnel` weighting so matte surfaces stop - // mirroring the sky. `env_spec` is the colour to multiply the cubemap by. - vec3 env_spec = EnvBRDFApprox(F0, env_roughness, NdotV); - float env_spec_w = max(env_spec.r, max(env_spec.g, env_spec.b)); - - // Tint texture toward fully-saturated under strong env, weighted by the - // env BRDF (so a rough/matte surface no longer gets washed toward env hue) - vec3 texturecoloryuv = rgb2yuv(texturecolor); - vec3 texturecolorfullv = yuv2rgb(vec3(0.2176, texturecoloryuv.gb)); - vec3 envyuv = rgb2yuv(envcolor); - texturecolor = mix(texturecolor, texturecolorfullv, envyuv.r * reflectivity * env_spec_w); - if (lights_count == 0U) - // Metals carry no diffuse term; env reflection is gated by the env BRDF - // (F0-tinted, roughness-attenuated) so matte surfaces barely reflect. - return fragcolor * texturecolor * (1.0 - metalic) + return fragcolor * diffuse_albedo + emissioncolor * texturecolor - + envcolor * env_spec * reflectivity; + + fragcolor * env_spec * ambient_share + + envcolor * cubemap_spec * cubemap_share; - vec2 sunlight = calc_dir_light(lights[0], fragnormal); + vec3 light_dir = normalize(-lights[0].dir); + float NdotL = max(dot(fragnormal, light_dir), 0.0); // Sharpen sun N.L falloff so the lit-to-shaded terminator on cab // panels, vehicle bodies and terrain reads as a clear edge rather // than a soft Lambertian ramp. Tunable via SUN_NDOTL_SHARPNESS. - float sun_NdotL = pow(sunlight.x, SUN_NDOTL_SHARPNESS); + float sun_NdotL = pow(NdotL, SUN_NDOTL_SHARPNESS); // the clouds hold back the sun (the moon at night) where their shadow falls float cloudsun = cloud_shadow(mat3(inv_view) * f_pos.xyz); - float diffuseamount = sun_NdotL * lights[0].intensity * cloudsun; + // Sun HDR scale -> SUN_DIFFUSE_SCALE, the same for the diffuse and the specular light + vec3 sunlight = lights[0].color * (SUN_DIFFUSE_SCALE * lights[0].intensity * cloudsun); - float shadow1 = 0.0; + float shadow = 0.0; if (shadowtone < 1.0) - shadow1 = (1.0 - shadowtone) * clamp(calc_shadow(), 0.0, 1.0); + shadow = clamp(calc_shadow(), 0.0, 1.0); - // Sun HDR scale -> SUN_DIFFUSE_SCALE (default 2.5). Controls how - // bright sun-lit (unshaded) faces get. Lower this if surfaces in - // direct sun read as too hot/burnt; raise it for more punch. - fragcolor += lights[0].color * SUN_DIFFUSE_SCALE * (1.0 - shadow1) * diffuseamount; + // the cubemap knows nothing of what shades the fragment: it is dimmed a little in the shadow of the sun + float cubemap_shadow = 1.0; + if (sunless) + { + // the sun adds no light in the cab, where it would fall its shadow darkens the ambient light + float diffuseamount = sun_NdotL * lights[0].intensity; + float darkness = (shadowtone < 1.0 ? max(shadow, 1.0 - cloudsun) : 0.0); + fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * darkness, 0.0, 1.0)); + } + else + cubemap_shadow = 1.0 - (1.0 - shadowtone) * shadow * 0.5; - fragcolor = add_headlights(fragcolor, fragnormal); + // 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); - float specularamount = sunlight.y * param[1].y * specularity * lights[0].intensity * cloudsun - * clamp(1.0 - shadowtone, 0.0, 1.0); - if (shadowtone < 1.0) - specularamount *= clamp(1.0 - shadow1, 0.0, 1.0); + // the shadow leaves the share of the sun Shadowtone tells (the old shadow colour of the scenery) to the diffuse light, + // nothing to the highlight + if (!sunless) + fragcolor += sunlight * (sun_NdotL * (1.0 - (1.0 - shadowtone) * shadow)); + vec3 specular = sunlight * specular_light(light_dir, fragnormal, f0, roughness, SUN_BRDF_LIMIT) * (1.0 - shadow); - vec3 specularcolor = specularamount * lights[0].color; + vec3 lampspecular = vec3(0.0); + add_local_lights(fragnormal, fragcolor, lampspecular); - // Env reflection tracked separately — must NOT go through the albedo multiply - // below. Gated by the pre-integrated env BRDF (env_spec) so reflection energy - // falls off with roughness; F0 inside env_spec already tints metals by albedo. - vec3 env_reflection = envcolor * env_spec * reflectivity * (1.0 - shadow1 * 0.5); + return fragcolor * diffuse_albedo // ambient, sun and lamps on the diffuse layer + + specular * param[1].y // highlight of the sun, fresnel coloured (metals take their base colour) + + lampspecular // highlights of the lamps, param[1].y already applied + + emissioncolor * texturecolor + + env_reflection; +} - // --- Physically-based metal/rough combine (Substance 3D Painter parity) --- - // Dielectrics: keep the full diffuse albedo; the direct sun highlight stays - // light-coloured because dielectric F0 is achromatic (~0.04) and - // must NOT be tinted by the base colour. - // Metals: drop the diffuse term entirely and tint the direct highlight - // with the albedo (metal F0 == base colour). - // The highlight *strength* (specularamount) is deliberately left untouched so - // existing material tuning is preserved — only the colour/energy split that - // was previously inverted gets corrected. - vec3 diffuse_albedo = texturecolor * (1.0 - metalic); - vec3 spec_tint = mix(vec3(1.0), texturecolor, metalic); +vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone) +{ + return shade_surface(fragcolor, fragnormal, texturecolor, reflectivity, specularity, shadowtone, false); +} - vec3 result = fragcolor * diffuse_albedo // sun + ambient + headlight diffuse - + specularcolor * spec_tint // direct sun highlight - + emissioncolor * texturecolor // emissive glow (albedo-tinted, unchanged) - + env_reflection; // env reflection (env_spec already F0-tinted) - - return result; -} \ No newline at end of file +// the interior of a cab: no light of the sun, its shadow darkens the ambient light +vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone) +{ + return shade_surface(fragcolor, fragnormal, texturecolor, reflectivity, specularity, shadowtone, true); +} diff --git a/shaders/mat_colored.frag b/shaders/mat_colored.frag index 46de50f0..5f5dd78a 100644 --- a/shaders/mat_colored.frag +++ b/shaders/mat_colored.frag @@ -34,7 +34,7 @@ void main() vec3 fragnormal = normalize(f_normal); float reflectivity = param[1].z; float specularity = dot(pow(param[0].rgb, vec3(2.2)), vec3(0.5)); - glossiness = abs(param[1].w); + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); diff --git a/shaders/mat_default.frag b/shaders/mat_default.frag index bc48d939..66b918c4 100644 --- a/shaders/mat_default.frag +++ b/shaders/mat_default.frag @@ -39,7 +39,7 @@ void main() vec3 fragnormal = normalize(f_normal); float reflectivity = param[1].z; //float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; - glossiness = abs(param[1].w); + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, 0.0, shadow_tone); vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult); diff --git a/shaders/mat_default_detail.frag b/shaders/mat_default_detail.frag index 63d15c7c..5f85e129 100644 --- a/shaders/mat_default_detail.frag +++ b/shaders/mat_default_detail.frag @@ -49,7 +49,7 @@ void main() vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z; float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; - glossiness = abs(param[1].w); + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult); diff --git a/shaders/mat_detail_normalmap.frag b/shaders/mat_detail_normalmap.frag index 85f55289..f46c6fea 100644 --- a/shaders/mat_detail_normalmap.frag +++ b/shaders/mat_detail_normalmap.frag @@ -62,6 +62,7 @@ void main() float reflblend = (normal_map.a + detailnormal_map.a); float reflectivity = reflblend > 1.0 ? param[1].z * 1.0 : param[1].z * reflblend; float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult); diff --git a/shaders/mat_detail_parallax.frag b/shaders/mat_detail_parallax.frag index 75919b13..185368f1 100644 --- a/shaders/mat_detail_parallax.frag +++ b/shaders/mat_detail_parallax.frag @@ -34,6 +34,8 @@ uniform sampler2D normalmap; uniform sampler2D detailnormalmap; #define PARALLAX +// param[2].w is the height offset, not the weathering +#define NO_WEATHERING #include #include #include @@ -68,7 +70,8 @@ void main() vec3 fragnormal = normalize(f_tbn * normalize(vec3(normal.xy + normaldetail.xy, normal.z))); float reflectivity = param[1].z * normal_map.a; - float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; + float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); @@ -91,36 +94,41 @@ void main() vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir) { float pos_len = length(f_pos.xyz); + // gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way + vec2 dcoorddx = dFdx(f_coord); + vec2 dcoorddy = dFdy(f_coord); if (pos_len > 100.0) { return f_coord; } + // the shift fades out before the distance it stops at, so it doesn't switch off at once there + float fade = 1.0 - smoothstep(80.0, 100.0, pos_len); #if EXTRAEFFECTS_ENABLED const float minLayers = 8.0; const float maxLayers = 32.0; float LayersWeight = pos_len / 20.0; vec2 currentTexCoords = f_coord; - float currentDepthMapValue = texture(normalmap, currentTexCoords).b; + float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight); float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers float layerDepth = 1.0 / numLayers; // calculate the size of each layer float currentLayerDepth = 0.0; // depth of current layer - vec2 P = viewDir.xy * param[2].z; // the amount to shift the texture coordinates per layer (from vector P) + vec2 P = viewDir.xy * param[2].z * fade; // the amount to shift the texture coordinates per layer (from vector P) vec2 deltaTexCoords = P / numLayers; while(currentLayerDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P - currentDepthMapValue = texture(normalmap, currentTexCoords).b; // get depthmap value at current texture coordinates + currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates currentLayerDepth += layerDepth; // get depth of next layer } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations) float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation - float beforeDepth = texture(normalmap, prevTexCoords).b - currentLayerDepth + layerDepth; + float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth; float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); @@ -128,7 +136,7 @@ vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir) return finalTexCoords; #else float height = texture(normalmap, f_coord).b; - vec2 p = viewDir.xy / viewDir.z * (height * (param[2].z - param[2].w) * 0.2); + vec2 p = viewDir.xy / viewDir.z * (height * (param[2].z - param[2].w) * 0.2) * fade; return f_coord - p; #endif } diff --git a/shaders/mat_detail_parallax_specgloss.frag b/shaders/mat_detail_parallax_specgloss.frag index fcc33ac3..7ac5a4c2 100644 --- a/shaders/mat_detail_parallax_specgloss.frag +++ b/shaders/mat_detail_parallax_specgloss.frag @@ -38,6 +38,8 @@ uniform sampler2D detailnormalmap; #define PARALLAX +// param[2].w is the height offset, not the weathering +#define NO_WEATHERING #include #include #include @@ -98,36 +100,41 @@ vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir) { float pos_len = length(f_pos.xyz); + // gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way + vec2 dcoorddx = dFdx(f_coord); + vec2 dcoorddy = dFdy(f_coord); if (pos_len > 100.0) { return f_coord; } + // the shift fades out before the distance it stops at, so it doesn't switch off at once there + float fade = 1.0 - smoothstep(80.0, 100.0, pos_len); #if EXTRAEFFECTS_ENABLED const float minLayers = 8.0; const float maxLayers = 32.0; float LayersWeight = pos_len / 20.0; vec2 currentTexCoords = f_coord; - float currentDepthMapValue = texture(normalmap, currentTexCoords).b; + float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight); float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers float layerDepth = 1.0 / numLayers; // calculate the size of each layer float currentLayerDepth = 0.0; // depth of current layer - vec2 P = viewDir.xy * param[2].z; // the amount to shift the texture coordinates per layer (from vector P) + vec2 P = viewDir.xy * param[2].z * fade; // the amount to shift the texture coordinates per layer (from vector P) vec2 deltaTexCoords = P / numLayers; while(currentLayerDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P - currentDepthMapValue = texture(normalmap, currentTexCoords).b; // get depthmap value at current texture coordinates + currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates currentLayerDepth += layerDepth; // get depth of next layer } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations) float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation - float beforeDepth = texture(normalmap, prevTexCoords).b - currentLayerDepth + layerDepth; + float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth; float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); @@ -135,7 +142,7 @@ vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir) return finalTexCoords; #else float height = texture(normalmap, f_coord).b; - vec2 p = viewDir.xy / viewDir.z * (height * (param[2].z - param[2].w) * 0.2); + vec2 p = viewDir.xy / viewDir.z * (height * (param[2].z - param[2].w) * 0.2) * fade; return f_coord - p; #endif } diff --git a/shaders/mat_normalmap.frag b/shaders/mat_normalmap.frag index 3ef505eb..182d67da 100644 --- a/shaders/mat_normalmap.frag +++ b/shaders/mat_normalmap.frag @@ -48,6 +48,7 @@ void main() vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z * texture(normalmap, f_coord).a; float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); diff --git a/shaders/mat_parallax.frag b/shaders/mat_parallax.frag index 7adac054..2211de23 100644 --- a/shaders/mat_parallax.frag +++ b/shaders/mat_parallax.frag @@ -55,7 +55,8 @@ void main() normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0)); vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z * texture(normalmap, f_coord_p).a; - float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; + float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); @@ -78,36 +79,41 @@ void main() vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir) { float pos_len = length(f_pos.xyz); + // gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way + vec2 dcoorddx = dFdx(f_coord); + vec2 dcoorddy = dFdy(f_coord); if (pos_len > 100.0) { return f_coord; } + // the shift fades out before the distance it stops at, so it doesn't switch off at once there + float fade = 1.0 - smoothstep(80.0, 100.0, pos_len); #if EXTRAEFFECTS_ENABLED const float minLayers = 8.0; const float maxLayers = 32.0; float LayersWeight = pos_len / 20.0; vec2 currentTexCoords = f_coord; - float currentDepthMapValue = texture(normalmap, currentTexCoords).b; + float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight); float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers float layerDepth = 1.0 / numLayers; // calculate the size of each layer float currentLayerDepth = 0.0; // depth of current layer - vec2 P = viewDir.xy * param[2].y; // the amount to shift the texture coordinates per layer (from vector P) + vec2 P = viewDir.xy * param[2].y * fade; // the amount to shift the texture coordinates per layer (from vector P) vec2 deltaTexCoords = P / numLayers; while(currentLayerDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P - currentDepthMapValue = texture(normalmap, currentTexCoords).b; // get depthmap value at current texture coordinates + currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates currentLayerDepth += layerDepth; // get depth of next layer } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations) float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation - float beforeDepth = texture(normalmap, prevTexCoords).b - currentLayerDepth + layerDepth; + float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth; float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); @@ -115,7 +121,7 @@ vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir) return finalTexCoords; #else float height = texture(normalmap, f_coord).b; - vec2 p = viewDir.xy / viewDir.z * (height * (param[2].y - param[2].z) * 0.2); + vec2 p = viewDir.xy / viewDir.z * (height * (param[2].y - param[2].z) * 0.2) * fade; return f_coord - p; #endif } diff --git a/shaders/mat_parallax_specgloss.frag b/shaders/mat_parallax_specgloss.frag index 8b590fae..46c7f456 100644 --- a/shaders/mat_parallax_specgloss.frag +++ b/shaders/mat_parallax_specgloss.frag @@ -84,36 +84,41 @@ void main() vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir) { float pos_len = length(f_pos.xyz); + // gradients of the coordinates taken here: inside the loop the fragments of a quad don't go the same way + vec2 dcoorddx = dFdx(f_coord); + vec2 dcoorddy = dFdy(f_coord); if (pos_len > 100.0) { return f_coord; } + // the shift fades out before the distance it stops at, so it doesn't switch off at once there + float fade = 1.0 - smoothstep(80.0, 100.0, pos_len); #if EXTRAEFFECTS_ENABLED const float minLayers = 8.0; const float maxLayers = 32.0; float LayersWeight = pos_len / 20.0; vec2 currentTexCoords = f_coord; - float currentDepthMapValue = texture(normalmap, currentTexCoords).b; + float currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; LayersWeight = min(abs(dot(vec3(0.0, 0.0, 1.0), viewDir)),LayersWeight); float numLayers = mix(maxLayers, minLayers, clamp(LayersWeight, 0.0, 1.0)); // number of depth layers float layerDepth = 1.0 / numLayers; // calculate the size of each layer float currentLayerDepth = 0.0; // depth of current layer - vec2 P = viewDir.xy * param[2].y; // the amount to shift the texture coordinates per layer (from vector P) + vec2 P = viewDir.xy * param[2].y * fade; // the amount to shift the texture coordinates per layer (from vector P) vec2 deltaTexCoords = P / numLayers; while(currentLayerDepth < currentDepthMapValue) { currentTexCoords -= deltaTexCoords; // shift texture coordinates along direction of P - currentDepthMapValue = texture(normalmap, currentTexCoords).b; // get depthmap value at current texture coordinates + currentDepthMapValue = textureGrad(normalmap, currentTexCoords, dcoorddx, dcoorddy).b; // get depthmap value at current texture coordinates currentLayerDepth += layerDepth; // get depth of next layer } vec2 prevTexCoords = currentTexCoords + deltaTexCoords; // get texture coordinates before collision (reverse operations) float afterDepth = currentDepthMapValue - currentLayerDepth; // get depth after and before collision for linear interpolation - float beforeDepth = texture(normalmap, prevTexCoords).b - currentLayerDepth + layerDepth; + float beforeDepth = textureGrad(normalmap, prevTexCoords, dcoorddx, dcoorddy).b - currentLayerDepth + layerDepth; float weight = afterDepth / (afterDepth - beforeDepth); // interpolation of texture coordinates vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight); @@ -121,7 +126,7 @@ vec2 ParallaxMapping(vec2 f_coord, vec3 viewDir) return finalTexCoords; #else float height = texture(normalmap, f_coord).b; - vec2 p = viewDir.xy / viewDir.z * (height * (param[2].y - param[2].z) * 0.2); + vec2 p = viewDir.xy / viewDir.z * (height * (param[2].y - param[2].z) * 0.2) * fade; return f_coord - p; #endif } diff --git a/shaders/mat_rain_windscreen.frag b/shaders/mat_rain_windscreen.frag index e441083d..c8dc657b 100644 --- a/shaders/mat_rain_windscreen.frag +++ b/shaders/mat_rain_windscreen.frag @@ -158,7 +158,7 @@ void main() { vec3 fragnormal = normalize(f_normal); float reflectivity = param[1].z; float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; - glossiness = abs(param[1].w); + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); vec4 color = vec4(fragcolor, tex_color.a * alpha_mult); diff --git a/shaders/mat_reflmap.frag b/shaders/mat_reflmap.frag index bd4c4ae7..7e09f18a 100644 --- a/shaders/mat_reflmap.frag +++ b/shaders/mat_reflmap.frag @@ -42,7 +42,7 @@ void main() vec3 fragnormal = normalize(f_normal); float reflectivity = param[1].z * texture(reflmap, f_coord).a; float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; - glossiness = abs(param[1].w); + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, 0.0, shadow_tone); diff --git a/shaders/mat_shadowlessnormalmap.frag b/shaders/mat_shadowlessnormalmap.frag index cb2c040e..1a872527 100644 --- a/shaders/mat_shadowlessnormalmap.frag +++ b/shaders/mat_shadowlessnormalmap.frag @@ -48,7 +48,7 @@ void main() vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z * texture(normalmap, f_coord).a; float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; - glossiness = abs(param[1].w); + legacy_gloss(); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, 1.0); vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult); diff --git a/shaders/mat_sunlessnormalmap.frag b/shaders/mat_sunlessnormalmap.frag index 5778253e..db3c1386 100644 --- a/shaders/mat_sunlessnormalmap.frag +++ b/shaders/mat_sunlessnormalmap.frag @@ -30,58 +30,6 @@ uniform sampler2D normalmap; #include #include -vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone) -{ - // diffuse colour applied once, to the albedo (see material_diffuse() in light_common.glsl) - texturecolor *= material_diffuse(); - - // Cab interior: dim ambient less than the exterior path because - // ambient is the dominant indoor illumination. 0.80 trims the - // brightest faces without making the cab feel under-lit. - fragcolor *= 0.80; - - vec3 emissioncolor = vec3(emission); - vec3 envcolor = envmap_color(fragnormal); - -// yuv path - vec3 texturecoloryuv = rgb2yuv(texturecolor); - vec3 texturecolorfullv = yuv2rgb(vec3(0.2176, texturecoloryuv.gb)); -// hsl path -// vec3 texturecolorhsl = rgb2hsl(texturecolor); -// vec3 texturecolorfullv = hsl2rgb(vec3(texturecolorhsl.rg, 0.5)); - - vec3 envyuv = rgb2yuv(envcolor); - texturecolor = mix(texturecolor, texturecolorfullv, envyuv.r * reflectivity); - - if(lights_count == 0U) - return (fragcolor + emissioncolor + envcolor * reflectivity) * texturecolor; - - vec2 sunlight = calc_dir_light(lights[0], fragnormal); - - // Sharpen N.L for stronger contrast between lit and shaded cab - // surfaces (uses SUN_NDOTL_SHARPNESS from light_common.glsl). - float sun_NdotL = pow(sunlight.x, SUN_NDOTL_SHARPNESS); - float diffuseamount = sun_NdotL * lights[0].intensity; - // the clouds hold back the sun where their shadow falls, as the shadow map does - float cloudsun = cloud_shadow(mat3(inv_view) * f_pos.xyz); - fragcolor += envcolor * reflectivity; - float specularamount = (sunlight.y * param[1].y * specularity) * lights[0].intensity * cloudsun; - glossiness = abs(param[1].w); - - fragcolor = add_headlights(fragcolor, fragnormal); - - if (shadowtone < 1.0) - { - float shadow = max(calc_shadow(), 1.0 - cloudsun); - specularamount *= clamp(1.0 - shadow, 0.0, 1.0); - fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * shadow + specularamount, 0.0, 1.0)); - } - fragcolor += emissioncolor; - fragcolor *= texturecolor; - - return fragcolor; -} - void main() { vec4 tex_color = texture(diffuse, f_coord); @@ -100,6 +48,7 @@ void main() vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z * texture(normalmap, f_coord).a; float specularity = (tex_color.r + tex_color.g + tex_color.b) * 0.5; + legacy_gloss(); fragcolor = apply_lights_sunless(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult); diff --git a/shaders/mat_sunlessnormalmap_specgloss.frag b/shaders/mat_sunlessnormalmap_specgloss.frag index 4ab9ae37..93e2c244 100644 --- a/shaders/mat_sunlessnormalmap_specgloss.frag +++ b/shaders/mat_sunlessnormalmap_specgloss.frag @@ -34,77 +34,6 @@ uniform sampler2D specgloss; #include #include -vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone) -{ - // diffuse colour applied once, to the albedo (see material_diffuse() in light_common.glsl) - texturecolor *= material_diffuse(); - // Cab interior: dim ambient less than the exterior path - ambient - // is the dominant indoor illumination, but it was still too bright. - fragcolor *= 0.80; - - vec3 emissioncolor = vec3(emission); - - vec3 view_dir = normalize(-f_pos.xyz); - float NdotV = max(dot(fragnormal, view_dir), 0.0); - vec3 F0 = mix(vec3(0.04), texturecolor, metalic); - vec3 fresnel = F0 + (1.0 - F0) * pow(1.0 - NdotV, 5.0); - - float env_roughness = 1.0 - clamp(glossiness / max(abs(param[1].w), 1.0), 0.0, 1.0); - vec3 envcolor = envmap_color_rough(fragnormal, env_roughness); - - // Pre-integrated env BRDF (matches apply_lights): roughness-attenuated - // reflection so matte cab surfaces no longer mirror the environment. - vec3 env_spec = EnvBRDFApprox(F0, env_roughness, NdotV); - float env_spec_w = max(env_spec.r, max(env_spec.g, env_spec.b)); - - vec3 texturecoloryuv = rgb2yuv(texturecolor); - vec3 texturecolorfullv = yuv2rgb(vec3(0.2176, texturecoloryuv.gb)); - vec3 envyuv = rgb2yuv(envcolor); - texturecolor = mix(texturecolor, texturecolorfullv, envyuv.r * reflectivity * env_spec_w); - - if (lights_count == 0U) - return fragcolor * texturecolor * (1.0 - metalic) - + emissioncolor * texturecolor - + envcolor * env_spec * reflectivity; - - vec2 sunlight = calc_dir_light(lights[0], fragnormal); - // Sharpen N.L for stronger contrast between lit and shaded cab - // surfaces (uses SUN_NDOTL_SHARPNESS from light_common.glsl). - float sun_NdotL = pow(sunlight.x, SUN_NDOTL_SHARPNESS); - float diffuseamount = sun_NdotL * lights[0].intensity; - // the clouds hold back the sun where their shadow falls, as the shadow map does - float cloudsun = cloud_shadow(mat3(inv_view) * f_pos.xyz); - - float specularamount = sunlight.y * param[1].y * specularity * lights[0].intensity * cloudsun; - - fragcolor = add_headlights(fragcolor, fragnormal); - - if (shadowtone < 1.0) - { - float shadow = max(calc_shadow(), 1.0 - cloudsun); - specularamount *= clamp(1.0 - shadow, 0.0, 1.0); - fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * shadow, 0.0, 1.0)); - } - - vec3 specularcolor = specularamount * lights[0].color; - - // Env reflection gated by env BRDF (roughness-attenuated, F0-tinted). - vec3 env_reflection = envcolor * env_spec * reflectivity; - - // Physically-based metal/rough combine (matches apply_lights / Substance): - // dielectric -> light-coloured highlight + full diffuse; metal -> albedo-tinted - // highlight, no diffuse term. Highlight strength left unchanged. - vec3 diffuse_albedo = texturecolor * (1.0 - metalic); - vec3 spec_tint = mix(vec3(1.0), texturecolor, metalic); - - vec3 result = fragcolor * diffuse_albedo - + specularcolor * spec_tint - + emissioncolor * texturecolor - + env_reflection; // env_spec already F0-tinted for metals - - return result; -} - void main() { vec4 tex_color = texture(diffuse, f_coord); diff --git a/shaders/mat_terrain.frag b/shaders/mat_terrain.frag index cd219690..0320c626 100644 --- a/shaders/mat_terrain.frag +++ b/shaders/mat_terrain.frag @@ -43,6 +43,8 @@ uniform highp sampler2DArray layerspecgloss; uniform highp sampler2D layertable; #define NORMALMAP +// param[2] holds the placement of the chunk, not the weathering +#define NO_WEATHERING #include #include #include @@ -111,7 +113,9 @@ void main() } else { - gloss += weight; + // a layer without a specgloss map: a dielectric of the glossiness: of the material (a blinn-phong exponent) + specularity += 0.5 * weight; + gloss += (1.0 - phong_roughness(abs(param[1].w))) * weight; } reflectivity += layerreflection * weight; total += weight; diff --git a/shaders/mat_water.frag b/shaders/mat_water.frag index ca461f21..b1c34179 100644 --- a/shaders/mat_water.frag +++ b/shaders/mat_water.frag @@ -58,7 +58,8 @@ void main() normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0)); vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z * texture(normalmap, texture_coords ).a; - float specularity = 1.0; + // water reflects 2% at normal incidence + float specularity = 0.25; glossiness = abs(param[1].w); fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); @@ -70,10 +71,10 @@ void main() // fresnel effect vec3 view_dir = normalize(vec3(0.0f, 0.0f, 0.0f) - f_pos.xyz); - float fresnel = pow ( dot (f_normal, view_dir), 0.2 ); + float fresnel = pow(max(dot(normalize(f_normal), view_dir), 0.0), 0.2); float fresnel_inv = ((fresnel - 1.0 ) * -1.0 ); - vec4 color = vec4(apply_fog(clamp(fragcolor, 0.0, 1.0)), clamp((fresnel_inv + param[0].a), 0.0, 1.0)); + vec4 color = vec4(apply_fog(fragcolor), clamp((fresnel_inv + param[0].a), 0.0, 1.0)); #if POSTFX_ENABLED out_color = color; #else diff --git a/shaders/mat_water_specgloss.frag b/shaders/mat_water_specgloss.frag index 8d263a83..c11e5eb9 100644 --- a/shaders/mat_water_specgloss.frag +++ b/shaders/mat_water_specgloss.frag @@ -62,9 +62,10 @@ void main() normal.z = sqrt(1.0 - clamp((dot(normal.xy, normal.xy)), 0.0, 1.0)); vec3 fragnormal = normalize(f_tbn * normalize(normal.xyz)); float reflectivity = param[1].z * texture(normalmap, texture_coords ).a; - float specularity = texture(specgloss, f_coord).r; - glossiness = texture(specgloss, f_coord).g * abs(param[1].w); - float metalic = texture(specgloss, f_coord).b; + vec4 specgloss_map = texture(specgloss, texture_coords); + float specularity = specgloss_map.r; + glossiness = specgloss_map.g * abs(param[1].w); + metalic = specgloss_map.b; fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone); @@ -75,10 +76,10 @@ void main() // fresnel effect vec3 view_dir = normalize(vec3(0.0f, 0.0f, 0.0f) - f_pos.xyz); - float fresnel = pow ( dot (f_normal, view_dir), 0.2 ); + float fresnel = pow(max(dot(normalize(f_normal), view_dir), 0.0), 0.2); float fresnel_inv = ((fresnel - 1.0 ) * -1.0 ); - vec4 color = vec4(apply_fog(clamp(fragcolor, 0.0, 1.0)), clamp((fresnel_inv + param[0].a), 0.0, 1.0)); + vec4 color = vec4(apply_fog(fragcolor), clamp((fresnel_inv + param[0].a), 0.0, 1.0)); #if POSTFX_ENABLED out_color = color; #else diff --git a/shaders/vertex.vert b/shaders/vertex.vert index b8c631e3..b042783c 100644 --- a/shaders/vertex.vert +++ b/shaders/vertex.vert @@ -38,9 +38,11 @@ void main() gl_Position = f_clip_pos; gl_PointSize = param[1].x; - vec3 T = normalize(mvn * v_tangent.xyz); vec3 N = f_normal; - vec3 B = normalize(cross(N, T)); + vec3 T = mvn * v_tangent.xyz; + T = normalize(T - N * dot(N, T)); + // w is -1 where the texture is mirrored (calculate_tangents()), the bitangent turns with it + vec3 B = cross(N, T) * (v_tangent.w < 0.0 ? -1.0 : 1.0); f_tbn = mat3(T, B, N); mat3 TBN = transpose(f_tbn);