This commit is contained in:
2026-09-10 18:15:12 +03:00
commit a4bbe43f87
201 changed files with 1785 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mask
shader_type canvas_item;
render_mode blend_mix, unshaded;
uniform vec4 channel : source_color;
uniform sampler2D tex_main : filter_linear_mipmap;
void fragment() {
COLOR = channel * texture(tex_main, UV).a;
}
+43
View File
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mask + Add
shader_type canvas_item;
render_mode blend_premul_alpha, unshaded;
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;
uniform sampler2D tex_mask : filter_linear_mipmap;
uniform float mask_scale;
uniform vec2 canvas_size;
uniform vec2 mesh_offset;
varying vec2 MASK_UV;
varying vec4 modulate;
void vertex() {
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
vec2 mask_vtx = VERTEX - mesh_offset;
MASK_UV = mask_vtx / mask_size;
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
vec4 color_for_mask = color_tex * color_base * modulate;
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
color_for_mask.rgb = color_for_mask.rgb * mask_val;
COLOR = vec4(color_for_mask.rgb, 0.0);
}
+43
View File
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mask + AddInv
shader_type canvas_item;
render_mode blend_premul_alpha, unshaded;
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;
uniform sampler2D tex_mask : filter_linear_mipmap;
uniform float mask_scale;
uniform vec2 canvas_size;
uniform vec2 mesh_offset;
varying vec2 MASK_UV;
varying vec4 modulate;
void vertex() {
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
vec2 mask_vtx = VERTEX - mesh_offset;
MASK_UV = mask_vtx / mask_size;
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
vec4 color_for_mask = color_tex * color_base * modulate;
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
color_for_mask.rgb = color_for_mask.rgb * (1.0 - mask_val);
COLOR = vec4(color_for_mask.rgb, 0.0);
}
+43
View File
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mask + Mix
shader_type canvas_item;
render_mode blend_premul_alpha, unshaded;
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;
uniform sampler2D tex_mask : filter_linear_mipmap;
uniform float mask_scale;
uniform vec2 canvas_size;
uniform vec2 mesh_offset;
varying vec2 MASK_UV;
varying vec4 modulate;
void vertex() {
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
vec2 mask_vtx = VERTEX - mesh_offset;
MASK_UV = mask_vtx / mask_size;
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
vec4 color_for_mask = color_tex * color_base * modulate;
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
color_for_mask = color_for_mask * mask_val;
COLOR = color_for_mask;
}
+43
View File
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mask + MixInv
shader_type canvas_item;
render_mode blend_premul_alpha, unshaded;
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;
uniform sampler2D tex_mask : filter_linear_mipmap;
uniform float mask_scale;
uniform vec2 canvas_size;
uniform vec2 mesh_offset;
varying vec2 MASK_UV;
varying vec4 modulate;
void vertex() {
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
vec2 mask_vtx = VERTEX - mesh_offset;
MASK_UV = mask_vtx / mask_size;
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
vec4 color_for_mask = color_tex * color_base * modulate;
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
color_for_mask = color_for_mask * (1.0 - mask_val);
COLOR = color_for_mask;
}
+48
View File
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mask + Mul
shader_type canvas_item;
render_mode blend_mul, unshaded;
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;
uniform sampler2D tex_mask : filter_linear_mipmap;
uniform float mask_scale;
uniform vec2 canvas_size;
uniform vec2 mesh_offset;
varying vec2 MASK_UV;
varying vec4 modulate;
void vertex() {
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
vec2 mask_vtx = VERTEX - mesh_offset;
MASK_UV = mask_vtx / mask_size;
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
vec4 color_for_mask = color_tex * color_base * modulate;
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
color_for_mask = color_for_mask * mask_val;
COLOR = vec4(
color_for_mask.r + (1.0 - color_for_mask.a),
color_for_mask.g + (1.0 - color_for_mask.a),
color_for_mask.b + (1.0 - color_for_mask.a),
1.0
);
}
+48
View File
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mask + MulInv
shader_type canvas_item;
render_mode blend_mul, unshaded;
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;
uniform sampler2D tex_mask : filter_linear_mipmap;
uniform float mask_scale;
uniform vec2 canvas_size;
uniform vec2 mesh_offset;
varying vec2 MASK_UV;
varying vec4 modulate;
void vertex() {
vec2 mask_size = vec2(textureSize(tex_mask, 0)) / mask_scale;
vec2 mask_vtx = VERTEX - mesh_offset;
MASK_UV = mask_vtx / mask_size;
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = color_tex.rgb + color_screen.rgb - (color_tex.rgb * color_screen.rgb);
vec4 color_for_mask = color_tex * color_base * modulate;
color_for_mask.rgb = color_for_mask.rgb * color_for_mask.a;
vec4 clip_mask = texture(tex_mask, MASK_UV) * channel;
float mask_val = clip_mask.r + clip_mask.g + clip_mask.b + clip_mask.a;
color_for_mask = color_for_mask * (1.0 - mask_val);
COLOR = vec4(
color_for_mask.r + (1.0 - color_for_mask.a),
color_for_mask.g + (1.0 - color_for_mask.a),
color_for_mask.b + (1.0 - color_for_mask.a),
1.0
);
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Add
shader_type canvas_item;
render_mode blend_premul_alpha, unshaded;
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 vec4 modulate;
void vertex() {
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = (color_tex.rgb + color_screen.rgb) - (color_tex.rgb * color_screen.rgb);
vec4 color = color_tex * color_base * modulate;
COLOR = vec4(color.rgb * color.a, 0.0);
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mix
shader_type canvas_item;
render_mode blend_premul_alpha, unshaded;
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 vec4 modulate;
void vertex() {
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = (color_tex.rgb + color_screen.rgb) - (color_tex.rgb * color_screen.rgb);
vec4 color = color_tex * color_base * modulate;
COLOR = vec4(color.rgb * color.a, color.a);
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
// GDCubism shader: Mul
shader_type canvas_item;
render_mode blend_premul_alpha, unshaded;
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 vec4 modulate;
void vertex() {
modulate = COLOR;
}
void fragment() {
vec4 color_tex = texture(tex_main, UV);
color_tex.rgb = color_tex.rgb * color_multiply.rgb;
// premul alpha
color_tex.rgb = (color_tex.rgb + color_screen.rgb) - (color_tex.rgb * color_screen.rgb);
vec4 color = color_tex * color_base * modulate;
COLOR = vec4(color.rgb * color.a, color.a);
}
+145
View File
@@ -0,0 +1,145 @@
# 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.
### 510. 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);
}
```