Needle-card crowns are many cut-out quads deep and a discarding shader turns early depth rejection off, so every card behind the front one ran the full lighting shader. The near tree LODs now write depth first (depth.frag, the same alpha coverage test), terrain under them is rejected before shading, and the lit pass draws them with no discard and an equal depth test (invariant gl_Position). R3D_NOPREPASS=1 restores the old path. Dense forest on a Windows PC: 61 -> 78 fps at 3840x2160, 116 -> 164 fps at 1920x1080. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
23 lines
1.1 KiB
GLSL
23 lines
1.1 KiB
GLSL
// depth.frag — the foliage depth prepass (scatter_draw_depth, render.ludic).
|
|
//
|
|
// A crown of needle cards is many cut-out quads deep from any angle. Lit directly, every
|
|
// card behind the front one runs the whole lighting shader and is thrown away afterwards,
|
|
// because a shader that can discard switches early depth rejection off. So the near tree
|
|
// LODs first write depth with this: the same alpha coverage test the lit shader applies,
|
|
// and nothing else. The lit pass then draws them with no discard and an equal depth test
|
|
// (model.frag's EQ_PASS), and each pixel of foliage is lit once.
|
|
//
|
|
// The test must match model.frag's ALPHA_TEST exactly - a texel this pass keeps and that
|
|
// one would have cut is drawn solid - so it is the same formula, taken at coverage 0.5
|
|
// because without MSAA a fragment is either there or not.
|
|
in vec3 v_wpos;
|
|
in vec2 v_uv;
|
|
uniform sampler2D u_diff;
|
|
void main() {
|
|
if (v_wpos.y < u_clip_y) discard;
|
|
vec4 d = texture(u_diff, v_uv);
|
|
float lod = textureQueryLod(u_diff, v_uv).x;
|
|
float a = min(d.a * (1.0 + 0.45 * max(lod, 0.0)), 1.0);
|
|
float cov = (a - 0.4) / max(fwidth(a), 1e-4) + 0.5;
|
|
if (cov < 0.5) discard;
|
|
}
|