121 lines
4.3 KiB
GLSL
121 lines
4.3 KiB
GLSL
/*
|
|
* MIT License
|
|
* Author: Mark Allyn
|
|
*
|
|
* sweep.frag — phosphor accumulation update shader.
|
|
*
|
|
* The FBO is GL_RG32F (two independent energy channels):
|
|
* R — signal energy: target echoes + sweep-background glow.
|
|
* Multiplied by u_gain in the display pass so operators can
|
|
* adjust received-signal brightness without touching rings.
|
|
* G — range-ring energy: written at u_ringBrightness; NOT scaled
|
|
* by gain. Rings are a precision timing reference, not a
|
|
* received echo. Both channels decay at the same P7 rate.
|
|
*
|
|
* The sweep background (u_sweepBg) goes into the G channel so the
|
|
* rotating beam is always visible regardless of the gain setting.
|
|
*
|
|
* PPI convention: north = +y, east = +x; bearing = atan2(x, y)
|
|
* in degrees, clockwise from north.
|
|
*/
|
|
#version 330 core
|
|
|
|
in vec2 vTexCoord;
|
|
|
|
layout(location = 0) out vec4 fragOut; // .r = signal; .g = ring+sweep; .ba unused
|
|
|
|
uniform sampler2D u_prevPhosphor; // previous frame's energy texture (GL_RG32F)
|
|
uniform float u_decayFactor; // exp(-decay_rate * dt)
|
|
uniform float u_beamAngle; // current beam angle, degrees CW from north
|
|
uniform float u_beamAnglePrev; // beam angle at previous frame
|
|
uniform float u_sweepBg; // ambient sweep-line energy (gain-independent)
|
|
uniform float u_halfBeamDeg; // half-beamwidth for target blobs (display widening)
|
|
|
|
// Targets: .x = range_norm (0-1), .y = bearing_deg, .z = brightness, .w = radial_size_norm
|
|
uniform vec4 u_targets[32];
|
|
uniform float u_targetAngHalfDeg[32]; // per-target azimuthal half-width (degrees)
|
|
uniform int u_targetCount;
|
|
|
|
// Range rings: up to 4 normalised radii
|
|
uniform float u_ringRadii[4];
|
|
uniform int u_ringCount;
|
|
uniform float u_ringWidth; // half-width in normalised range units
|
|
uniform float u_ringBrightness;
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
// Smallest unsigned angular distance between two bearings [0,360)
|
|
float angleDiff(float a, float b) {
|
|
float d = mod(abs(a - b), 360.0);
|
|
return (d > 180.0) ? (360.0 - d) : d;
|
|
}
|
|
|
|
// True if bearing b is inside the arc [prev, curr] swept this frame.
|
|
// Handles the 0/360 wraparound when the sweep crosses north.
|
|
bool inSweep(float b, float prev, float curr) {
|
|
if (curr >= prev) {
|
|
return (b >= prev && b <= curr);
|
|
}
|
|
// Wraparound: arc crosses 360→0
|
|
return (b >= prev || b <= curr);
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
void main() {
|
|
vec2 pos = vTexCoord * 2.0 - 1.0; // PPI coords: (-1,-1) SW … (+1,+1) NE
|
|
float rng = length(pos);
|
|
|
|
if (rng > 1.0) {
|
|
fragOut = vec4(0.0);
|
|
return;
|
|
}
|
|
|
|
// Bearing: clockwise from north — atan2(east, north) = atan2(x, y)
|
|
float brg = degrees(atan(pos.x, pos.y));
|
|
if (brg < 0.0) brg += 360.0;
|
|
|
|
vec2 prev = texture(u_prevPhosphor, vTexCoord).rg;
|
|
float signal = prev.r * u_decayFactor;
|
|
float ring = prev.g * u_decayFactor;
|
|
|
|
if (inSweep(brg, u_beamAnglePrev, u_beamAngle)) {
|
|
|
|
// ---- Range rings → G channel (gain-independent) ----
|
|
float ringContrib = u_sweepBg; // sweep-background glow also in G channel
|
|
for (int i = 0; i < u_ringCount; i++) {
|
|
float d = abs(rng - u_ringRadii[i]);
|
|
if (d < u_ringWidth) {
|
|
float w = 1.0 - d / u_ringWidth;
|
|
ringContrib = max(ringContrib, u_ringBrightness * w * w);
|
|
}
|
|
}
|
|
ring = max(ring, ringContrib);
|
|
|
|
// ---- Target echoes → R channel (gain-scaled in display pass) ----
|
|
float sigContrib = 0.0;
|
|
for (int i = 0; i < u_targetCount; i++) {
|
|
float tRng = u_targets[i].x;
|
|
float tBrg = u_targets[i].y;
|
|
float tBrt = u_targets[i].z;
|
|
float tSize = u_targets[i].w;
|
|
|
|
if (tRng <= 0.0 || tBrt <= 0.0) continue;
|
|
|
|
float tAngHalf = u_targetAngHalfDeg[i];
|
|
float dBrg = angleDiff(brg, tBrg);
|
|
if (dBrg >= tAngHalf) continue;
|
|
|
|
float dRng = abs(rng - tRng);
|
|
if (dRng >= tSize) continue;
|
|
|
|
float bw = 1.0 - dBrg / tAngHalf;
|
|
float rw = 1.0 - dRng / tSize;
|
|
sigContrib = max(sigContrib, tBrt * bw * rw);
|
|
}
|
|
signal = max(signal, sigContrib);
|
|
}
|
|
|
|
fragOut = vec4(clamp(signal, 0.0, 1.0), clamp(ring, 0.0, 1.0), 0.0, 1.0);
|
|
}
|