Texture blit: texture-processing shaders
Inputs, target coordinates, four outputs and RGBA blend equations.
texture_blit defines one GPU operation on DrawableTexture2D. Its sole blit() function runs for destination-rectangle pixels. It is not a canvas_item effect automatically evaluated every time the texture is displayed.
Complete tinted copy shader
Create a ShaderMaterial containing this Shader and pass it as the final BlitRect argument. Initialize the destination using Setup first; a complete C# helper is in the DrawableTexture2D guide.
shader_type texture_blit;
uniform sampler2D source : hint_blit_source0;
uniform vec4 tint : source_color = vec4(1.0);
void blit() {
COLOR0 = texture(source, UV) * MODULATE * tint;
}target.BlitRect(rect, source, Colors.White, 0, material) scales the entire source into the destination rect. Use the destination dimensions for a full-target write. Edit tint on the ShaderMaterial; the new uniform takes effect on the next blit.
Blend modes
S is shader output, D is the previous destination, and a is S.a. The checked ordinary RGBA8 path uses these equations before format clamping:
| Mode | RGB | Alpha |
|---|---|---|
| blend_disabled | S.rgb | S.a |
| blend_mix | S.rgb × a + D.rgb × (1−a) | a + D.a × (1−a) |
| blend_add | S.rgb + D.rgb | S.a + D.a |
| blend_sub | D.rgb − S.rgb | D.a − S.a |
| blend_mul | S.rgb × D.rgb | S.a × D.a |
| blend_premul_alpha | S.rgb + D.rgb × (1−a) | a + D.a × (1−a) |
A custom shader without render_mode uses blend_disabled. The DrawableTexture2D default material explicitly uses blend_mix. Do not transfer one default to the other. Here add/sub RGB is not multiplied by alpha; the table does not promise identical CanvasItem or all-format arithmetic.
Shader premultiplied alpha is supported. Use this complete example for straight, not-yet-premultiplied RGBA. Remove the RGB multiplication when the input is already premultiplied.
shader_type texture_blit;
render_mode blend_premul_alpha;
uniform sampler2D source : hint_blit_source0;
void blit() {
COLOR0 = texture(source, UV) * MODULATE;
COLOR0.rgb *= COLOR0.a;
}Use a ShaderMaterial for this mode. In this BlitMaterial version, the declared PremultipliedAlpha value exceeds the current cache range; that path is not executed by these checks and should not be invoked. The wrapper's other five modes and shader blend_premul_alpha are different paths.
Four inputs and four outputs
Declare sampler2D inputs with hint_blit_source0 through hint_blit_source3. Supplied sources are bound by order, not variable name. MODULATE contains the operation's color argument, but your code must apply it: COLOR0 is not automatically multiplied by it.
A complete two-source/four-output shader:
shader_type texture_blit;
uniform sampler2D source0 : hint_blit_source0;
uniform sampler2D source1 : hint_blit_source1;
uniform vec2 target_size = vec2(64.0, 32.0);
void blit() {
COLOR0 = texture(source0, UV) * MODULATE;
COLOR1 = texture(source1, UV) * MODULATE;
COLOR2 = vec4(UV, 0.0, 1.0);
COLOR3 = vec4(FRAGCOORD.xy / target_size, 0.0, 1.0);
}Use DrawableOps.Split from the linked guide. It supplies first/second sources and three extra destinations. target_size must match the actual base destination dimensions for this diagnostic FRAGCOORD output; the default example uses 64×32.
COLOR0 maps to the primary destination; COLOR1 through COLOR3 map to extra destinations. Outputs without a bound destination are discarded. However, a bound output not assigned by user code receives opaque black (0,0,0,1) in current RD, not transparent color. Do not leave it unwritten expecting existing pixels to survive; blend_disabled overwrites them.
Coordinates and built-ins
UV spans 0 through 1 across the destination rectangle, allowing a full source sample. FRAGCOORD.xy is a pixel center in the current destination texture mipmap level, not the window or viewport. For a partial 32×16 rectangle in a 64×32 target, UV and FRAGCOORD/target size differ.
| Type and name | Meaning |
|---|---|
in float TIME | Renderer time at the operation; it does not schedule another blit. Follows Engine.TimeScale, not tree pause. Rollover is rendering/limits/time/time_rollover_secs, normally 3600 seconds. |
in float PI / TAU / E | Mathematical constants. |
in vec4 FRAGCOORD | Destination pixel center; xy in target-level pixels. |
in vec2 UV | Normalized destination-rectangle coordinates. |
in vec4 MODULATE | Operation color; a multiplier, not a channel write mask. |
out vec4 COLOR0 / COLOR1 / COLOR2 / COLOR3 | Corresponding destination RGBA. Current RD initializes each to (0,0,0,1). |
in is read-only and out is a result. This type has no CanvasItem vertex(), fragment(), TEXTURE or COLOR: declare sources and outputs according to the texture_blit contract. TIME and constants are global; remaining names belong to blit().
Constraints and troubleshooting
The 1–4 destinations must have equal dimensions and the selected mipmap level. Sources cannot alias destinations. Resource BlitRect does not support AtlasTexture as a crop: use the underlying atlas and compute UV yourself. Uniform/source/time changes without a new operation do not update stored pixels. Alternate two resources for an effect chain.
| Symptom | Check |
|---|---|
| Material has no intended effect | shader_type texture_blit and material passed to the current blit call. |
| Alpha is stored rather than blended | Custom shader render_mode; its default is blend_disabled. |
| Premultiplied result is too dark | RGB may have been multiplied by alpha twice. |
| Extra destination turns black | Explicit assignment to its COLOR1/2/3. |
| Coordinates appear offset | Rectangle UV versus destination FRAGCOORD, especially at a mipmap level. |
| An individual channel was not preserved | MODULATE is not a channel mask; use an explicit formula and separate previous-value source. |
Three complete shaders, the listed ordinary RGBA8 blend modes, outputs and coordinates are checked on Windows Forward+/Vulkan. Other color formats, the complete renderer/platform matrix and performance are not fully runtime-accepted here. The BlitMaterial cache limitation is source-identified and its unsafe enum is not invoked; engine core was not changed for this documentation.