#include "stdafx.h" #include "glsl_common.h" std::string gl::glsl_common; void gl::glsl_common_setup() { glsl_common = "#define SHADOWMAP_ENABLED " + std::to_string((int)Global.gfx_shadowmap_enabled) + "\n" + "#define ENVMAP_ENABLED " + std::to_string((int)Global.gfx_envmap_enabled) + "\n" + "#define MOTIONBLUR_ENABLED " + std::to_string((int)Global.gfx_postfx_motionblur_enabled) + "\n" + "#define POSTFX_ENABLED " + std::to_string((int)!Global.gfx_skippipeline) + "\n" + "#define EXTRAEFFECTS_ENABLED " + std::to_string((int)Global.gfx_extraeffects) + "\n" + "#define ALPHA_TO_COVERAGE_ENABLED " + std::to_string((int)(Global.iMultisampling > 0)) + "\n" + "#define MAX_LIGHTS " + std::to_string(MAX_LIGHTS) + "U\n" + "#define MAX_CASCADES " + std::to_string(MAX_CASCADES) + "U\n" + "#define MAX_PARAMS " + std::to_string(MAX_PARAMS) + "U\n" + "#define MAX_INSTANCES_PER_BATCH " + std::to_string(MAX_INSTANCES_PER_BATCH) + "U\n" + R"STRING( const uint LIGHT_SPOT = 0U; const uint LIGHT_POINT = 1U; const uint LIGHT_DIR = 2U; const uint LIGHT_HEADLIGHTS = 3U; struct light_s { vec3 pos; uint type; vec3 dir; float in_cutoff; vec3 color; float out_cutoff; float linear; float quadratic; float intensity; float ambient; mat4 headlight_projection; vec4 headlight_weights; }; layout(std140) uniform light_ubo { vec3 ambient; vec3 fog_color; uint lights_count; light_s lights[MAX_LIGHTS]; }; layout (std140) uniform model_ubo { mat4 modelview; mat3 modelviewnormal; vec4 param[MAX_PARAMS]; mat4 future; float opacity; float emission; float fog_density; float alpha_mult; float shadow_tone; float alpha_to_coverage; }; layout (std140) uniform scene_ubo { mat4 projection; mat4 inv_view; mat4 lightview[MAX_CASCADES]; vec3 cascade_end; float time; vec4 rain_params; vec4 wiper_pos; vec4 wiper_timer_out; vec4 wiper_timer_return; }; // Per-instance modelview matrices for GPU-instanced draws. // The UBO itself is declared in every stage so the binding stays valid, // but the helper functions reference gl_InstanceID which is vertex-only, // so they are guarded behind STAGE_VERTEX. layout (std140) uniform instance_ubo { mat4 instance_modelview[MAX_INSTANCES_PER_BATCH]; }; #ifdef STAGE_VERTEX // For non-instanced draws gl_InstanceID == 0 and slot 0 is permanently set // to identity, so effective_modelview() reduces to model_ubo.modelview. // For instanced draws the C++ side uploads camera-space root transforms for // instances 0..N-1 and sets model_ubo.modelview to the submodel-local chain // (computed from identity, NOT from the camera/instance), so the product // gives the correct final transform per instance per submodel. mat4 effective_modelview() { return instance_modelview[gl_InstanceID] * modelview; } mat3 effective_modelviewnormal() { // instance_modelview can include per-instance scale (uniform or per-axis), // so its upper-3x3 is NOT its own inverse-transpose in the non-uniform // case. Compute the correct normal matrix via transpose(inverse(...)). // For pure rotation+translation this still produces the rotation matrix // (one extra mat3 inverse per vertex — modern GPUs handle it cheaply). mat3 instance_mv3 = mat3(instance_modelview[gl_InstanceID]); mat3 instance_normal = transpose(inverse(instance_mv3)); return instance_normal * modelviewnormal; } #endif #ifdef STAGE_FRAGMENT // alpha tested surfaces (positive opacity, the opaque pass) get the coverage of their alpha // test edge from GL_SAMPLE_ALPHA_TO_COVERAGE, which the renderer turns on for them with // multisampling; otherwise the alpha test keeps or drops whole pixels and its edges stay // jagged whatever the multisampling. the alpha is sharpened to go from 0 to 1 over about // a pixel around the test threshold, so the edge keeps the place and sharpness the alpha // test gave it. computed before any discard, which leaves the derivatives undefined. // alpha_to_coverage of model_ubo tells whether alpha to coverage is on for this draw; where // it isn't (reflections, previews) the shaders keep the plain alpha test float alpha_coverage(float alpha) { #if ALPHA_TO_COVERAGE_ENABLED if (opacity > 0.0 && alpha_to_coverage > 0.0) { // a flat alpha has no edge in the pixel and gets the plain test, so an alpha equal // to the threshold stays fully covered float fw = fwidth(alpha); return (fw > 0.0 ? clamp((alpha - opacity) / fw + 0.5, 0.0, 1.0) : step(opacity, alpha)); } #endif return alpha; } // true for the fragments the alpha test drops: below the opacity threshold in the opaque // pass, at or above it (negative opacity) in the translucent pass. with alpha to coverage // only the fragments which cover no sample at all bool alpha_test_fail(float alpha, float coverage) { #if ALPHA_TO_COVERAGE_ENABLED if (opacity > 0.0 && alpha_to_coverage > 0.0) return coverage <= 0.0; #endif return (opacity >= 0.0 ? (alpha < opacity) : (alpha >= -opacity)); } #endif )STRING"; }