From 484f38382654457b6a452dbf4513c67dda94f7a4 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 3 May 2026 10:17:52 +0900 Subject: [PATCH 1/2] Parts: fix multiply/screen draw_filter to respect alpha The multiply and screen blend functions used in render_flat_item ignored the source alpha, so transparent pixels still modulated the destination color instead of leaving it untouched. Add a draw_filter uniform to the parts fragment shader and have it output an alpha-baked source color, so that the framebuffer result after the GL blend equation interpolates correctly in alpha: - multiply: shader outputs src = mix(vec3(1.0), mod_color, alpha) blend (GL_DST_COLOR, GL_ZERO) gives src*dst, i.e. mix(dst, mod_color*dst, alpha) - screen: shader outputs src = mod_color * alpha blend (GL_ONE, GL_ONE_MINUS_SRC_COLOR) gives src + (1-src)*dst, i.e. mod_color*alpha + (1 - mod_color*alpha)*dst In both cases alpha=0 leaves the destination untouched, alpha=1 gives the full multiply/screen result. Also switch the surface-area mask from multiplication to discard, so pixels outside the rect leave the destination intact under multiply blending (where multiplying src by 0 would otherwise zero out dst). --- shaders/parts.f.glsl | 33 ++++++++++++++++++++++++--------- src/parts/parts.c | 2 +- src/parts/parts_internal.h | 7 +++++++ src/parts/render.c | 23 +++++++++++++---------- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/shaders/parts.f.glsl b/shaders/parts.f.glsl index 952a5fb..a93d64a 100644 --- a/shaders/parts.f.glsl +++ b/shaders/parts.f.glsl @@ -20,6 +20,7 @@ uniform vec2 bot_left; uniform vec2 top_right; uniform vec3 add_color; uniform vec3 multiply_color; +uniform int draw_filter; uniform int use_clipper; uniform sampler2D clipper_tex; @@ -28,23 +29,37 @@ in vec2 tex_coord; in vec2 clip_coord; out vec4 frag_color; -float point_in_rect(vec2 p, vec2 bot_left, vec2 top_right) { - vec2 s = step(bot_left, p) - step(top_right, p); - return s.x * s.y; +const int DRAW_FILTER_MULTIPLY = 2; +const int DRAW_FILTER_SCREEN = 3; + +bool inside_rect(vec2 p, vec2 bl, vec2 tr) { + return all(greaterThanEqual(p, bl)) && all(lessThan(p, tr)); } void main() { - vec2 size = vec2(textureSize(tex, 0)); - vec2 bl = bot_left / size; - vec2 tr = top_right / size; + vec2 size = vec2(textureSize(tex, 0)); + if (!inside_rect(tex_coord, bot_left / size, top_right / size)) + discard; vec4 tex_color = texture(tex, tex_coord); vec3 mod_color = (tex_color.rgb + add_color) * multiply_color; - float alpha = tex_color.a * blend_rate; + if (use_clipper != 0) { - alpha *= texture(clipper_tex, clip_coord).a * point_in_rect(clip_coord, vec2(0.0), vec2(1.0)); + if (!inside_rect(clip_coord, vec2(0.0), vec2(1.0))) + discard; + alpha *= texture(clipper_tex, clip_coord).a; } - frag_color = vec4(mod_color, alpha) * point_in_rect(tex_coord, bl, tr); + if (draw_filter == DRAW_FILTER_MULTIPLY) { + // result = mix(dst, mod_color*dst, alpha), used with (GL_DST_COLOR, GL_ZERO) + frag_color = vec4(mix(vec3(1.0), mod_color, alpha), 1.0); + } else if (draw_filter == DRAW_FILTER_SCREEN) { + // result = mod_color*alpha + (1 - mod_color*alpha)*dst, + // used with (GL_ONE, GL_ONE_MINUS_SRC_COLOR) + frag_color = vec4(mod_color * alpha, 1.0); + } else { + // normal or additive — blend func handles alpha + frag_color = vec4(mod_color, alpha); + } } diff --git a/src/parts/parts.c b/src/parts/parts.c index a45ad70..709c0f8 100644 --- a/src/parts/parts.c +++ b/src/parts/parts.c @@ -1728,7 +1728,7 @@ void PE_SetAlpha(int parts_no, int alpha) void PE_SetPartsDrawFilter(int parts_no, int draw_filter) { - if (draw_filter && draw_filter != 1) + if (draw_filter != PARTS_DRAW_FILTER_NORMAL && draw_filter != PARTS_DRAW_FILTER_ADDITIVE) UNIMPLEMENTED("(%d, %d)", parts_no, draw_filter); parts_get(parts_no)->draw_filter = draw_filter; } diff --git a/src/parts/parts_internal.h b/src/parts/parts_internal.h index ca4396d..7b4c48c 100644 --- a/src/parts/parts_internal.h +++ b/src/parts/parts_internal.h @@ -265,6 +265,13 @@ enum parts_flash_blend_mode { PARTS_FLASH_BLEND_HARDLIGHT = 14, }; +enum parts_draw_filter { + PARTS_DRAW_FILTER_NORMAL = 0, + PARTS_DRAW_FILTER_ADDITIVE = 1, + PARTS_DRAW_FILTER_MULTIPLY = 2, + PARTS_DRAW_FILTER_SCREEN = 3, +}; + struct parts_flash_object { TAILQ_ENTRY(parts_flash_object) entry; uint16_t depth; diff --git a/src/parts/render.c b/src/parts/render.c index 557988a..b23cdb7 100644 --- a/src/parts/render.c +++ b/src/parts/render.c @@ -46,12 +46,13 @@ static struct { GLint top_right; GLint add_color; GLint multiply_color; + GLint draw_filter; GLint use_clipper; GLint clipper_tex; GLint inv_clipper_transform; } parts_shader; -static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rectangle *rect, float blend_rate, vec3 add_color, vec3 multiply_color, int alpha_clipper) +static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rectangle *rect, float blend_rate, vec3 add_color, vec3 multiply_color, int draw_filter, int alpha_clipper) { mat4 wv_transform = WV_TRANSFORM(config.view_width, config.view_height); @@ -70,6 +71,7 @@ static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rec glUniform2f(parts_shader.top_right, rect->x + rect->w, rect->y + rect->h); glUniform3fv(parts_shader.add_color, 1, add_color); glUniform3fv(parts_shader.multiply_color, 1, multiply_color); + glUniform1i(parts_shader.draw_filter, draw_filter); struct parts *clipper = alpha_clipper ? parts_try_get(alpha_clipper) : NULL; if (clipper) { @@ -121,7 +123,7 @@ static void parts_render_text(struct parts *parts, struct parts_text *t) struct parts_text_char *ch = &line->chars[j]; mat4 mw_transform = WORLD_TRANSFORM(ch->t.w, ch->t.h, x, y); Rectangle r = { 0, 0, ch->t.w, ch->t.h }; - parts_render_texture(&ch->t, mw_transform, &r, blend_rate, add_color, multiply_color, parts->alpha_clipper_parts_no); + parts_render_texture(&ch->t, mw_transform, &r, blend_rate, add_color, multiply_color, 0, parts->alpha_clipper_parts_no); x += ch->advance; } x = parts->global.pos.x + t->common.origin_offset.x; @@ -132,7 +134,7 @@ static void parts_render_text(struct parts *parts, struct parts_text *t) static void parts_render_cg(struct parts *parts, struct parts_common *common) { switch (parts->draw_filter) { - case 1: + case PARTS_DRAW_FILTER_ADDITIVE: glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); break; default: @@ -164,7 +166,7 @@ static void parts_render_cg(struct parts *parts, struct parts_common *common) parts->global.multiply_color.g / 255.0f, parts->global.multiply_color.b / 255.0f, }; - parts_render_texture(&common->texture, mw_transform, &r, parts->global.alpha / 255.0, add_color, multiply_color, parts->alpha_clipper_parts_no); + parts_render_texture(&common->texture, mw_transform, &r, parts->global.alpha / 255.0, add_color, multiply_color, 0, parts->alpha_clipper_parts_no); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); } @@ -207,13 +209,13 @@ static void render_flat_item(struct parts *parts, struct parts_flat *f, Texture *tex = &f->textures[lib_idx]; switch (key->draw_filter) { - case 1: // additive + case PARTS_DRAW_FILTER_ADDITIVE: glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); break; - case 2: // multiply + case PARTS_DRAW_FILTER_MULTIPLY: glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); break; - case 3: // screen + case PARTS_DRAW_FILTER_SCREEN: glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE); break; default: @@ -234,9 +236,9 @@ static void render_flat_item(struct parts *parts, struct parts_flat *f, vec3 add_color = { key->add_r / 255.0f, key->add_g / 255.0f, key->add_b / 255.0f }; vec3 mul_color = { key->mul_r / 255.0f, key->mul_g / 255.0f, key->mul_b / 255.0f }; parts_render_texture(tex, render_m, &rect, alpha, add_color, mul_color, - parts->alpha_clipper_parts_no); + key->draw_filter, parts->alpha_clipper_parts_no); - if (key->draw_filter != 0) + if (key->draw_filter != PARTS_DRAW_FILTER_NORMAL) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); break; } @@ -329,7 +331,7 @@ static void parts_render_flash_shape(struct parts *parts, struct parts_flash *f, (parts->global.multiply_color.g / 255.0f) * fixed16_to_float(obj->color_transform.mult_terms[1]), (parts->global.multiply_color.b / 255.0f) * fixed16_to_float(obj->color_transform.mult_terms[2]) }; - parts_render_texture(src, mw_transform, &r, blend_rate, add_color, multiply_color, parts->alpha_clipper_parts_no); + parts_render_texture(src, mw_transform, &r, blend_rate, add_color, multiply_color, 0, parts->alpha_clipper_parts_no); } static void parts_render_flash_sprite(struct parts *parts, struct parts_flash *f, struct parts_flash_object *obj, struct swf_tag_define_sprite *tag) @@ -502,6 +504,7 @@ void parts_render_init(void) parts_shader.top_right = glGetUniformLocation(parts_shader.shader.program, "top_right"); parts_shader.add_color = glGetUniformLocation(parts_shader.shader.program, "add_color"); parts_shader.multiply_color = glGetUniformLocation(parts_shader.shader.program, "multiply_color"); + parts_shader.draw_filter = glGetUniformLocation(parts_shader.shader.program, "draw_filter"); parts_shader.use_clipper = glGetUniformLocation(parts_shader.shader.program, "use_clipper"); parts_shader.clipper_tex = glGetUniformLocation(parts_shader.shader.program, "clipper_tex"); parts_shader.inv_clipper_transform = glGetUniformLocation(parts_shader.shader.program, "inv_clipper_transform"); From c45bb3a7366341bc1f64b6957a6ecac8e4421b9d Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 3 May 2026 11:32:32 +0900 Subject: [PATCH 2/2] Parts: support multiply/screen draw_filter for parts_render_cg --- src/parts/parts.c | 2 -- src/parts/render.c | 41 ++++++++++++++++++++--------------------- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/parts/parts.c b/src/parts/parts.c index 709c0f8..7ad11f6 100644 --- a/src/parts/parts.c +++ b/src/parts/parts.c @@ -1728,8 +1728,6 @@ void PE_SetAlpha(int parts_no, int alpha) void PE_SetPartsDrawFilter(int parts_no, int draw_filter) { - if (draw_filter != PARTS_DRAW_FILTER_NORMAL && draw_filter != PARTS_DRAW_FILTER_ADDITIVE) - UNIMPLEMENTED("(%d, %d)", parts_no, draw_filter); parts_get(parts_no)->draw_filter = draw_filter; } diff --git a/src/parts/render.c b/src/parts/render.c index b23cdb7..3a49d2d 100644 --- a/src/parts/render.c +++ b/src/parts/render.c @@ -52,6 +52,23 @@ static struct { GLint inv_clipper_transform; } parts_shader; +static void set_draw_filter_blend_func(int draw_filter) +{ + switch (draw_filter) { + case PARTS_DRAW_FILTER_ADDITIVE: + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); + break; + case PARTS_DRAW_FILTER_MULTIPLY: + glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); + break; + case PARTS_DRAW_FILTER_SCREEN: + glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE); + break; + default: + break; + } +} + static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rectangle *rect, float blend_rate, vec3 add_color, vec3 multiply_color, int draw_filter, int alpha_clipper) { mat4 wv_transform = WV_TRANSFORM(config.view_width, config.view_height); @@ -133,13 +150,7 @@ static void parts_render_text(struct parts *parts, struct parts_text *t) static void parts_render_cg(struct parts *parts, struct parts_common *common) { - switch (parts->draw_filter) { - case PARTS_DRAW_FILTER_ADDITIVE: - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); - break; - default: - break; - } + set_draw_filter_blend_func(parts->draw_filter); mat4 mw_transform = GLM_MAT4_IDENTITY_INIT; glm_translate(mw_transform, (vec3) { parts->global.pos.x, parts->global.pos.y, 0 }); @@ -166,7 +177,7 @@ static void parts_render_cg(struct parts *parts, struct parts_common *common) parts->global.multiply_color.g / 255.0f, parts->global.multiply_color.b / 255.0f, }; - parts_render_texture(&common->texture, mw_transform, &r, parts->global.alpha / 255.0, add_color, multiply_color, 0, parts->alpha_clipper_parts_no); + parts_render_texture(&common->texture, mw_transform, &r, parts->global.alpha / 255.0, add_color, multiply_color, parts->draw_filter, parts->alpha_clipper_parts_no); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); } @@ -208,19 +219,7 @@ static void render_flat_item(struct parts *parts, struct parts_flat *f, return; Texture *tex = &f->textures[lib_idx]; - switch (key->draw_filter) { - case PARTS_DRAW_FILTER_ADDITIVE: - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); - break; - case PARTS_DRAW_FILTER_MULTIPLY: - glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE); - break; - case PARTS_DRAW_FILTER_SCREEN: - glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE); - break; - default: - break; - } + set_draw_filter_blend_func(key->draw_filter); mat4 render_m; glm_mat4_copy(combined, render_m);