Files
updated-radar/shaders/phosphor.frag
2026-04-23 08:05:03 -07:00

85 lines
2.8 KiB
GLSL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* MIT License
* Author: Mark Allyn
*
* phosphor.frag — maps the single-channel phosphor energy texture to
* the P7 colour sequence (blue → green → yellow-green → dark) and
* applies a simple inline bloom (box-filter glow) to bright pixels.
*
* Coordinate system: gl_FragCoord.xy in GL viewport pixels (origin
* bottom-left). Scope centre is passed as u_scopeCenter in the same
* coordinate system.
*/
#version 330 core
out vec4 fragColor;
uniform sampler2D u_phosphor; // GL_R32F phosphor energy FBO
uniform vec2 u_scopeCenter; // scope centre in GL viewport pixels (bottom-left origin)
uniform float u_scopeRadius; // scope radius in pixels
uniform float u_gain; // receiver gain [0,1] — scales brightness
uniform float u_bloomStep; // UV step for bloom sample (≈ 2.5 / FBO_SIZE)
uniform float u_bloomStrength; // additive blend weight for bloom
// P7 energy thresholds (match settings.h)
const float T_BLUE = 0.82;
const float T_GREEN = 0.55;
const float T_YGREE = 0.22;
const float T_DARK = 0.03;
// P7 colour anchors
const vec3 C_BLUE = vec3(0.30, 0.70, 1.00);
const vec3 C_GREEN = vec3(0.05, 1.00, 0.30);
const vec3 C_YGREE = vec3(0.50, 1.00, 0.05);
const vec3 C_YELLW = vec3(0.70, 0.70, 0.00);
const vec3 C_BLACK = vec3(0.00, 0.00, 0.00);
vec3 p7Color(float e) {
if (e >= T_BLUE)
return mix(C_GREEN, C_BLUE, (e - T_GREEN) / (T_BLUE - T_GREEN));
if (e >= T_GREEN)
return mix(C_YGREE, C_GREEN, (e - T_YGREE) / (T_GREEN - T_YGREE));
if (e >= T_YGREE)
return mix(C_YELLW, C_YGREE, (e - T_DARK ) / (T_YGREE - T_DARK ));
if (e >= T_DARK)
return mix(C_BLACK, C_YELLW, e / T_DARK);
return C_BLACK;
}
void main() {
// Fragment position relative to scope centre
vec2 delta = (gl_FragCoord.xy - u_scopeCenter) / u_scopeRadius;
float dist = length(delta);
if (dist > 1.0) {
fragColor = vec4(0.0); // outside scope circle — transparent black
return;
}
// Map from PPI delta [-1,+1] to phosphor texture UV [0,1]
// delta.x = east, delta.y = north (both y directions already match)
vec2 uv = delta * 0.5 + 0.5;
float energy = texture(u_phosphor, uv).r * u_gain;
// Inline bloom: weighted box-filter over a 5×5 neighbourhood
float bloom = 0.0;
float wsum = 0.0;
for (int dx = -2; dx <= 2; dx++) {
for (int dy = -2; dy <= 2; dy++) {
float w = exp(-float(dx*dx + dy*dy) * 0.45);
float e = texture(u_phosphor, uv + vec2(dx, dy) * u_bloomStep).r;
bloom += e * w;
wsum += w;
}
}
bloom = (bloom / wsum) * u_bloomStrength;
float finalE = clamp(energy + bloom, 0.0, 1.0);
vec3 col = p7Color(finalE);
// Soft-edge vignette at the scope boundary
float edge = smoothstep(1.0, 0.97, dist);
fragColor = vec4(col * edge, 1.0);
}