# Shader contract for gd_cubism (Live2D) ## Overview gd_cubism uses 10 shaders to render Live2D models. Each drawable (mesh part) selects one of these shaders based on its blend mode and mask configuration. A mod can replace any shader by placing a `.gdshader` file with the same name in its `shaders/` directory. ## Mandatory contract ### Uniforms — DO NOT remove or rename Every shader below MUST declare the uniforms listed in its section. The C++ renderer sets these uniforms by name via `ShaderMaterial::set_shader_parameter()`. Removing or renaming a uniform will break rendering. You MAY add new uniforms (e.g. `uniform float pixel_size = 4.0;`), but you must set them manually in code via `model.GetMeshes()`. ### Vertex shader All shaders MUST include `UV.y = 1.0 - UV.y;` in the vertex function. This flips the texture vertically to match Live2D's coordinate system. ### blend_mode must match The `render_mode` line must remain compatible with the blend operation the C++ code expects. Changing `blend_premul_alpha` to `blend_mix` will produce incorrect transparency. --- ## Shader catalog ### 1. `2d_cubism_norm_mix.gdshader` — Normal blend (most drawables) render_mode: `blend_premul_alpha, unshaded` Mandatory uniforms: ``` uniform vec4 color_base : source_color; uniform vec4 color_screen : source_color; uniform vec4 color_multiply : source_color; uniform vec4 channel : source_color; uniform sampler2D tex_main : filter_linear_mipmap; ``` Varying: `varying vec4 modulate` (passed from vertex: `modulate = COLOR`) Fragment output: `COLOR = vec4(color.rgb * color.a, color.a);` ### 2. `2d_cubism_norm_add.gdshader` — Additive blend Same uniforms and contract as norm_mix. Fragment output: `COLOR = vec4(color.rgb * color.a, 0.0);` (alpha=0 for additive) ### 3. `2d_cubism_norm_mul.gdshader` — Multiply blend Same uniforms and contract as norm_mix. ### 4. `2d_cubism_mask.gdshader` — Mask generation (offscreen) render_mode: `blend_mix, unshaded` Mandatory uniforms: ``` uniform vec4 channel : source_color; uniform sampler2D tex_main : filter_linear_mipmap; ``` Fragment output: `COLOR = channel * texture(tex_main, UV).a;` This shader writes alpha to the mask texture. Not visible on screen. ### 5–10. Masked drawable shaders `2d_cubism_mask_mix`, `mask_add`, `mask_mul`, `mask_mix_inv`, `mask_add_inv`, `mask_mul_inv` render_mode: same as their non-mask counterpart Mandatory uniforms (all of norm_mix PLUS): ``` uniform sampler2D tex_mask : filter_linear_mipmap; ``` Additional varyings (computed in vertex): ``` varying vec2 MASK_UV; ``` Mask UV computation in vertex: ```glsl MASK_UV = (VERTEX - mesh_offset) / mask_size; // where mask_size = textureSize(tex_mask, 0) / mask_scale ``` Fragment must sample `tex_mask` and multiply/lerp the main color by the sum of mask channels: `clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a`. The `_inv` variants invert this: `(1.0 - mask_val)`. --- ## How to add custom logic **Safe additions** (will not break rendering): - Add new `uniform` declarations (set values via `ShaderMaterial.SetShaderParameter`) - Modify internal fragment calculations (e.g. pixel-art downscale) - Add conditional logic (e.g. `if (pixel_size > 1.0) { ... }`) **Allowed modifications:** - Change texture filtering: `filter_linear_mipmap` → `filter_nearest` - Add post-processing in fragment (color grading, dithering, outline) - Add noise/procedural effects using new uniforms **Forbidden modifications:** - Remove or rename mandatory uniforms - Change `render_mode` blend type - Remove `UV.y = 1.0 - UV.y` from vertex - Change the data type of mandatory uniforms (vec4 → vec3, etc.) --- ## Mod override example To create a pixel-art Live2D shader: 1. Copy `2d_cubism_norm_mix.gdshader` to `mods/your_mod/shaders/` 2. Add a new uniform: `uniform float pixel_size : hint_range(1.0, 16.0) = 4.0;` 3. Modify fragment to downsample: ```glsl vec2 tex_size = vec2(textureSize(tex_main, 0)); vec2 snapped_uv = floor(UV * tex_size / pixel_size) * pixel_size / tex_size; vec4 color_tex = texture(tex_main, snapped_uv); ``` 4. Keep all mandatory uniforms and the vertex shader intact. 5. In code, find the material and set `pixel_size`: ```csharp var meshes = model.GetMeshes(); foreach (var kv in meshes) { var mesh = (ArrayMesh)kv.Value; var mat = (ShaderMaterial)mesh.SurfaceGetMaterial(0); mat.SetShaderParameter("pixel_size", 4.0f); } ```