From 0e5a5c527e7d201142297bb04f7e3faa1f0b757e Mon Sep 17 00:00:00 2001 From: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Date: Sun, 9 Aug 2026 05:38:01 -0400 Subject: [PATCH] Amish Bubble Butter --- selfdrive/ui/onroad/starpilot/pip_sidecam.py | 50 +++++++++++++++----- selfdrive/ui/tests/test_pip_sidecam.py | 21 ++++++++ 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/selfdrive/ui/onroad/starpilot/pip_sidecam.py b/selfdrive/ui/onroad/starpilot/pip_sidecam.py index 31fbb6af9..23441a26c 100644 --- a/selfdrive/ui/onroad/starpilot/pip_sidecam.py +++ b/selfdrive/ui/onroad/starpilot/pip_sidecam.py @@ -43,8 +43,32 @@ uniform vec2 uCropMin; uniform vec2 uCropSize; uniform int uFlipX; out vec4 fragColor; + +const float BUBBLE_REFRACTION = 0.016; +const float BUBBLE_EDGE_DARKEN = 0.15; +const float BUBBLE_HIGHLIGHT = 0.12; +const float BUBBLE_RIM = 0.10; +const float BUBBLE_EDGE_TRANSPARENCY = 0.12; + void main() { - vec2 cropCoord = fragTexCoord; + // Calculate the mask before sampling: fragments outside the bubble do no texture work. + vec2 p = fragTexCoord * 2.0 - 1.0; + float radius = length(p); + float aa = max(fwidth(radius), 0.00001); + float alpha = 1.0 - smoothstep(1.0 - aa, 1.0 + aa, radius); + if (radius > 1.0 + aa) { + discard; + } + + // A shallow hemisphere gives the image a convex bubble surface without a mesh or pass. + float z = sqrt(max(0.0, 1.0 - dot(p, p))); + vec2 sampleCoord = clamp( + fragTexCoord + p * BUBBLE_REFRACTION * (1.0 - z), + 0.001, + 0.999 + ); + + vec2 cropCoord = sampleCoord; if (uFlipX == 1) { cropCoord.x = 1.0 - cropCoord.x; } @@ -53,12 +77,19 @@ void main() { vec2 c = texture(texture1, uv).ra - 0.5; vec3 rgb = vec3(y + 1.402 * c.y, y - 0.344 * c.x - 0.714 * c.y, y + 1.772 * c.x); - vec2 p = fragTexCoord - 0.5; - float dist = length(p); - float edge = 0.02; - float alpha = 1.0 - smoothstep(0.5 - edge, 0.5, dist); + // Keep the interior gradient and use cheap analytic lighting instead of specular math. + float edgeShade = smoothstep(0.22, 0.98, radius); + rgb *= mix(1.0, 1.0 - BUBBLE_EDGE_DARKEN, edgeShade); - fragColor = vec4(rgb, alpha); + vec2 highlightOffset = p - vec2(-0.28, -0.34); + float highlight = 1.0 - smoothstep(0.0, 0.22, dot(highlightOffset, highlightOffset)); + rgb += vec3(1.0) * highlight * z * BUBBLE_HIGHLIGHT; + + // Let the rim blend into the camera image and the UI underneath it. + float rim = smoothstep(0.60, 0.99, radius); + rgb = mix(rgb, vec3(0.48, 0.70, 1.0), rim * BUBBLE_RIM); + float surfaceAlpha = alpha * (1.0 - rim * BUBBLE_EDGE_TRANSPARENCY); + fragColor = vec4(rgb, surfaceAlpha); } """ @@ -253,13 +284,6 @@ class PipSideCamera: self._draw_bubble(bubble, crop) def _draw_bubble(self, bubble: rl.Rectangle, crop: rl.Rectangle): - cx = bubble.x + bubble.width / 2 - cy = bubble.y + bubble.height / 2 - radius = bubble.width / 2 - - rl.draw_circle(int(round(cx)), int(round(cy)), radius, rl.BLACK) - rl.draw_circle_lines(int(round(cx)), int(round(cy)), radius, rl.Color(255, 255, 255, 120)) - tex_w = float(self.texture_y.width) tex_h = float(self.texture_y.height) crop_min = rl.Vector2(crop.x / tex_w, crop.y / tex_h) diff --git a/selfdrive/ui/tests/test_pip_sidecam.py b/selfdrive/ui/tests/test_pip_sidecam.py index 900bf59a8..9a86dbf36 100644 --- a/selfdrive/ui/tests/test_pip_sidecam.py +++ b/selfdrive/ui/tests/test_pip_sidecam.py @@ -26,3 +26,24 @@ def test_pip_maps_raw_driver_image_sides_to_vehicle_sides(): def test_pip_driver_camera_shader_mirrors_the_crop(): assert "uniform int uFlipX" in PIP_FRAGMENT_SHADER assert "cropCoord.x = 1.0 - cropCoord.x" in PIP_FRAGMENT_SHADER + + +def test_pip_driver_camera_shader_masks_before_sampling_and_keeps_two_texture_reads(): + assert "fwidth(radius)" in PIP_FRAGMENT_SHADER + assert "if (radius > 1.0 + aa)" in PIP_FRAGMENT_SHADER + assert PIP_FRAGMENT_SHADER.index("if (radius > 1.0 + aa)") < PIP_FRAGMENT_SHADER.index("texture(texture0") + assert PIP_FRAGMENT_SHADER.count("texture(texture0") == 1 + assert PIP_FRAGMENT_SHADER.count("texture(texture1") == 1 + + +def test_pip_driver_camera_shader_uses_analytic_bubble_shading_without_new_uniforms(): + assert "float z = sqrt(max(0.0, 1.0 - dot(p, p)))" in PIP_FRAGMENT_SHADER + assert "BUBBLE_REFRACTION" in PIP_FRAGMENT_SHADER + assert "BUBBLE_EDGE_DARKEN" in PIP_FRAGMENT_SHADER + assert "BUBBLE_HIGHLIGHT" in PIP_FRAGMENT_SHADER + assert "BUBBLE_RIM" in PIP_FRAGMENT_SHADER + assert "BUBBLE_EDGE_TRANSPARENCY" in PIP_FRAGMENT_SHADER + assert "BUBBLE_BORDER_WIDTH" not in PIP_FRAGMENT_SHADER + assert "rgb = mix(rgb, vec3(1.0)" not in PIP_FRAGMENT_SHADER + assert "uniform sampler2D texture2" not in PIP_FRAGMENT_SHADER + assert "uRefraction" not in PIP_FRAGMENT_SHADER