mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-15 05:59:06 +01:00
The sides of the trackbed (and grass, fences, foliage) are cut out of their textures by the alpha test, which keeps or drops whole pixels, so their edges stayed jagged at any multisampling level; only geometry edges got smoothed. With multisampling on, the alpha tested materials of the opaque colour pass now draw with GL_SAMPLE_ALPHA_TO_COVERAGE. Their shaders do the alpha test with alpha_test_fail() of glsl_common and write as alpha the coverage of the test edge: the alpha sharpened around the opacity threshold to go from 0 to 1 over about a pixel, so the edge keeps the place it had and gets the samples it covers. The renderer turns alpha to coverage on per material, only for shaders which use the helper and only in the opaque pass with a positive opacity, so translucent, water, terrain and the other shaders are unchanged. Without multisampling the shaders work as before.
145 lines
5.1 KiB
C++
145 lines
5.1 KiB
C++
#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;
|
|
};
|
|
|
|
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
|
|
float alpha_coverage(float alpha)
|
|
{
|
|
#if ALPHA_TO_COVERAGE_ENABLED
|
|
if (opacity > 0.0)
|
|
return clamp((alpha - opacity) / max(fwidth(alpha), 0.0001) + 0.5, 0.0, 1.0);
|
|
#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)
|
|
return coverage <= 0.0;
|
|
#endif
|
|
return (opacity >= 0.0 ? (alpha < opacity) : (alpha >= -opacity));
|
|
}
|
|
#endif
|
|
|
|
)STRING";
|
|
}
|