From b4b85c1f7520ae5541b2cabca6eba4ba77ae8fd6 Mon Sep 17 00:00:00 2001 From: Nunuhara Cabbage Date: Sun, 20 Jun 2021 10:46:53 -0700 Subject: [PATCH] Implement crossfade mosaic effect This one is used quite frequently in Sengoku Rance. The implementation here is slightly wrong, in that the anchor point for the pixel zoom is at the lop left of the screen instead of the center. --- shaders/effects/mosaic.f.glsl | 70 +++++++++++++++++++++++++++++++++++ src/effect.c | 2 + 2 files changed, 72 insertions(+) create mode 100644 shaders/effects/mosaic.f.glsl diff --git a/shaders/effects/mosaic.f.glsl b/shaders/effects/mosaic.f.glsl new file mode 100644 index 0000000..d99af13 --- /dev/null +++ b/shaders/effects/mosaic.f.glsl @@ -0,0 +1,70 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#version 140 + +uniform sampler2D tex; // the new scene +uniform sampler2D old; // the old scene +uniform vec2 resolution; // the screen resolution +uniform float progress; // effect progress (0..1) + +in vec2 tex_coord; +out vec4 frag_color; + +#define MAX_STRIDE 128.0 + +vec3 get_pixel(vec2 xy) { + return mix(texture(old, xy).rgb, texture(tex, xy).rgb, progress); +} + +void main() { + // size of each chunk + float stride = MAX_STRIDE * (1.0 - abs(progress * 2.0 - 1.0)); // progress=(0..0.5..1) -> stride=(0..max..0) + // offset of frag coordinate relative to chunk + vec2 offset = mod(gl_FragCoord.xy, stride); + // the chunk coordinate + vec2 chunk = tex_coord - offset / resolution; + // coordinate of chunk center + vec2 center = chunk + vec2(stride/2.0, stride/2.0) / resolution; + + // FIXME: In this implementation, the anchor point is at the top left of the screen. + // It should be anchored at the center to match SACT2.dll. + frag_color = vec4(get_pixel(center), 1.0); +} + +/* NOTE: This is an alternate implementation which computes an average for each chunk. + SACT2.dll does not do this. + +#define SAMPLES 8.0 +#define MAX_STRIDE 16.0 + +vec3 get_pixel(vec2 xy) { + return mix(texture(old, xy).xyz, texture(tex, xy).xyz, progress); +} + +void main() { + float stride = MAX_STRIDE * (1.0 - abs(progress * 2.0 - 1.0)); // progress=(0..0.5..1) -> stride=(0..max..0) + vec3 avg = vec3(0, 0, 0); + vec2 xy = floor(gl_FragCoord.xy / (SAMPLES * stride)) * (SAMPLES * stride); + for (float j = 0.0; j < SAMPLES; j += 1.0) { + for (float i = 0.0; i < SAMPLES; i+= 1.0) { + avg += get_pixel((xy + stride * vec2(i, j)) / resolution); + } + } + avg /= SAMPLES * SAMPLES; + frag_color = vec4(avg, 1.0); +} +*/ diff --git a/src/effect.c b/src/effect.c index 71826d1..bfd704b 100644 --- a/src/effect.c +++ b/src/effect.c @@ -129,11 +129,13 @@ static void render_effect_shader(struct effect_shader *shader, Texture *old, Tex #define EFFECT_SHADER(path) { .s = { .prepare = prepare_effect_shader }, .f_path = path} static struct effect_shader crossfade_shader = EFFECT_SHADER("shaders/effects/crossfade.f.glsl"); +static struct effect_shader mosaic_shader = EFFECT_SHADER("shaders/effects/mosaic.f.glsl"); extern GLuint main_surface_fb; static struct effect_shader *effect_shaders[NR_EFFECTS] = { [EFFECT_CROSSFADE] = &crossfade_shader, + [EFFECT_CROSSFADE_MOSAIC] = &mosaic_shader, }; int sact_Effect(int type, possibly_unused int time, possibly_unused int key)