16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 04:19:19 +02:00

Merge pull request #96 from MaSzyna-EU07/opengl-improvements

Opengl improvements
This commit is contained in:
2026-04-27 17:56:48 +02:00
committed by GitHub
25 changed files with 417 additions and 120 deletions

View File

@@ -32,7 +32,9 @@ void gl::cubemap::bind(int unit)
void gl::cubemap::generate_mipmaps()
{
glBindTexture(GL_TEXTURE_CUBE_MAP, *this);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glBindTexture(GL_TEXTURE_CUBE_MAP, *this);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

View File

@@ -147,6 +147,7 @@ commanddescription_sequence Commands_descriptions = {
{"radiostopenable", command_target::vehicle, command_mode::oneoff},
{"radiostopdisable", command_target::vehicle, command_mode::oneoff},
{"radiostoptest", command_target::vehicle, command_mode::oneoff},
{"radiocall1send", command_target::vehicle, command_mode::oneoff},
{"radiocall3send", command_target::vehicle, command_mode::oneoff},
{"radiovolumeincrease", command_target::vehicle, command_mode::oneoff},
{"radiovolumedecrease", command_target::vehicle, command_mode::oneoff},
@@ -526,6 +527,7 @@ std::unordered_map<std::string, user_command> commandMap = {
{"radiostopenable", user_command::radiostopenable},
{"radiostopdisable", user_command::radiostopdisable},
{"radiostoptest", user_command::radiostoptest},
{"radiocall1send", user_command::radiocall1send},
{"radiocall3send", user_command::radiocall3send},
{"radiovolumeincrease", user_command::radiovolumeincrease},
{"radiovolumedecrease", user_command::radiovolumedecrease},

View File

@@ -141,6 +141,7 @@ enum class user_command
radiostopenable,
radiostopdisable,
radiostoptest,
radiocall1send,
radiocall3send,
radiovolumeincrease,
radiovolumedecrease,

View File

@@ -835,6 +835,9 @@ drivermouse_input::default_bindings() {
{ "radiotest_sw:", {
user_command::radiostoptest,
user_command::none } },
{ "radiocall1_sw:", {
user_command::radiocall1send,
user_command::none } },
{ "radiocall3_sw:", {
user_command::radiocall3send,
user_command::none } },

View File

@@ -162,6 +162,35 @@ bool opengl33_renderer::Init(GLFWwindow *Window)
m_pfx_tonemapping = std::make_unique<gl::postfx>("tonemapping");
m_pfx_chromaticaberration = std::make_unique<gl::postfx>( "chromaticaberration" );
m_pfx_ssao = std::make_unique<gl::postfx>("ssao");
m_pfx_ssao_blur = std::make_unique<gl::postfx>("ssao_blur");
m_pfx_ssao_apply = std::make_unique<gl::postfx>("ssao_apply");
// Generate hemisphere kernel (z > 0 = toward surface normal)
std::uniform_real_distribution<float> rnd(0.0f, 1.0f);
std::default_random_engine gen(42); // fixed seed for consistency
for (int i = 0; i < 32; i++) {
glm::vec3 s(rnd(gen)*2.0f-1.0f, rnd(gen)*2.0f-1.0f, rnd(gen));
s = glm::normalize(s) * rnd(gen);
float scale = float(i) / 32.0f;
scale = glm::mix(0.1f, 1.0f, scale * scale); // accelerating interpolant
m_ssao_kernel[i] = s * scale;
}
// Generate 4x4 noise texture (random rotation vectors in XY)
std::vector<glm::vec3> noise_data;
for (int i = 0; i < 16; i++)
noise_data.push_back(glm::normalize(glm::vec3(rnd(gen)*2.0f-1.0f, rnd(gen)*2.0f-1.0f, 0.0f)));
// noise texture (4x4 random rotation vectors in tangent space)
// noise texture (4x4 random rotation vectors in tangent space)
m_ssao_noise_tex = std::make_unique<opengl_texture>();
m_ssao_noise_tex->alloc_rendertarget(GL_RGB32F, GL_RGB, 4, 4, 1, 1, GL_REPEAT);
m_ssao_noise_tex->bind(0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_RGB, GL_FLOAT, noise_data.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
m_empty_cubemap = std::make_unique<gl::cubemap>();
m_empty_cubemap->alloc(Global.gfx_format_color, 16, 16, GL_RGB, GL_FLOAT);
@@ -426,7 +455,7 @@ bool opengl33_renderer::init_viewport(viewport_config &vp)
vp.msaa_fb->attach(*vp.msaa_rbc, GL_COLOR_ATTACHMENT0);
vp.msaa_fb->attach(*vp.msaa_rbd, GL_DEPTH_ATTACHMENT);
if (Global.gfx_postfx_chromaticaberration_enabled || Global.gfx_postfx_motionblur_enabled)
if (Global.gfx_postfx_chromaticaberration_enabled || Global.gfx_postfx_motionblur_enabled || Global.gfx_postfx_ssao_enabled)
{
vp.main_tex = std::make_unique<opengl_texture>();
vp.main_tex->alloc_rendertarget(Global.gfx_format_color, GL_RGB, vp.width, vp.height, 1, 1, GL_CLAMP_TO_EDGE);
@@ -470,6 +499,25 @@ bool opengl33_renderer::init_viewport(viewport_config &vp)
vp.main2_fb = std::make_unique<gl::framebuffer>();
vp.main2_fb->attach(*vp.main2_tex, GL_COLOR_ATTACHMENT0);
if (Global.gfx_postfx_ssao_enabled)
{
int ssao_w = std::max(1, vp.width / 2);
int ssao_h = std::max(1, vp.height / 2);
vp.ssao_tex = std::make_unique<opengl_texture>();
vp.ssao_tex->alloc_rendertarget(GL_R8, GL_RED, ssao_w, ssao_h, 1, 1, GL_CLAMP_TO_EDGE);
vp.ssao_fb = std::make_unique<gl::framebuffer>();
vp.ssao_fb->attach(*vp.ssao_tex, GL_COLOR_ATTACHMENT0);
if (!vp.ssao_fb->is_complete()) { ErrorLog("ssao framebuffer setup failed"); return false; }
vp.ssao_blur_tex = std::make_unique<opengl_texture>();
vp.ssao_blur_tex->alloc_rendertarget(GL_R8, GL_RED, ssao_w, ssao_h, 1, 1, GL_CLAMP_TO_EDGE);
vp.ssao_blur_fb = std::make_unique<gl::framebuffer>();
vp.ssao_blur_fb->attach(*vp.ssao_blur_tex, GL_COLOR_ATTACHMENT0);
if (!vp.ssao_blur_fb->is_complete()) { ErrorLog("ssao blur framebuffer setup failed"); return false; }
}
if (!vp.main2_fb->is_complete())
return false;
}
@@ -913,7 +961,7 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
if (!Global.gfx_usegles)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
if (!Global.gfx_skippipeline)
if (!Global.gfx_skippipeline)
{
if (Global.gfx_postfx_motionblur_enabled)
{
@@ -927,12 +975,36 @@ void opengl33_renderer::Render_pass(viewport_config &vp, rendermode const Mode)
model_ubo->update(model_ubs);
m_pfx_motionblur->apply({vp.main_tex.get(), vp.main_texv.get(), vp.main_texd.get()}, vp.main2_fb.get());
}
else if (Global.gfx_postfx_ssao_enabled)
{
// resolve color+depth so SSAO can sample the depth texture
vp.main_fb->clear(GL_COLOR_BUFFER_BIT);
vp.main_fb->setup_drawing(1);
vp.msaa_fb->blit_to(vp.main_fb.get(), vp.width, vp.height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0);
vp.msaa_fb->blit_to(vp.main_fb.get(), vp.width, vp.height, GL_DEPTH_BUFFER_BIT, GL_DEPTH_ATTACHMENT);
}
else
{
vp.main2_fb->clear(GL_COLOR_BUFFER_BIT);
vp.msaa_fb->blit_to(vp.main2_fb.get(), vp.width, vp.height, GL_COLOR_BUFFER_BIT, GL_COLOR_ATTACHMENT0);
}
// SSAO: depth -> occlusion -> blur -> modulate color into main2_fb
if (Global.gfx_postfx_ssao_enabled && !Global.gfx_postfx_motionblur_enabled)
{
int ssao_w = std::max(1, vp.width / 2);
int ssao_h = std::max(1, vp.height / 2);
// (kernel is generated procedurally in the shader; no UBO upload needed)
glViewport(0, 0, ssao_w, ssao_h);
m_pfx_ssao ->apply({ vp.main_texd.get(), m_ssao_noise_tex.get() }, vp.ssao_fb.get());
m_pfx_ssao_blur->apply({ vp.ssao_tex.get() }, vp.ssao_blur_fb.get());
glViewport(0, 0, vp.width, vp.height);
vp.main2_fb->clear(GL_COLOR_BUFFER_BIT);
m_pfx_ssao_apply->apply({ vp.main_tex.get(), vp.ssao_blur_tex.get() }, vp.main2_fb.get());
}
if (!Global.gfx_usegles && !Global.gfx_shadergamma)
glEnable(GL_FRAMEBUFFER_SRGB);

View File

@@ -227,6 +227,12 @@ class opengl33_renderer : public gfx_renderer {
std::unique_ptr<gl::framebuffer> main2_fb;
std::unique_ptr<opengl_texture> main2_tex;
// ssao
std::unique_ptr<opengl_texture> ssao_tex;
std::unique_ptr<opengl_texture> ssao_blur_tex;
std::unique_ptr<gl::framebuffer> ssao_fb;
std::unique_ptr<gl::framebuffer> ssao_blur_fb;
// LDR backbuffer for offscreen rendering
std::unique_ptr<gl::framebuffer> backbuffer_fb;
std::unique_ptr<opengl_texture> backbuffer_tex;
@@ -396,6 +402,13 @@ class opengl33_renderer : public gfx_renderer {
std::unique_ptr<gl::postfx> m_pfx_tonemapping;
std::unique_ptr<gl::postfx> m_pfx_chromaticaberration;
// postfx ssao
std::unique_ptr<gl::postfx> m_pfx_ssao;
std::unique_ptr<gl::postfx> m_pfx_ssao_blur;
std::unique_ptr<gl::postfx> m_pfx_ssao_apply;
std::unique_ptr<opengl_texture> m_ssao_noise_tex;
glm::vec3 m_ssao_kernel[32];
std::unique_ptr<gl::program> m_shadow_shader;
std::unique_ptr<gl::program> m_alpha_shadow_shader;

View File

@@ -13,3 +13,16 @@ vec3 envmap_color( vec3 normal )
#endif
return envcolor;
}
// Roughness-aware env map lookup — uses mip levels for blurry reflections.
// lod 0.0 = mirror sharp, lod ~8.0 = fully diffuse blur.
vec3 envmap_color_lod(vec3 fragnormal, float lod)
{
#if ENVMAP_ENABLED
vec3 refvec = reflect(f_pos.xyz, fragnormal); // view space — matches envmap_color exactly
refvec = vec3(inv_view * vec4(refvec, 0.0)); // world space — was missing
return textureLod(envmap, refvec, lod).rgb;
#else
return vec3(0.5); // was vec3(0.0), match the non-LOD fallback
#endif
}

View File

@@ -10,6 +10,27 @@ uniform sampler2D headlightmap;
float glossiness = 1.0;
float metalic = 0.0;
// ---------------------------------------------------------------------
// Lighting balance tunables - tweak these to control overall scene
// exposure without touching tonemapping.glsl.
//
// AMBIENT_SCALE: brightness of SHADED faces (indirect/sky term).
// Lower -> deeper shadows, less burn under bright
// textures. Higher -> flatter / brighter shading.
//
// SUN_DIFFUSE_SCALE: brightness of UNSHADED (sun-lit) faces. Lower
// this to dim hot surfaces in direct sunlight
// without affecting shaded areas. Was 3.5; 2.5
// calmly fits the ACES tonemap shoulder.
//
// SUN_NDOTL_SHARPNESS: N.L curve on the sun. 1.0 = pure Lambert, higher
// = sharper terminator (more contrast between
// lit and shaded faces of the same surface).
// ---------------------------------------------------------------------
const float AMBIENT_SCALE = 0.65;
const float SUN_DIFFUSE_SCALE = 2.5;
const float SUN_NDOTL_SHARPNESS = 1.25;
float length2(vec3 v)
{
return dot(v, v);
@@ -60,7 +81,17 @@ vec2 calc_light(vec3 light_dir, vec3 fragnormal)
vec3 halfway_dir = normalize(light_dir + view_dir);
float diffuse_v = max(dot(fragnormal, light_dir), 0.0);
float specular_v = pow(max(dot(fragnormal, halfway_dir), 0.0), max(glossiness, 0.01)) * diffuse_v;
// Energy-conserving Blinn-Phong normalization:
// (n+8)/(8*pi) ensures the specular lobe integrates to the same
// total energy regardless of glossiness — low glossiness stays dim
// and spreads wide (blurry), high glossiness is bright and tight (sharp).
// Capped at 4.0 so very high glossiness (n>~92) does not produce pinhole
// highlights that blow past the tonemap shoulder and read as burnt white.
float n = max(glossiness, 0.01);
float normalization = min((n + 8.0) / (8.0 * 3.14159265), 4.0);
float NdotH = max(dot(fragnormal, halfway_dir), 0.0);
float specular_v = normalization * pow(NdotH, n);
return vec2(diffuse_v, specular_v);
}
@@ -108,7 +139,10 @@ vec2 calc_headlights(light_s light, vec3 fragnormal)
return vec2(0.0);
vec3 light_dir = normalize(light.pos - f_pos.xyz);
vec2 part = vec2(1.0) * clamp(dot(fragnormal, light_dir) + 0.25, 0.0, 1.0);
// Tighter wrap (was +0.25): faces angled away from the headlight cone
// fall off to dark much faster, so cab/exterior surfaces read with a
// clear directional shape instead of a flat half-lit wash.
vec2 part = vec2(1.0) * clamp(dot(fragnormal, light_dir) + 0.10, 0.0, 1.0);
float distance = length(light.pos - f_pos.xyz);
float atten = 1.0f / (1.0f + light.linear * distance + light.quadratic * (distance * distance));
atten *= mix(1.0, 0.0, clamp((coords.z - 0.998) * 500.0, 0.0, 1.0));
@@ -121,71 +155,75 @@ vec2 calc_headlights(light_s light, vec3 fragnormal)
// do magic here
vec3 apply_lights(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone)
{
vec3 basecolor = param[0].rgb;
vec3 basecolor = param[0].rgb;
// Scale ambient before it gets tinted by basecolor / texture.
// Sun, headlights and emission are added afterwards so they are NOT
// attenuated by AMBIENT_SCALE - this only dims the indirect term.
fragcolor *= basecolor * AMBIENT_SCALE;
fragcolor *= basecolor;
vec3 emissioncolor = basecolor * emission;
vec3 emissioncolor = basecolor * emission;
vec3 envcolor = envmap_color(fragnormal);
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);
// 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));
const float MAX_REFLECTION_LOD = 8.0;
float env_roughness = 1.0 - clamp(glossiness / max(abs(param[1].w), 1.0), 0.0, 1.0);
vec3 envcolor = envmap_color_lod(fragnormal, env_roughness * MAX_REFLECTION_LOD);
vec3 envyuv = rgb2yuv(envcolor);
texturecolor = mix(texturecolor, texturecolorfullv, envyuv.r * reflectivity);
// Tint texture toward fully-saturated under strong env, weighted by fresnel
vec3 texturecoloryuv = rgb2yuv(texturecolor);
vec3 texturecolorfullv = yuv2rgb(vec3(0.2176, texturecoloryuv.gb));
vec3 envyuv = rgb2yuv(envcolor);
texturecolor = mix(texturecolor, texturecolorfullv, envyuv.r * reflectivity * fresnel.r);
if(lights_count == 0U)
return (fragcolor + emissioncolor + envcolor * reflectivity) * texturecolor;
if (lights_count == 0U)
return (fragcolor + emissioncolor) * texturecolor
+ envcolor * fresnel * reflectivity;
// fragcolor *= lights[0].intensity;
vec2 sunlight = calc_dir_light(lights[0], fragnormal);
vec2 sunlight = calc_dir_light(lights[0], fragnormal);
// 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 diffuseamount = sun_NdotL * param[1].x * lights[0].intensity;
float diffuseamount = (sunlight.x * param[1].x) * lights[0].intensity;
// fragcolor += mix(lights[0].color * diffuseamount, envcolor, reflectivity);
float shadow1 = 0.0;
if (shadowtone < 1.0)
{
shadow1 = (1 - shadowtone) * clamp(calc_shadow(), 0.0, 1.00);
}
fragcolor += lights[0].color * 5.0 * (1.0 - shadow1) * diffuseamount;
fragcolor = mix(fragcolor * 0.35, envcolor, reflectivity);
float shadow1 = 0.0;
if (shadowtone < 1.0)
shadow1 = (1.0 - shadowtone) * clamp(calc_shadow(), 0.0, 1.0);
for (uint i = 1U; i < lights_count; i++)
{
light_s light = lights[i];
vec2 part = vec2(0.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;
// if (light.type == LIGHT_SPOT)
// part = calc_spot_light(light, fragnormal);
// else if (light.type == LIGHT_POINT)
// part = calc_point_light(light, fragnormal);
// else if (light.type == LIGHT_DIR)
// part = calc_dir_light(light, fragnormal);
// else if (light.type == LIGHT_HEADLIGHTS)
part = calc_headlights(light, fragnormal);
for (uint i = 1U; i < lights_count; i++)
{
light_s light = lights[i];
vec2 part = calc_headlights(light, fragnormal);
fragcolor += light.color * (part.x * param[1].x + part.y * param[1].y) * light.intensity;
}
fragcolor += light.color * (part.x * param[1].x + part.y * param[1].y) * light.intensity;
}
float specularamount = sunlight.y * param[1].y * specularity * lights[0].intensity
* clamp(1.0 - shadowtone, 0.0, 1.0);
if (shadowtone < 1.0)
specularamount *= clamp(1.0 - shadow1, 0.0, 1.0);
float specularamount = (sunlight.y * param[1].y * specularity) * lights[0].intensity * clamp(1.0 - shadowtone, 0.0, 1.0);
if (shadowtone < 1.0)
{
//float shadow = calc_shadow();
specularamount *= clamp(1.0 - shadow1, 0.0, 1.00);
//fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp((diffuseamount) * shadow1 , 0.0, 1.0));
}
fragcolor += emissioncolor;
vec3 specularcolor = specularamount * lights[0].color;
fragcolor += emissioncolor;
vec3 specularcolor = specularamount * lights[0].color;
if (param[1].w < 0.0)
{
float metalic = 1.0;
}
fragcolor = mix(((fragcolor + specularcolor) * texturecolor),(fragcolor * texturecolor + specularcolor),metalic) ;
// Env reflection tracked separately — must NOT go through the albedo multiply below
vec3 env_reflection = envcolor * fresnel * reflectivity * (1.0 - shadow1 * 0.5);
return fragcolor;
}
// Diffuse + specular: albedo tints diffuse, metals also tint specular
vec3 result = mix(
(fragcolor + specularcolor) * texturecolor,
fragcolor * texturecolor + specularcolor,
metalic);
// Env added after albedo multiply: raw for dielectrics, albedo-tinted for metals
result += mix(env_reflection, env_reflection * texturecolor, metalic);
return result;
}

View File

@@ -43,7 +43,7 @@ void main()
float reflectivity = param[1].z;
float specularity = texture(specgloss, f_coord).r;
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
float metalic = texture(specgloss, f_coord).b;
metalic = texture(specgloss, f_coord).b;
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);

View File

@@ -68,7 +68,7 @@ void main()
float reflectivity = reflblend > 1.0 ? param[1].z * 1.0 : param[1].z * reflblend;
float specularity = specgloss_map.r;
glossiness = specgloss_map.g * abs(param[1].w);
float metalic = specgloss_map.b;
metalic = specgloss_map.b;
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);

View File

@@ -73,8 +73,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 = specgloss_map.r;
float glossiness = specgloss_map.g * abs(param[1].w);
float metalic = specgloss_map.b;
glossiness = specgloss_map.g * abs(param[1].w);
metalic = specgloss_map.b;
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);

View File

@@ -53,7 +53,7 @@ void main()
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
float specularity = texture(specgloss, f_coord).r;
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
float metalic = texture(specgloss, f_coord).b;
metalic = texture(specgloss, f_coord).b;
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);

View File

@@ -61,7 +61,7 @@ void main()
float reflectivity = param[1].z * texture(normalmap, f_coord_p).a;
float specularity = texture(specgloss, f_coord_p).r;
glossiness = texture(specgloss, f_coord_p).g * abs(param[1].w);
float metalic = texture(specgloss, f_coord_p).b;
metalic = texture(specgloss, f_coord_p).b;
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);

View File

@@ -47,7 +47,7 @@ void main()
float reflectivity = param[1].z * texture(reflmap, f_coord).a;
float specularity = texture(specgloss, f_coord).r;
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
float metalic = texture(specgloss, f_coord).b;
metalic = texture(specgloss, f_coord).b;
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);

