From 0656a01c0ff7298f5c20f109949a3e78d58d1e30 Mon Sep 17 00:00:00 2001 From: LLLL Colonq Date: Mon, 8 Jun 2026 01:17:37 -0400 Subject: Refactor ubersahader flags --- crates/teleia/src/assets/shaders/uber/frag.glsl | 48 +++++++++++++---------- crates/teleia/src/postprocessing.rs | 52 ++++++++++++++++++++++--- crates/teleia/src/renderer.rs | 44 +++++++++++---------- 3 files changed, 97 insertions(+), 47 deletions(-) diff --git a/crates/teleia/src/assets/shaders/uber/frag.glsl b/crates/teleia/src/assets/shaders/uber/frag.glsl index 31efd0d..9c9e64a 100644 --- a/crates/teleia/src/assets/shaders/uber/frag.glsl +++ b/crates/teleia/src/assets/shaders/uber/frag.glsl @@ -7,8 +7,11 @@ uniform vec2 sprite_offset; uniform vec2 sprite_dims; uniform vec4 color; + uniform sampler2D texture_color; uniform sampler2D texture_normal; +uniform vec2 texture_flip; + uniform vec3 light_ambient_color; uniform vec3 light_dir; uniform vec3 light_dir_color; @@ -17,10 +20,10 @@ uniform vec3 light_pos[5]; uniform vec3 light_color[5]; uniform vec2 light_attenuation[5]; -uniform int effect_flip; -uniform float effect_flash; -uniform float effect_hueshift; -uniform float effect_huescale; +uniform float flash; + +uniform float hue_shift; +uniform float hue_scale; uniform float opacity; @@ -37,20 +40,21 @@ bool flag(int mask) { return (flags & mask) != 0; } -mat3 compute_tbn() { +mat3 compute_tbn(vec2 tc) { vec3 p = -vertex_view_vector; vec3 normal = normalize(vertex_normal); vec3 dpx = dFdx(p); vec3 dpy = dFdy(p); - vec2 duvx = dFdx(vertex_texcoord); - vec2 duvy = dFdy(vertex_texcoord); + vec2 duvx = dFdx(tc); + vec2 duvy = dFdy(tc); vec3 dpyperp = cross(dpy, normal); vec3 dpxperp = cross(normal, dpx); vec3 tangent = dpyperp * duvx.x + dpxperp * duvy.x; vec3 bitangent = dpyperp * duvx.y + dpxperp * duvy.y; float invmax = inversesqrt(max(dot(bitangent, bitangent), dot(bitangent, bitangent))); - float flip = flag(FLIP_TEXTURE) ? 1.0 : -1.0; - return mat3(-tangent * invmax, flip * bitangent * invmax, normal); + // float xflip = -1.0 + 2.0 * float(texture_flip.x); + // float yflip = -1.0 + 2.0 * float(texture_flip.y); + return mat3(-tangent * invmax, bitangent * invmax, normal); } @@ -144,14 +148,16 @@ vec3 hsl_to_rgb(vec3 hsl) { } void main() { - float tcy = flag(FLIP_TEXTURE) ? vertex_texcoord.y : 1.0 - vertex_texcoord.y; - vec2 tc = vec2(vertex_texcoord.x, tcy); - mat3 tbn = compute_tbn(); - if (flag(EFFECTS)) { - float fbase = float(effect_flip); - float fmul = 1.0 - 2.0 * fbase; - tc = vec2(fbase + fmul * vertex_texcoord.x, tcy); + vec2 tc = vec2(vertex_texcoord.x, vertex_texcoord.y); + if (flag(TEXTURE_FLIP)) { + float xfbase = float(texture_flip.x); + float xfmul = 1.0 - 2.0 * xfbase; + float yfbase = float(texture_flip.y); + float yfmul = 1.0 - 2.0 * yfbase; + tc.x *= xfmul; tc.x += xfbase; + tc.y *= yfmul; tc.y += yfbase; } + mat3 tbn = compute_tbn(tc); if (flag(SPRITE)) { tc *= sprite_dims; tc += sprite_offset; @@ -160,11 +166,13 @@ void main() { if (flag(TEXTURE_COLOR)) { frag_color = texture(texture_color, tc); } - if (flag(EFFECTS)) { + if (flag(HUE)) { vec3 hsl = rgb_to_hsl(frag_color.rgb); - hsl.x = mod(hsl.x * effect_huescale + effect_hueshift, 1.0); - vec3 p = hsl_to_rgb(hsl); - frag_color.rgb = vec3(p.r + effect_flash, p.g + effect_flash, p.b + effect_flash); + hsl.x = mod(hsl.x * hue_scale + hue_shift, 1.0); + frag_color.rgb = hsl_to_rgb(hsl); + } + if (flag(FLASH)) { + frag_color.rgb += vec3(flash); } vec3 normal = vertex_normal; if (flag(TEXTURE_NORMAL)) { diff --git a/crates/teleia/src/postprocessing.rs b/crates/teleia/src/postprocessing.rs index b5e8165..227af09 100644 --- a/crates/teleia/src/postprocessing.rs +++ b/crates/teleia/src/postprocessing.rs @@ -1,12 +1,28 @@ use crate::{context, framebuffer, shader, state, utils}; +const NUM_BINDINGS: usize = 5; + #[derive(Clone, Copy)] pub struct Effect(usize); + +pub enum Uniform { + I32(i32), + F32(f32), + Vec2(glam::Vec2), + Vec3(glam::Vec3), + Vec4(glam::Vec4), + Mat4(glam::Mat4), +} +struct Stage { + effect: Effect, + bindings: [Option<(&'static str, Uniform)>; NUM_BINDINGS], +} + pub struct Pipeline { - pub effects: Vec, - pub upscale: shader::Shader, - pub stages: Vec, - pub framebuffer: framebuffer::Framebuffer, + effects: Vec, + upscale: shader::Shader, + stages: Vec, + framebuffer: framebuffer::Framebuffer, } impl Pipeline { pub fn new(ctx: &context::Context) -> Self { @@ -37,19 +53,43 @@ impl Pipeline { } /// Apply a postprocessing effect to the current frame. pub fn apply(&mut self, effect: Effect) { - self.stages.push(effect); + self.stages.push(Stage { effect, bindings: Default::default() }); + } + /// Apply a postprocessing effect and set extra uniforms. + pub fn apply_with_bindings(&mut self, effect: Effect, bs: I) + where I: IntoIterator { + let mut bindings: [_; _] = Default::default(); + for (si, b) in bs.into_iter().enumerate() { + if si < NUM_BINDINGS { + bindings[si] = Some(b); + } else { + log::warn!("too many bindings on postprocessing effect!"); + break; + } + } + self.stages.push(Stage { effect, bindings }); } /// Upscale the image in the state's rendering framebuffer to the screen, /// applying postprocessing effects in between. pub fn render(&self, ctx: &context::Context, st: &state::State) -> utils::Erm<()> { let mut src = &st.render_framebuffer; let mut dst = &self.framebuffer; - for Effect(e) in self.stages.iter() { + for Stage { effect: Effect(e), bindings } in self.stages.iter() { let s = &self.effects[*e]; dst.bind(ctx); ctx.clear(); s.bind(ctx); s.set_f32(ctx, "time", st.tick as f32 + (st.tick % 60) as f32 / 60.0); + for (nm, val) in bindings.iter().flatten(){ + match val { + Uniform::I32(x) => s.set_i32(ctx, nm, *x), + Uniform::F32(x) => s.set_f32(ctx, nm, *x), + Uniform::Vec2(x) => s.set_vec2(ctx, nm, x), + Uniform::Vec3(x) => s.set_vec3(ctx, nm, x), + Uniform::Vec4(x) => s.set_vec4(ctx, nm, x), + Uniform::Mat4(x) => s.set_mat4(ctx, nm, x), + } + } src.bind_texture(ctx); ctx.render_no_geometry(); std::mem::swap(&mut src, &mut dst); diff --git a/crates/teleia/src/renderer.rs b/crates/teleia/src/renderer.rs index faad4f7..4864cc5 100644 --- a/crates/teleia/src/renderer.rs +++ b/crates/teleia/src/renderer.rs @@ -5,17 +5,18 @@ use bitflags::bitflags; bitflags! { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct UberFlags: u32 { - const TEXTURE_COLOR = 0b00000000001; - const TEXTURE_NORMAL = 0b00000000010; - const FLIP_TEXTURE = 0b00000000100; - const LIGHT_AMBIENT = 0b00000001000; - const LIGHT_DIR = 0b00000010000; - const LIGHT_POINT = 0b00000100000; - const SPRITE = 0b00001000000; - const EFFECTS = 0b00010000000; - const YSKEW = 0b00100000000; - const VERTEX_COLOR = 0b01000000000; - const OPACITY = 0b10000000000; + const VERTEX_COLOR = 1 << 0; + const TEXTURE_COLOR = 1 << 1; + const TEXTURE_NORMAL = 1 << 2; + const TEXTURE_FLIP = 1 << 3; + const LIGHT_AMBIENT = 1 << 4; + const LIGHT_DIR = 1 << 5; + const LIGHT_POINT = 1 << 6; + const HUE = 1 << 7; + const SPRITE = 1 << 8; + const FLASH = 1 << 9; + const YSKEW = 1 << 10; + const OPACITY = 1 << 11; } } impl UberFlags { @@ -268,9 +269,10 @@ impl Renderer { pos: glam::Vec2, dims: glam::Vec2, ) { - self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR); + self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::TEXTURE_FLIP); self.bind_texture(ctx, st, texture); self.set_position_2d(ctx, st, pos, dims); + self.set_vec2(ctx, st, "texture_flip", glam::Vec2::new(0.0, 1.0)); self.render_square(ctx, st); } @@ -281,9 +283,10 @@ impl Renderer { dims: glam::Vec2, rot: glam::Quat, ) { - self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR); + self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::TEXTURE_FLIP); self.bind_texture(ctx, st, texture); self.set_position_2d_rotate(ctx, st, pos, dims, rot); + self.set_vec2(ctx, st, "texture_flip", glam::Vec2::new(0.0, 1.0)); self.render_square(ctx, st); } @@ -293,14 +296,13 @@ impl Renderer { pos: glam::Vec2, dims: glam::Vec2, ) { - self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::EFFECTS); + self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::TEXTURE_FLIP | UberFlags::HUE); self.bind_texture(ctx, st, texture); - self.set_f32(ctx, st, "effect_huescale", 0.0); - self.set_f32(ctx, st, "effect_hueshift", hue); + self.set_vec2(ctx, st, "texture_flip", glam::Vec2::new(0.0, 1.0)); + self.set_f32(ctx, st, "hue_scale", 0.0); + self.set_f32(ctx, st, "hue_shift", hue); self.set_position_2d(ctx, st, pos, dims); self.render_square(ctx, st); - self.set_f32(ctx, st, "effect_huescale", 1.0); - self.set_f32(ctx, st, "effect_hueshift", 0.0); } /// Common case: text in the default font (units are pixels, pos is top left) @@ -311,7 +313,7 @@ impl Renderer { ) { // drawing text might bind the texture self.texture = BoundTexture::None; - self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR | UberFlags::FLIP_TEXTURE); + self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR); let dims = glam::Vec2::new(st.font_default.char_width as f32, st.font_default.char_height as f32); let fpos = pos + glam::Vec2::new(-dims.x / 2.0, dims.y / 2.0); self.set_position_2d(ctx, st, fpos, dims); @@ -326,7 +328,7 @@ impl Renderer { s: &str, ) { self.texture = BoundTexture::None; - self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR | UberFlags::FLIP_TEXTURE); + self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR); let dims = glam::Vec2::new(st.font_default.char_width as f32, st.font_default.char_height as f32); let fpos = pos + glam::Vec2::new(-dims.x / 2.0, st.font_default.char_height as f32 / 2.0); self.set_position_2d(ctx, st, fpos, dims); @@ -340,7 +342,7 @@ impl Renderer { s: &str, ) { self.texture = BoundTexture::None; - self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR | UberFlags::FLIP_TEXTURE); + self.bind_uber_2d(ctx, st, UberFlags::TEXTURE_COLOR | UberFlags::VERTEX_COLOR); let width = s.len() as f32 * st.font_default.char_width as f32; let dims = glam::Vec2::new(st.font_default.char_width as f32, st.font_default.char_height as f32); let fpos = pos + glam::Vec2::new(-dims.x / 2.0 - (width / 2.0).round(), st.font_default.char_height as f32 / 2.0); -- cgit v1.2.3