From aa5c8e6730b03ea901ead59bc7cb1c31dac62012 Mon Sep 17 00:00:00 2001 From: skal Date: Sun, 22 Mar 2026 20:10:53 +0100 Subject: refactor(shaders): extract oct-normal encode/decode into math/normal snippet New src/shaders/math/normal.wgsl: oct_encode, oct_decode, oct_encode_unorm, oct_decode_unorm. Registered in InitShaderComposer as "math/normal". Removed inline copies from gbuf_raster.wgsl and gbuf_pack.wgsl. 18/18 tests passing. --- cnn_v3/shaders/gbuf_pack.wgsl | 12 ++---------- cnn_v3/shaders/gbuf_raster.wgsl | 17 ++--------------- src/effects/shaders.cc | 1 + src/shaders/math/normal.wgsl | 34 ++++++++++++++++++++++++++++++++++ workspaces/main/assets.txt | 1 + 5 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 src/shaders/math/normal.wgsl diff --git a/cnn_v3/shaders/gbuf_pack.wgsl b/cnn_v3/shaders/gbuf_pack.wgsl index 71d8471..333589c 100644 --- a/cnn_v3/shaders/gbuf_pack.wgsl +++ b/cnn_v3/shaders/gbuf_pack.wgsl @@ -1,4 +1,5 @@ // G-buffer pack compute shader for CNN v3 +#include "math/normal" // Pass 4: Pack all G-buffer channels into two rgba32uint feature textures (32 bytes/pixel) // Output feat_tex0 holds 8×f16 geometric channels; feat_tex1 holds 12×u8 context channels. @@ -44,15 +45,6 @@ fn box4(tl: vec2i) -> vec3f { return acc * (1.0 / 16.0); } -// Decode oct-normal from [0,1] storage → [-1,1] encoded xy → reconstruct z. -fn decode_oct_normal(rg: vec2f) -> vec3f { - let f = rg * 2.0 - vec2f(1.0); - var n = vec3f(f.x, f.y, 1.0 - abs(f.x) - abs(f.y)); - let t = max(-n.z, 0.0); - n.x += select(t, -t, n.x >= 0.0); - n.y += select(t, -t, n.y >= 0.0); - return normalize(n); -} @compute @workgroup_size(8, 8) fn pack_features(@builtin(global_invocation_id) id: vec3u) { @@ -73,7 +65,7 @@ fn pack_features(@builtin(global_invocation_id) id: vec3u) { // Normal: stored as oct-encoded [0,1] in RG; extract just the encoded xy for feat_tex0 let normal_enc = nm.rg; // already in [0,1] — decode to get the xy for CNN input - let n3 = decode_oct_normal(normal_enc); + let n3 = oct_decode_unorm(normal_enc); // Store oct-encoded in [-1,1] remapped back to what CNN expects (the [-1,1] oct xy) let oct_xy = normal_enc * 2.0 - vec2f(1.0); // remap [0,1] → [-1,1] diff --git a/cnn_v3/shaders/gbuf_raster.wgsl b/cnn_v3/shaders/gbuf_raster.wgsl index c762db2..a5eb921 100644 --- a/cnn_v3/shaders/gbuf_raster.wgsl +++ b/cnn_v3/shaders/gbuf_raster.wgsl @@ -3,6 +3,7 @@ // Uses GlobalUniforms, ObjectData, ObjectsBuffer from common_uniforms. #include "common_uniforms" +#include "math/normal" @group(0) @binding(0) var globals: GlobalUniforms; @group(0) @binding(1) var object_data: ObjectsBuffer; @@ -15,20 +16,6 @@ struct VertexOutput { @location(3) @interpolate(flat) instance_index: u32, } -// Octahedral encoding: maps unit normal to [-1,1]^2 -fn oct_encode(n: vec3f) -> vec2f { - let inv_l1 = 1.0 / (abs(n.x) + abs(n.y) + abs(n.z)); - var p = n.xy * inv_l1; - // Fold lower hemisphere - if (n.z < 0.0) { - let s = vec2f( - select(-1.0, 1.0, p.x >= 0.0), - select(-1.0, 1.0, p.y >= 0.0) - ); - p = (1.0 - abs(p.yx)) * s; - } - return p; // in [-1, 1] -} @vertex fn vs_main( @@ -96,7 +83,7 @@ fn fs_main(in: VertexOutput) -> GBufOutput { let mat_id = f32(in.instance_index) / 255.0; // Oct-encode world normal, remap [-1,1] → [0,1] for storage - let oct = oct_encode(normalize(in.world_normal)) * 0.5 + vec2f(0.5); + let oct = oct_encode_unorm(normalize(in.world_normal)); var out: GBufOutput; out.albedo = vec4f(in.color.rgb, 1.0); diff --git a/src/effects/shaders.cc b/src/effects/shaders.cc index 7a68e5f..a6c1b64 100644 --- a/src/effects/shaders.cc +++ b/src/effects/shaders.cc @@ -35,6 +35,7 @@ void InitShaderComposer() { register_if_exists("math/common_utils", AssetId::ASSET_SHADER_MATH_COMMON_UTILS); register_if_exists("math/noise", AssetId::ASSET_SHADER_MATH_NOISE); + register_if_exists("math/normal", AssetId::ASSET_SHADER_MATH_NORMAL); register_if_exists("render/shadows", AssetId::ASSET_SHADER_RENDER_SHADOWS); register_if_exists("render/scene_query_bvh", AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_BVH); diff --git a/src/shaders/math/normal.wgsl b/src/shaders/math/normal.wgsl new file mode 100644 index 0000000..5a9a1a9 --- /dev/null +++ b/src/shaders/math/normal.wgsl @@ -0,0 +1,34 @@ +// Normal encoding/decoding utilities. +// Octahedral encoding: losslessly maps unit normals ↔ vec2f in [-1,1]². +// Storage convention: remap to [0,1] before writing to u8/f16 textures. + +// Encode a unit normal to octahedral XY in [-1, 1]. +fn oct_encode(n: vec3f) -> vec2f { + let inv_l1 = 1.0 / (abs(n.x) + abs(n.y) + abs(n.z)); + var p = n.xy * inv_l1; + if (n.z < 0.0) { + let s = vec2f(select(-1.0, 1.0, p.x >= 0.0), + select(-1.0, 1.0, p.y >= 0.0)); + p = (1.0 - abs(p.yx)) * s; + } + return p; // [-1, 1] +} + +// Decode octahedral XY in [-1, 1] back to a unit normal. +fn oct_decode(f: vec2f) -> vec3f { + var n = vec3f(f.x, f.y, 1.0 - abs(f.x) - abs(f.y)); + let t = max(-n.z, 0.0); + n.x += select(t, -t, n.x >= 0.0); + n.y += select(t, -t, n.y >= 0.0); + return normalize(n); +} + +// Convenience: encode + remap to [0, 1] for texture storage. +fn oct_encode_unorm(n: vec3f) -> vec2f { + return oct_encode(n) * 0.5 + vec2f(0.5); +} + +// Convenience: undo [0, 1] remap then decode. +fn oct_decode_unorm(rg: vec2f) -> vec3f { + return oct_decode(rg * 2.0 - vec2f(1.0)); +} diff --git a/workspaces/main/assets.txt b/workspaces/main/assets.txt index 4a71aac..d436319 100644 --- a/workspaces/main/assets.txt +++ b/workspaces/main/assets.txt @@ -79,6 +79,7 @@ SHADER_RENDER_FULLSCREEN_UV_VS, WGSL, ../../src/shaders/render/fullscreen_uv_vs. SHADER_MATH_COLOR, WGSL, ../../src/shaders/math/color.wgsl, "Color Functions" SHADER_MATH_COLOR_C64, WGSL, ../../src/shaders/math/color_c64.wgsl, "C64 Palette and Bayer Dither" SHADER_MATH_UTILS, WGSL, ../../src/shaders/math/utils.wgsl, "Math Utilities" +SHADER_MATH_NORMAL, WGSL, ../../src/shaders/math/normal.wgsl, "Octahedral normal encode/decode" SHADER_RENDER_RAYMARCHING, WGSL, ../../src/shaders/render/raymarching.wgsl, "Raymarching Functions" SHADER_RENDER_RAYMARCHING_ID, WGSL, ../../src/shaders/render/raymarching_id.wgsl, "Raymarching-ID Functions" SHADER_VIGNETTE, WGSL, ../../src/effects/vignette.wgsl, "Vignette Shader" -- cgit v1.2.3