#version 330 core in vec2 vTexCoord; in vec3 vWorldPos; uniform sampler2D texture0; // --- Soft bubble (kept) --- uniform vec3 u_playerPos; uniform float u_inner; uniform float u_outer; uniform float u_minShadow; // --- Flashlight cone + distance --- uniform vec3 u_spotDir; // normalized camera forward uniform float u_spotCos; // cos(half-angle) uniform float u_spotSoft; // base penumbra width in cos-space uniform float u_lightRange; // bright zone distance (>0) uniform float u_lightFade; // fade width beyond range // --- NEW: decouple brightness + widen with distance --- uniform float u_intensity; // flashlight peak brightness (independent of range) uniform float u_beamSpread; // additional penumbra growth per unit distance (0..~0.5) // Alpha cut (trees, grass) uniform float u_alphaCutoff; out vec4 finalColor; // Helpers float remap01(float x, float a, float b) { return clamp((x - a) / max(b - a, 1e-5), 0.0, 1.0); } float invsq(float d, float r) { float s = d / max(r, 1e-4); return 1.0 / (1.0 + s*s); } void main() { vec4 texel = texture(texture0, vTexCoord); if (texel.a < u_alphaCutoff) discard; float dist = length(vWorldPos - u_playerPos); // (A) Soft ambient bubble around the player (unchanged) float bubble = 1.0 - remap01(dist, u_inner, u_outer); // 1 near, 0 far float ambient = mix(1.0 - clamp(u_minShadow,0.0,1.0), 0.0, 1.0 - bubble); // (B) Flashlight cone with penumbra + distance drop + widening vec3 L = normalize(vWorldPos - u_playerPos); float cosTheta = dot(normalize(u_spotDir), L); // Widen penumbra with distance so long ranges don't look like a laser float distNorm = dist / max(u_lightRange, 1e-4); float softWide = clamp(u_spotSoft + u_beamSpread * distNorm, 0.0, 0.8); float innerEdge = u_spotCos; float outerEdge = clamp(u_spotCos - softWide, -1.0, 1.0); float spotA = smoothstep(outerEdge, innerEdge, cosTheta); spotA *= spotA; // mild hotspot // Distance attenuation: inverse-square * soft fade band float band = 1.0 - remap01(dist, u_lightRange, u_lightRange + u_lightFade); float lightD = invsq(dist, u_lightRange) * band; // Decoupled brightness float flashlight = u_intensity * spotA * lightD; // Compose float light = clamp(ambient + flashlight, 0.0, 1.0); finalColor = vec4(texel.rgb * light, texel.a); }