View File

@@ -53,7 +53,7 @@ void main()
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
float specularity = texture(specgloss, f_coord).r;
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
float metalic = texture(specgloss, f_coord).b;
metalic = texture(specgloss, f_coord).b;
fragcolor = apply_lights(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, 1.0);
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);

View File

@@ -34,7 +34,10 @@ vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, fl
{
vec3 basecolor = param[0].rgb;
fragcolor *= basecolor;
// 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 *= basecolor * 0.80;
vec3 emissioncolor = basecolor * emission;
vec3 envcolor = envmap_color(fragnormal);
@@ -54,7 +57,10 @@ vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, fl
vec2 sunlight = calc_dir_light(lights[0], fragnormal);
float diffuseamount = (sunlight.x * param[1].x) * lights[0].intensity;
// 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 * param[1].x) * lights[0].intensity;
fragcolor += envcolor * reflectivity;
float specularamount = (sunlight.y * param[1].y * specularity) * lights[0].intensity;
glossiness = abs(param[1].w);

View File

@@ -36,59 +36,67 @@ uniform sampler2D specgloss;
vec3 apply_lights_sunless(vec3 fragcolor, vec3 fragnormal, vec3 texturecolor, float reflectivity, float specularity, float shadowtone)
{
vec3 basecolor = param[0].rgb;
vec3 basecolor = param[0].rgb;
// Cab interior: dim ambient less than the exterior path - ambient
// is the dominant indoor illumination, but it was still too bright.
fragcolor *= basecolor * 0.80;
fragcolor *= basecolor;
vec3 emissioncolor = basecolor * emission;
vec3 emissioncolor = basecolor * emission;
vec3 envcolor = envmap_color(fragnormal);
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);
// 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));
const float MAX_REFLECTION_LOD = 8.0;
float env_roughness = 1.0 - clamp(glossiness / max(abs(param[1].w), 1.0), 0.0, 1.0);
vec3 envcolor = envmap_color_lod(fragnormal, env_roughness * MAX_REFLECTION_LOD);
vec3 envyuv = rgb2yuv(envcolor);
texturecolor = mix(texturecolor, texturecolorfullv, envyuv.r * reflectivity);
vec3 texturecoloryuv = rgb2yuv(texturecolor);
vec3 texturecolorfullv = yuv2rgb(vec3(0.2176, texturecoloryuv.gb));
vec3 envyuv = rgb2yuv(envcolor);
texturecolor = mix(texturecolor, texturecolorfullv, envyuv.r * reflectivity * fresnel.r);
if(lights_count == 0U)
return (fragcolor + emissioncolor + envcolor * reflectivity) * texturecolor;
if (lights_count == 0U)
return (fragcolor + emissioncolor) * texturecolor
+ envcolor * fresnel * reflectivity;
vec2 sunlight = calc_dir_light(lights[0], fragnormal);
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 * param[1].x * lights[0].intensity;
float diffuseamount = (sunlight.x * param[1].x) * lights[0].intensity;
fragcolor += envcolor * reflectivity;
float specularamount = (sunlight.y * param[1].y * specularity) * lights[0].intensity;
float specularamount = sunlight.y * param[1].y * specularity * lights[0].intensity;
for (uint i = 1U; i < lights_count; i++)
{
light_s light = lights[i];
vec2 part = vec2(0.0);
for (uint i = 1U; i < lights_count; i++)
{
light_s light = lights[i];
vec2 part = calc_headlights(light, fragnormal);
fragcolor += light.color * (part.x * param[1].x + part.y * param[1].y) * light.intensity;
}
// if (light.type == LIGHT_SPOT)
// part = calc_spot_light(light, fragnormal);
// else if (light.type == LIGHT_POINT)
// part = calc_point_light(light, fragnormal);
// else if (light.type == LIGHT_DIR)
// part = calc_dir_light(light, fragnormal);
// else if (light.type == LIGHT_HEADLIGHTS)
part = calc_headlights(light, fragnormal);
if (shadowtone < 1.0)
{
float shadow = calc_shadow();
specularamount *= clamp(1.0 - shadow, 0.0, 1.0);
fragcolor = mix(fragcolor, fragcolor * shadowtone, clamp(diffuseamount * shadow, 0.0, 1.0));
}
fragcolor += light.color * (part.x * param[1].x + part.y * param[1].y) * light.intensity;
}
fragcolor += emissioncolor;
vec3 specularcolor = specularamount * lights[0].color;
if (shadowtone < 1.0)
{
float shadow = calc_shadow();
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;
// Env reflection separate from albedo multiply — same fix as apply_lights
vec3 env_reflection = envcolor * fresnel * reflectivity;
vec3 result = mix(
(fragcolor + specularcolor) * texturecolor,
fragcolor * texturecolor + specularcolor,
metalic);
result += mix(env_reflection, env_reflection * texturecolor, metalic);
return result;
}
void main()
@@ -110,7 +118,7 @@ void main()
float reflectivity = param[1].z * texture(normalmap, f_coord).a;
float specularity = texture(specgloss, f_coord).r;
glossiness = texture(specgloss, f_coord).g * abs(param[1].w);
float metalic = texture(specgloss, f_coord).b;
metalic = texture(specgloss, f_coord).b;
fragcolor = apply_lights_sunless(fragcolor, fragnormal, tex_color.rgb, reflectivity, specularity, shadow_tone);
vec4 color = vec4(apply_fog(fragcolor), tex_color.a * alpha_mult);

72
shaders/postfx_ssao.frag Normal file
View File

@@ -0,0 +1,72 @@
in vec2 f_coords;
layout(location = 0) out vec4 out_color;
#texture (depth_tex, 0, R)
uniform sampler2D depth_tex;
#texture (noise_tex, 1, RGB)
uniform sampler2D noise_tex;
#include <common>
vec3 view_pos_from_depth(vec2 uv, float depth) {
vec4 clip = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
vec4 view = inverse(projection) * clip;
return view.xyz / view.w;
}
// Deterministic hemisphere sample (z > 0), mimics the C++ kernel generator
vec3 kernel_sample(int i) {
float fi = float(i);
vec3 dir = normalize(vec3(
fract(sin(fi * 12.9898) * 43758.5453) * 2.0 - 1.0,
fract(sin(fi * 78.2330) * 43758.5453) * 2.0 - 1.0,
fract(sin(fi * 37.7190) * 43758.5453) // z >= 0 -> hemisphere
));
float len = fract(sin(fi * 94.6720) * 43758.5453);
float scale = fi / 32.0;
scale = mix(0.1, 1.0, scale * scale); // bias samples inward
return dir * len * scale;
}
void main() {
float d = texture(depth_tex, f_coords).r;
if (d >= 1.0) { out_color = vec4(1.0); return; } // skybox -> no occlusion
vec3 pos = view_pos_from_depth(f_coords, d);
vec3 ddx = dFdx(pos);
vec3 ddy = dFdy(pos);
vec3 n = normalize(cross(ddx, ddy));
// derive screen size from the depth texture instead of a UBO field
vec2 screen_size = vec2(textureSize(depth_tex, 0));
vec2 noise_uv = f_coords * (screen_size / 4.0);
vec3 rvec = texture(noise_tex, noise_uv).xyz;
vec3 t = normalize(rvec - n * dot(rvec, n));
vec3 b = cross(n, t);
mat3 TBN = mat3(t, b, n);
const int KERNEL = 32;
const float RADIUS = 0.5;
const float BIAS = 0.025;
float occ = 0.0;
for (int i = 0; i < KERNEL; ++i) {
vec3 sp = TBN * kernel_sample(i);
sp = pos + sp * RADIUS;
vec4 clip = projection * vec4(sp, 1.0);
vec3 ndc = clip.xyz / clip.w;
vec2 suv = ndc.xy * 0.5 + 0.5;
float sd = texture(depth_tex, suv).r;
float szv = view_pos_from_depth(suv, sd).z;
float range = smoothstep(0.0, 1.0, RADIUS / abs(pos.z - szv));
occ += (szv >= sp.z + BIAS ? 1.0 : 0.0) * range;
}
out_color = vec4(1.0 - occ / float(KERNEL));
}

View File

@@ -0,0 +1,22 @@
in vec2 f_coords;
layout(location = 0) out vec4 out_color;
#texture (color_tex, 0, RGB)
uniform sampler2D color_tex;
#texture (ssao_tex, 1, R)
uniform sampler2D ssao_tex;
const float SSAO_STRENGTH = 0.8;
const float SSAO_MIN = 0.3; // floor on darkening so shadows don't go pitch black
void main() {
vec3 c = texture(color_tex, f_coords).rgb;
float occ = texture(ssao_tex, f_coords).r;
// remap occlusion with strength and a minimum floor
occ = pow(clamp(occ, 0.0, 1.0), SSAO_STRENGTH);
occ = mix(SSAO_MIN, 1.0, occ);
out_color = vec4(c * occ, 1.0);
}

View File

@@ -0,0 +1,14 @@
in vec2 f_coords;
layout(location = 0) out vec4 out_color;
#texture (ssao_tex, 0, R)
uniform sampler2D ssao_tex;
void main() {
vec2 texel = 1.0 / vec2(textureSize(ssao_tex, 0));
float sum = 0.0;
for (int x = -2; x < 2; ++x)
for (int y = -2; y < 2; ++y)
sum += texture(ssao_tex, f_coords + vec2(x, y) * texel).r;
out_color = vec4(sum / 16.0);
}

View File

@@ -42,6 +42,10 @@ vec3 filmic(vec3 x)
vec4 tonemap(vec4 x)
{
// return FBOUT(vec4(ACESFilm(x.rgb), x.a));
return FBOUT(vec4(reinhard(x.rgb), x.a));
// Use ACES Filmic by default. Reinhard above kept for reference, but
// with pureWhite=1.0 it collapses to identity (L*(1+L)/(1+L) = L) and
// just clips HDR>1.0 at the framebuffer -> washed-out / burnt look.
// ACES gives a smooth highlight shoulder + slight toe contrast.
return FBOUT(vec4(ACESFilm(x.rgb), x.a));
//return FBOUT(vec4(reinhard(x.rgb), x.a));
}

View File

@@ -294,6 +294,7 @@ struct global_settings {
std::string exec_on_exit;
std::string prepend_scn;
bool gfx_postfx_ssao_enabled {false};
struct extraviewport_config {
std::string monitor;

View File

@@ -450,6 +450,7 @@ TTrain::commandhandler_map const TTrain::m_commandhandlers = {
{ user_command::radiostopenable, &TTrain::OnCommand_radiostopenable },
{ user_command::radiostopdisable, &TTrain::OnCommand_radiostopdisable },
{ user_command::radiostoptest, &TTrain::OnCommand_radiostoptest },
{ user_command::radiocall1send, &TTrain::OnCommand_radiocall1send },
{ user_command::radiocall3send, &TTrain::OnCommand_radiocall3send },
{ user_command::radiovolumeincrease, &TTrain::OnCommand_radiovolumeincrease },
{ user_command::radiovolumedecrease, &TTrain::OnCommand_radiovolumedecrease },
@@ -6917,6 +6918,23 @@ void TTrain::OnCommand_radiostoptest( TTrain *Train, command_data const &Command
}
}
void TTrain::OnCommand_radiocall1send( TTrain *Train, command_data const &Command )
{
if( Command.action == GLFW_PRESS ) {
if( ( Train->RadioChannel() != 10 )
&& ( true == Train->mvOccupied->Radio )
&& ( Train->mvOccupied->Power24vIsAvailable || Train->mvOccupied->Power110vIsAvailable) ) {
simulation::Events.queue_receivers( radio_message::call1, Train->Dynamic()->GetPosition() );
}
// visual feedback
Train->ggRadioCall1.UpdateValue( 1.0 );
}
else if( Command.action == GLFW_RELEASE ) {
// visual feedback
Train->ggRadioCall1.UpdateValue( 0.0 );
}
}
void TTrain::OnCommand_radiocall3send( TTrain *Train, command_data const &Command ) {
if( Command.action == GLFW_PRESS ) {
@@ -8343,6 +8361,7 @@ bool TTrain::Update( double const Deltatime )
ggRadioChannelNext.Update();
ggRadioStop.Update();
ggRadioTest.Update();
ggRadioCall1.Update();
ggRadioCall3.Update();
ggRadioVolumeSelector.Update();
ggRadioVolumePrevious.Update();
@@ -9861,6 +9880,7 @@ void TTrain::clear_cab_controls()
ggRadioChannelNext.Clear();
ggRadioStop.Clear();
ggRadioTest.Clear();
ggRadioCall1.Clear();
ggRadioCall3.Clear();
ggRadioVolumeSelector.Clear();
ggRadioVolumePrevious.Clear();
@@ -10668,6 +10688,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con
{ "radiochannelnext_sw:", ggRadioChannelNext },
{ "radiostop_sw:", ggRadioStop },
{ "radiotest_sw:", ggRadioTest },
{ "radiocall1_sw:", ggRadioCall1 },
{ "radiocall3_sw:", ggRadioCall3 },
{ "radiovolume_sw:", ggRadioVolumeSelector },
{ "radiovolumeprev_sw:", ggRadioVolumePrevious },

View File

@@ -475,6 +475,7 @@ class TTrain {
static void OnCommand_radiostopenable( TTrain *Train, command_data const &Command );
static void OnCommand_radiostopdisable( TTrain *Train, command_data const &Command );
static void OnCommand_radiostoptest( TTrain *Train, command_data const &Command );
static void OnCommand_radiocall1send( TTrain *Train, command_data const &Command );
static void OnCommand_radiocall3send( TTrain *Train, command_data const &Command );
static void OnCommand_radiovolumeincrease(TTrain *Train, command_data const &Command);
static void OnCommand_radiovolumedecrease(TTrain *Train, command_data const &Command);
@@ -580,6 +581,7 @@ public: // reszta może by?publiczna
TGauge ggRadioChannelNext;
TGauge ggRadioTest;
TGauge ggRadioStop;
TGauge ggRadioCall1;
TGauge ggRadioCall3;
TGauge ggRadioVolumeSelector;
TGauge ggRadioVolumePrevious;

View File

@@ -687,9 +687,12 @@ std::unordered_map<std::string, vr_openvr::button_bindings> vr_openvr::m_buttonb
{ "radiotest_sw:", {
user_command::radiostoptest,
user_command::none } },
{ "radiocall3_sw:", {
user_command::radiocall3send,
user_command::none } },
{ "radiocall1_sw:", {
user_command::radiocall1send,
user_command::none } },
{ "radiocall3_sw:", {
user_command::radiocall3send,
user_command::none } },
{ "radiovolume_sw:",{
user_command::radiovolumeincrease,
user_command::radiovolumedecrease } },