#version 450 core #include "common.glsl" #include "pixelart.glsl" in VS { vec2 uv; vec3 worldPos; vec3 pivot; vec2 local; float tint; flat vec2 fx; // alpha, flash } v; layout(binding = 0) uniform sampler2D uTex; uniform int uPass; uniform float uEmissive; layout(location = 0) out vec4 fragColor; const float kFlashWhite = 4.0; // linear HDR white the flash blends toward (blooms) // 4x4 ordered (Bayer) threshold in (0, 1): alpha 1 keeps every pixel, 0 none. float bayer4(vec2 p) { ivec2 i = ivec2(p) & 3; const float m[16] = float[](0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5); return (m[i.y * 4 + i.x] + 0.5) / 16.0; } void main() { vec4 c = sharpSample(uTex, v.uv); if (c.a < 0.5) discard; if (v.fx.x < 1.0 && v.fx.x < bayer4(gl_FragCoord.xy)) discard; // dithered fade, shadows included if (uPass == 1) return; vec3 up = vec3(0.0, 1.0, 0.0); vec3 toCam = cross(uBillboardRight.xyz, up); // Sprites are flat cards; a normal leaning upward keeps them readable // under top lighting while still responding to light direction. vec3 n = normalize(toCam * 0.7 + up * 0.7); vec3 L = uSunDir.xyz; float ndl = clamp((dot(n, L) + 0.55) / 1.55, 0.0, 1.0); // Receive shadows at the matching point on this sprite's own sun-facing // shadow card, nudged toward the sun: no self-shadowing, correct // occlusion by everything else. vec3 sp = v.pivot + uShadowRight.xyz * v.local.x + vec3(0.0, v.local.y, 0.0) + L * 0.22; float vis = sunShadow(sp) * canopyCookie(v.worldPos); vec3 albedo = c.rgb * v.tint * mix(1.0, 0.8, uWeather.y); // rain-soaked sprites read darker float gao = groundAO(v.pivot, v.local.y); vec3 light = uSunColor.rgb * uSunDir.w * ndl * vis * mix(1.0, gao, 0.7) + hemiAmbient(n) * gao + pointLights(v.worldPos, n); // Thin-foliage translucency when the sun is behind the sprite. float behind = max(dot(-toCam, L), 0.0); light += uSunColor.rgb * uSunDir.w * behind * behind * vis * 0.25; vec3 col = albedo * light + albedo * uEmissive; if (uBlob.w > 0.0) { // fold the player's contact shadow onto grass sprites too float d = length(v.pivot.xz - uBlob.xz) / uBlob.w; col *= 1.0 - 0.35 * (1.0 - smoothstep(0.3, 1.0, d)) * (1.0 - clamp(v.local.y / 0.6, 0.0, 1.0)); } col = mix(col, vec3(kFlashWhite), clamp(v.fx.y, 0.0, 1.0)); // hit / capture flash fragColor = vec4(col, 1.0); }