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.