Quick answer: Sample the ViewportTexture with vec2(uv.x, 1.0 - uv.y). For 2D UI rendering of a SubViewport, use a TextureRect (handles flip automatically) instead of a custom shader.

A SubViewport renders a mini-map. You assign its texture to a 3D quad shader. The mini-map appears upside-down. Y axis differs between framebuffer and texture.

The Symptom

SubViewport content displayed on a custom mesh appears flipped vertically. Same content in a TextureRect (UI) is right-side-up. Or: appears correct in editor preview, flipped in PIE.

The Fix

shader_type spatial;

uniform sampler2D viewport_tex : filter_linear;

void fragment() {
    vec2 flipped = vec2(UV.x, 1.0 - UV.y);
    ALBEDO = texture(viewport_tex, flipped).rgb;
}

Pass the SubViewport’s texture to the shader from script:

$Quad.material_override.set_shader_parameter(
    "viewport_tex",
    $SubViewport.get_texture()
)

HDR / sRGB Match

SubViewport → tick Use HDR if the target mesh expects linear values; untick if you want sRGB. Mismatch produces washed-out or over-saturated colors. The flip-only fix is for orientation; HDR is a separate dial.

Transparent Background

If the SubViewport should overlay other geometry, set transparent_bg = true on the SubViewport. Without it, the texture has a solid clear color around the rendered content.

Verifying

Place text in the SubViewport. Display on the quad. Text should read correctly. If still flipped, the shader sampling line wasn’t edited; if upside-down on a TextureRect (rare), the SubViewport’s render direction setting is wrong.

“Flip Y in the shader. Match HDR/sRGB. Mini-map reads right.”

Related Issues

For shader uniform arrays, see uniform arrays. For shader uniform runtime, see uniform update.

Flip Y. Match color space. Right way up.