From e4426ab0d29439e1746f3430980421286e8e2bac Mon Sep 17 00:00:00 2001 From: jacob Date: Sat, 14 Feb 2026 04:08:38 -0600 Subject: [PATCH] particle descs --- src/base/base_shader.gh | 13 +- src/pp/pp_vis/pp_vis.lay | 2 + src/pp/pp_vis/pp_vis_core.c | 122 ++++---- src/pp/pp_vis/pp_vis_gpu.g | 536 ++++++++++++++++++-------------- src/pp/pp_vis/pp_vis_gpu.gh | 3 +- src/pp/pp_vis/pp_vis_shared.cg | 42 +++ src/pp/pp_vis/pp_vis_shared.cgh | 77 ++++- 7 files changed, 496 insertions(+), 299 deletions(-) create mode 100644 src/pp/pp_vis/pp_vis_shared.cg diff --git a/src/base/base_shader.gh b/src/base/base_shader.gh index b9128262..c4d50b15 100644 --- a/src/base/base_shader.gh +++ b/src/base/base_shader.gh @@ -15,8 +15,6 @@ typedef uint2 Vec2U32; typedef uint3 Vec3U32; typedef uint4 Vec4U32; typedef float2x3 Affine; -typedef float4 Rect; -typedef float4 ClipRect; typedef float4 Aabb; typedef float4 Quad; typedef float4x4 Mat4x4; @@ -48,6 +46,17 @@ u32 countof(T arr[N]) //////////////////////////////////////////////////////////// //~ C -> HLSL interoperability stubs +//- Constructors +#define VEC2(...) Vec2(__VA_ARGS__) +#define VEC3(...) Vec3(__VA_ARGS__) +#define VEC4(...) Vec4(__VA_ARGS__) +#define VEC2I32(...) Vec2I32(__VA_ARGS__) +#define VEC3I32(...) Vec3I32(__VA_ARGS__) +#define VEC4I32(...) Vec4I32(__VA_ARGS__) +#define VEC2U32(...) Vec2U32(__VA_ARGS__) +#define VEC3U32(...) Vec3U32(__VA_ARGS__) +#define VEC4U32(...) Vec4U32(__VA_ARGS__) + //- Min #define MinU8 (u8)min #define MinI8 (i8)min diff --git a/src/pp/pp_vis/pp_vis.lay b/src/pp/pp_vis/pp_vis.lay index d07f7c5d..35f236c5 100644 --- a/src/pp/pp_vis/pp_vis.lay +++ b/src/pp/pp_vis/pp_vis.lay @@ -41,5 +41,7 @@ ////////////////////////////// //- Impl +@IncludeC pp_vis_shared.cg @IncludeC pp_vis_core.c +@IncludeG pp_vis_shared.cg @IncludeG pp_vis_gpu.g diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index a8e004a1..2d0b0295 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -392,9 +392,10 @@ void V_TickForever(WaveLaneCtx *lane) // Init gpu state G_ResourceHandle gpu_tiles_res = Zi; G_ResourceHandle gpu_particles_res = Zi; - G_ResourceHandle gpu_stains_res = Zi; + G_ResourceHandle gpu_stain_cells_res = Zi; G_ResourceHandle gpu_ground_cells_res = Zi; G_ResourceHandle gpu_air_cells_res = Zi; + G_ResourceHandle gpu_stain_densities_res = Zi; G_ResourceHandle gpu_ground_densities_res = Zi; G_ResourceHandle gpu_air_densities_res = Zi; G_ResourceHandle gpu_drynesses_res = Zi; @@ -402,9 +403,10 @@ void V_TickForever(WaveLaneCtx *lane) G_Texture2DRef gpu_tiles = Zi; G_RWStructuredBufferRef gpu_particles = Zi; - G_RWTexture2DRef gpu_stains = Zi; + G_RWTexture2DRef gpu_stain_cells = Zi; G_RWTexture2DRef gpu_ground_cells = Zi; G_RWTexture2DRef gpu_air_cells = Zi; + G_RWTexture2DRef gpu_stain_densities = Zi; G_RWTexture2DRef gpu_ground_densities = Zi; G_RWTexture2DRef gpu_air_densities = Zi; G_RWTexture2DRef gpu_drynesses = Zi; @@ -435,17 +437,17 @@ void V_TickForever(WaveLaneCtx *lane) ); gpu_particles = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles_res, V_Particle); } - // Init stains texture + // Init stain cells texture { - gpu_stains_res = G_PushTexture2D( + gpu_stain_cells_res = G_PushTexture2D( gpu_perm, cl, - G_Format_R16G16B16A16_Float, + G_Format_R32_Uint, cells_dims, G_Layout_DirectQueue_ShaderReadWrite, .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite, - .name = Lit("Stains") + .name = Lit("Stain cells") ); - gpu_stains = G_PushRWTexture2DRef(gpu_perm, gpu_stains_res); + gpu_stain_cells = G_PushRWTexture2DRef(gpu_perm, gpu_stain_cells_res); } // Init ground cells texture { @@ -471,6 +473,18 @@ void V_TickForever(WaveLaneCtx *lane) ); gpu_air_cells = G_PushRWTexture2DRef(gpu_perm, gpu_air_cells_res); } + // Init stain densities texture + { + gpu_stain_densities_res = G_PushTexture2D( + gpu_perm, cl, + G_Format_R32_Uint, + cells_dims, + G_Layout_DirectQueue_ShaderReadWrite, + .flags = G_ResourceFlag_ZeroMemory | G_ResourceFlag_AllowShaderReadWrite, + .name = Lit("Stain densities") + ); + gpu_stain_densities = G_PushRWTexture2DRef(gpu_perm, gpu_stain_densities_res); + } // Init ground densities texture { gpu_ground_densities_res = G_PushTexture2D( @@ -631,15 +645,15 @@ void V_TickForever(WaveLaneCtx *lane) //- Initialize persistent gpu resources { - // Persistent resources frame->tiles = gpu_tiles; frame->pt_clamp_sampler = G_BasicPointClampSampler(); frame->pt_wrap_sampler = G_BasicPointWrapSampler(); frame->particles = gpu_particles; - frame->stains = gpu_stains; + frame->stain_cells = gpu_stain_cells; frame->ground_cells = gpu_ground_cells; frame->air_cells = gpu_air_cells; + frame->stain_densities = gpu_stain_densities; frame->ground_densities = gpu_ground_densities; frame->air_densities = gpu_air_densities; frame->drynesses = gpu_drynesses; @@ -2176,6 +2190,7 @@ void V_TickForever(WaveLaneCtx *lane) quad->quad_uv_to_world_af = wep_uv_to_world_af; quad->tex = wep.tex; quad->tex_slice_uv = DivRng2Vec2(wep.tex_rect, wep.tex_dims); + quad->occluder = P_OccluderKind_Guy; } //- Push body quad @@ -2187,6 +2202,7 @@ void V_TickForever(WaveLaneCtx *lane) quad->quad_uv_to_world_af = body_uv_to_world_af; quad->tex = body.tex; quad->tex_slice_uv = DivRng2Vec2(body.tex_rect, body.tex_dims); + quad->occluder = P_OccluderKind_Guy; } } } @@ -2550,17 +2566,18 @@ void V_TickForever(WaveLaneCtx *lane) { V_Emitter emitter = Zi; - emitter.kind = V_ParticleKind_Test; + emitter.kind = V_ParticleKind_Blood; f32 angle = AngleFromVec2(frame->look); // f32 angle = 0; - // f32 angle_spread = Tau * 0.25; - f32 angle_spread = Tau; + f32 angle_spread = Tau * 0.25; + // f32 angle_spread = Tau; // f32 angle_spread = 0; - // f32 speed = 25; + // f32 speed = 5; + f32 speed = 25; // f32 speed = 50; - f32 speed = 100; + // f32 speed = 100; f32 speed_spread = speed * 2; emitter.pos.p0 = emitter.pos.p1 = frame->world_cursor; @@ -2632,42 +2649,42 @@ void V_TickForever(WaveLaneCtx *lane) } // Smoke - { - V_Emitter emitter = Zi; + // { + // V_Emitter emitter = Zi; - emitter.kind = V_ParticleKind_Smoke; + // emitter.kind = V_ParticleKind_Smoke; - f32 angle = AngleFromVec2(frame->look); - // f32 angle = 0; - // f32 angle_spread = Tau * 0.25; - f32 angle_spread = Tau; - // f32 angle_spread = 0; + // f32 angle = AngleFromVec2(frame->look); + // // f32 angle = 0; + // // f32 angle_spread = Tau * 0.25; + // f32 angle_spread = Tau; + // // f32 angle_spread = 0; - f32 speed = 25; - // f32 speed = 50; - // f32 speed = 50; - f32 speed_spread = speed * 2; + // f32 speed = 25; + // // f32 speed = 50; + // // f32 speed = 50; + // f32 speed_spread = speed * 2; - emitter.pos.p0 = emitter.pos.p1 = frame->world_cursor; - emitter.speed.min = speed - speed_spread * 0.5; - emitter.speed.max = speed + speed_spread * 0.5; - emitter.angle.min = angle - angle_spread * 0.5; - emitter.angle.max = angle + angle_spread * 0.5; + // emitter.pos.p0 = emitter.pos.p1 = frame->world_cursor; + // emitter.speed.min = speed - speed_spread * 0.5; + // emitter.speed.max = speed + speed_spread * 0.5; + // emitter.angle.min = angle - angle_spread * 0.5; + // emitter.angle.max = angle + angle_spread * 0.5; - // emitter.falloff.min = emitter.falloff.max = 0; + // // emitter.falloff.min = emitter.falloff.max = 0; - // emitter.count = CeilF32(Kibi(64) * frame->dt); - // emitter.count = Mebi(16); - // emitter.count = Mebi(2); - // emitter.count = Kibi(32); - emitter.count = Kibi(8); - // emitter.count = Kibi(1); - // emitter.count = 128; - // emitter.count = 32; - // emitter.count = 1; + // // emitter.count = CeilF32(Kibi(64) * frame->dt); + // // emitter.count = Mebi(16); + // // emitter.count = Mebi(2); + // // emitter.count = Kibi(32); + // emitter.count = Kibi(8); + // // emitter.count = Kibi(1); + // // emitter.count = 128; + // // emitter.count = 32; + // // emitter.count = 1; - V_PushParticles(emitter); - } + // V_PushParticles(emitter); + // } } @@ -4945,9 +4962,10 @@ void V_TickForever(WaveLaneCtx *lane) G_DumbGlobalMemorySync(frame->cl); ////////////////////////////// - //- Quads pass + //- Quads & emitters pass { + // Draw quads G_Rasterize( frame->cl, V_QuadVS, V_QuadPS, @@ -4956,29 +4974,29 @@ void V_TickForever(WaveLaneCtx *lane) screen_viewport, screen_scissor, G_RasterMode_TriangleList ); + + // Emit particles + G_Compute(frame->cl, V_EmitParticlesCS, V_ThreadGroupSizeFromBufferSize(frame->emitters_count)); + + // Sync particles & occluders + G_DumbGlobalMemorySync(frame->cl); } ////////////////////////////// //- Particle simulation pass { - // Emit particles - G_Compute(frame->cl, V_EmitParticlesCS, V_ThreadGroupSizeFromBufferSize(frame->emitters_count)); - - // Barrier particles buffer - G_DumbMemorySync(frame->cl, gpu_particles_res); - // Simulate particles G_Compute(frame->cl, V_SimParticlesCS, V_ThreadGroupSizeFromBufferSize(V_ParticlesCap)); - // Barrier since stains were written + // Sync cells G_DumbGlobalMemorySync(frame->cl); } ////////////////////////////// //- Shading pass - G_DumbGlobalMemorySync(frame->cl); + // TODO: Remove this { G_Compute(frame->cl, V_ShadeCS, V_ThreadGroupSizeFromTexSize(frame->shade_dims)); diff --git a/src/pp/pp_vis/pp_vis_gpu.g b/src/pp/pp_vis/pp_vis_gpu.g index 6928b6d3..aa245dfe 100644 --- a/src/pp/pp_vis/pp_vis_gpu.g +++ b/src/pp/pp_vis/pp_vis_gpu.g @@ -10,10 +10,54 @@ f32 V_RandFromPos(Vec3 pos) return rand; } -Vec4 V_DryColor(Vec4 color, f32 dryness) +Vec4 V_ColorFromParticle(V_ParticleKind particle_kind, u32 particle_idx, u32 density, f32 dryness) { - Vec4 result = color; + V_ParticleDesc desc = V_DescFromParticleKind(particle_kind); + + Vec4 result = 0; + u64 seed = MixU64(P_ParticleColorBasis ^ particle_idx); + f32 rand_color = Norm16(seed >> 0); + + result = desc.color; + + // // FIXME: Base color on particle desc + // if (particle_kind == V_ParticleKind_Test) + // { + // // result.rgb = Vec3(0, 0, 0); + // result = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 0.5)); + // } + // else if (particle_kind == V_ParticleKind_Debris) + // { + // result = Color_Orange; + // } + // else if (particle_kind == V_ParticleKind_Smoke) + // { + // result = Vec4(0.15, 0.15, 0.15, 1); + // } + + // Apply density + { + if (particle_kind == V_ParticleKind_Smoke) + { + // f32 t = saturate(density / 10.0); + // f32 t = smoothstep(-10, 32, density); + f32 t = smoothstep(0, 2, (f32)density); + result.a = lerp(0, 0.85, t); + } + else if (particle_kind == V_ParticleKind_Blood) + { + f32 t = (f32)density / 10000; + t = pow(t, 2); + t = saturate(t); + result.rgb *= 1.0 - (t * 0.9); + } + } + + result.rgb += (rand_color - 0.5) * 0.025; + + // Apply dryness result.rgb *= 1.0 - (dryness * 0.75); + return result; } @@ -37,8 +81,10 @@ ComputeShader2D(V_PrepareCellsCS, 8, 8) { V_SharedFrame frame = G_Dereference(V_ShaderConst_Frame)[0]; Texture2D tiles = G_Dereference(frame.tiles); + RWTexture2D stain_cells = G_Dereference(frame.stain_cells); RWTexture2D ground_cells = G_Dereference(frame.ground_cells); RWTexture2D air_cells = G_Dereference(frame.air_cells); + RWTexture2D stain_densities = G_Dereference(frame.stain_densities); RWTexture2D ground_densities = G_Dereference(frame.ground_densities); RWTexture2D air_densities = G_Dereference(frame.air_densities); RWTexture2D drynesses = G_Dereference(frame.drynesses); @@ -51,6 +97,28 @@ ComputeShader2D(V_PrepareCellsCS, 8, 8) Vec2 tile_pos = mul(frame.af.world_to_tile, Vec3(world_pos, 1)); P_TileKind tile = tiles[tile_pos]; + // Update stains + if (frame.should_clear_particles) + { + stain_densities[cell_pos] = 0; + stain_cells[cell_pos] = 0; + drynesses[cell_pos] = 0; + } + else + { + u32 stain = stain_cells[cell_pos]; + { + stain &= ~(1 << 31); + } + f32 dryness = drynesses[cell_pos]; + { + f32 dry_rate = frame.dt * 0.1; + dryness = lerp(dryness, 1, dry_rate); + } + stain_cells[cell_pos] = stain; + drynesses[cell_pos] = dryness; + } + // Clear cells ground_cells[cell_pos] = 0; air_cells[cell_pos] = 0; @@ -59,15 +127,6 @@ ComputeShader2D(V_PrepareCellsCS, 8, 8) ground_densities[cell_pos] = 0; air_densities[cell_pos] = 0; - // Increase dryness - // TODO: Use simulation dt - f32 dry_rate = frame.dt * 0.1; - { - f32 old_dryness = drynesses[cell_pos]; - f32 new_dryness = lerp(old_dryness, 1, dry_rate); - drynesses[cell_pos] = new_dryness; - } - // Reset occluders P_OccluderKind occluder = P_OccluderKind_None; if (tile == P_TileKind_Wall) @@ -75,14 +134,6 @@ ComputeShader2D(V_PrepareCellsCS, 8, 8) occluder = P_OccluderKind_Wall; } occluders[cell_pos] = occluder; - - // Clear stain - if (frame.should_clear_particles) - { - RWTexture2D stains = G_Dereference(frame.stains); - stains[cell_pos] = 0; - drynesses[cell_pos] = 0; - } } } @@ -120,6 +171,7 @@ VertexShader(V_QuadVS, V_QuadPSInput) V_QuadPSInput result; result.sv_position = Vec4(NdcFromPos(screen_pos, frame.screen_dims).xy, 0, 1); result.quad_idx = SV_InstanceID; + result.world_pos = world_pos; result.samp_uv = samp_uv; return result; } @@ -130,14 +182,29 @@ VertexShader(V_QuadVS, V_QuadPSInput) PixelShader(V_QuadPS, V_QuadPSOutput, V_QuadPSInput input) { V_SharedFrame frame = G_Dereference(V_ShaderConst_Frame)[0]; + RWTexture2D occluders = G_Dereference(frame.occluders); StructuredBuffer quads = G_Dereference(frame.quads); SamplerState clamp_sampler = G_Dereference(frame.pt_clamp_sampler); V_Quad quad = quads[input.quad_idx]; - Texture2D tex = G_Dereference(quad.tex); + + Vec2 world_pos = input.world_pos; + Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(world_pos, 1)); + + b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(occluders)); + Vec4 albedo = tex.Sample(clamp_sampler, input.samp_uv); + if (is_in_world) + { + // TODO: Remove this + if (albedo.a > 0 && quad.occluder != P_OccluderKind_None && is_in_world) + { + InterlockedMax(occluders[cell_pos], quad.occluder); + } + } + V_QuadPSOutput output; output.sv_target0 = albedo; return output; @@ -180,9 +247,10 @@ ComputeShader(V_SimParticlesCS, 64) V_SharedFrame frame = G_Dereference(V_ShaderConst_Frame)[0]; Texture2D tiles = G_Dereference(frame.tiles); RWStructuredBuffer particles = G_Dereference(frame.particles); - RWTexture2D stains = G_Dereference(frame.stains); + RWTexture2D stain_cells = G_Dereference(frame.stain_cells); RWTexture2D ground_cells = G_Dereference(frame.ground_cells); RWTexture2D air_cells = G_Dereference(frame.air_cells); + RWTexture2D stain_densities = G_Dereference(frame.stain_densities); RWTexture2D ground_densities = G_Dereference(frame.ground_densities); RWTexture2D air_densities = G_Dereference(frame.air_densities); RWTexture2D drynesses = G_Dereference(frame.drynesses); @@ -230,6 +298,16 @@ ComputeShader(V_SimParticlesCS, 64) if (particle.kind > V_ParticleKind_None && particle.kind < V_ParticleKind_COUNT) { + V_ParticleDesc desc = V_DescFromParticleKind((V_ParticleKind)particle.kind); + V_ParticleFlag flags = desc.flags; + + u32 packed = 0; + packed |= (particle_idx & ((1 >> 24) - 1)) << 0; + packed |= (particle.kind & 0xFF) << 24; + packed |= 1 << 31; + StaticAssert(V_ParticlesCap <= (1 << 24)); // particle idx must fit in 24 bits + StaticAssert(V_ParticleKind_COUNT <= 0x7F); // particle kind must fit in 7 bits + ////////////////////////////// //- Move @@ -257,7 +335,7 @@ ComputeShader(V_SimParticlesCS, 64) t_max *= inv_delta; t_max = abs(t_max); - f32 t_hit = 1; + f32 t_hit = 0; Vec2I32 cell_pos = cell_p0; @@ -269,6 +347,13 @@ ComputeShader(V_SimParticlesCS, 64) b32 done = 0; for (u32 iteration_idx = 0; iteration_idx < max_iterations && !done; ++iteration_idx) { + Vec2 cell_screen_pos_p0 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(floor(cell_pos), 1)), 1)); + Vec2 cell_screen_pos_p1 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(ceil(cell_pos), 1)), 1)); + cell_screen_pos_p1 = max(cell_screen_pos_p1, cell_screen_pos_p0 + 1); + + b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells)); + b32 is_visible = all(cell_screen_pos_p1 >= 0) && all(cell_screen_pos_p0 < frame.screen_dims); + if (cell_pos.x == cell_p1.x && cell_pos.y == cell_p1.y) { done = 1; @@ -290,20 +375,58 @@ ComputeShader(V_SimParticlesCS, 64) stepped_y = 1; } - if (all(cell_pos >= 0) && all(cell_pos < countof(occluders))) + if (is_in_world) { + // Check collision P_OccluderKind occluder = (P_OccluderKind)occluders[cell_pos]; if (occluder != P_OccluderKind_None) { done = 1; collision = 1; - particle.velocity *= 0.5; + // particle.velocity *= 0.5; + } + + + // Draw + { + b32 should_stain = 0; + b32 should_draw_ground = !collision && is_visible && AnyBit(flags, V_ParticleFlag_Ground); + b32 should_draw_air = !collision && is_visible && AnyBit(flags, V_ParticleFlag_Air); + if (AnyBit(flags, V_ParticleFlag_Stain)) + { + should_stain = 1; + } + + if (should_stain) + { + // InterlockedMax(stain_cells[cell_pos], packed); + // InterlockedAdd(stain_densities[cell_pos], 1); + } + + if (should_draw_ground) + { + InterlockedMax(ground_cells[cell_pos], packed); + InterlockedAdd(ground_densities[cell_pos], 1); + } + + if (should_draw_air) + { + InterlockedMax(air_cells[cell_pos], packed); + InterlockedAdd(air_densities[cell_pos], 1); + } } - stains[cell_pos] = Color_Black; + // stain_cells[cell_pos] = LinearFromSrgb(Vec4(0.5, 0.1, 0.1, 1)); + // stain_cells[cell_pos] = Color_Black; // ground_cells[cell_pos] = Color_White; + + // if (should_stain) + // { + // InterlockedMax(ground_cells[cell_pos], packed); + // InterlockedAdd(ground_densities[cell_pos], 1); + // } } else { @@ -347,45 +470,48 @@ ComputeShader(V_SimParticlesCS, 64) ////////////////////////////// //- Commit + // { + // Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); + // Vec2 cell_screen_pos_p0 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(floor(cell_pos), 1)), 1)); + // Vec2 cell_screen_pos_p1 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(ceil(cell_pos), 1)), 1)); + // cell_screen_pos_p1 = max(cell_screen_pos_p1, cell_screen_pos_p0 + 1); + + // b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells)); + // b32 is_visible = all(cell_screen_pos_p1 >= 0) && all(cell_screen_pos_p0 < frame.screen_dims); + + // b32 should_draw_ground = is_in_world && is_visible && AnyBit(flags, V_ParticleFlag_Ground); + // b32 should_draw_air = is_in_world && is_visible && AnyBit(flags, V_ParticleFlag_Air); + + // // Draw + // if (should_draw_ground || should_draw_air) + // { + // if (should_draw_ground) + // { + // InterlockedMax(ground_cells[cell_pos], packed); + // InterlockedAdd(ground_densities[cell_pos], 1); + // } + + // if (should_draw_air) + // { + // InterlockedMax(air_cells[cell_pos], packed); + // InterlockedAdd(air_densities[cell_pos], 1); + // } + // } + + // // Prune + // if (!is_in_world) + // { + // particle.kind = V_ParticleKind_None; + // } + + // // Increment life + // particle.life += frame.dt; + // } + + { Vec2 cell_pos = mul(frame.af.world_to_cell, Vec3(particle.pos, 1)); - Vec2 cell_screen_pos_p0 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(floor(cell_pos), 1)), 1)); - Vec2 cell_screen_pos_p1 = mul(frame.af.world_to_screen, Vec3(mul(frame.af.cell_to_world, Vec3(ceil(cell_pos), 1)), 1)); - cell_screen_pos_p1 = max(cell_screen_pos_p1, cell_screen_pos_p0 + 1); - b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(air_cells)); - b32 is_visible = all(cell_screen_pos_p1 >= 0) && all(cell_screen_pos_p0 < frame.screen_dims); - - // TODO: Particle based flags - b32 is_stain_particle = particle.kind == V_ParticleKind_Debris || particle.kind == V_ParticleKind_Test; - b32 is_ground_particle = particle.kind == V_ParticleKind_Debris || particle.kind == V_ParticleKind_Test; - b32 is_air_particle = particle.kind == V_ParticleKind_Smoke; - - b32 should_stain = is_stain_particle && is_in_world; - b32 should_draw_ground = is_ground_particle && is_in_world && (is_visible || should_stain); - b32 should_draw_air = is_air_particle && is_in_world && is_visible; - - // Draw - if (should_draw_ground || should_draw_air) - { - u32 packed = 0; - packed |= (particle_idx & ((1 >> 24) - 1)) << 0; - packed |= (particle.kind & 0xFF) << 24; - StaticAssert(V_ParticlesCap <= (1 << 24)); // particle idx must fit in 24 bits - StaticAssert(V_ParticleKind_COUNT <= 0xFF); // particle kind must fit in 8 bits - - if (should_draw_ground) - { - InterlockedMax(ground_cells[cell_pos], packed); - InterlockedAdd(ground_densities[cell_pos], 1); - } - - if (should_draw_air) - { - InterlockedMax(air_cells[cell_pos], packed); - InterlockedAdd(air_densities[cell_pos], 1); - } - } // Prune if (!is_in_world) @@ -614,7 +740,7 @@ ComputeShader2D(V_ShadeCS, 8, 8) RWTexture2D shade_tex = G_Dereference(frame.shade_rw); Texture2D albedo_tex = G_Dereference(frame.albedo_ro); Texture2D tiles = G_Dereference(frame.tiles); - RWTexture2D stains = G_Dereference(frame.stains); + RWTexture2D stain_cells = G_Dereference(frame.stain_cells); RWTexture2D drynesses = G_Dereference(frame.drynesses); SamplerState clamp_sampler = G_Dereference(frame.pt_clamp_sampler); @@ -626,7 +752,7 @@ ComputeShader2D(V_ShadeCS, 8, 8) P_TileKind tile = tiles[tile_pos]; Vec2 half_world_dims = Vec2(P_WorldPitch, P_WorldPitch) * 0.5; - b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(stains)); + b32 is_in_world = all(cell_pos >= 0) && all(cell_pos < countof(stain_cells)); ////////////////////////////// //- Compute result @@ -664,8 +790,9 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input) V_SharedFrame frame = G_Dereference(V_ShaderConst_Frame)[0]; // Texture2D shade_tex = G_Dereference(frame.shade_ro); Texture2D albedo_tex = G_Dereference(frame.albedo_ro); - RWTexture2D stains = G_Dereference(frame.stains); + RWTexture2D stain_cells = G_Dereference(frame.stain_cells); RWTexture2D ground_cells = G_Dereference(frame.ground_cells); + RWTexture2D stain_densities = G_Dereference(frame.stain_densities); RWTexture2D air_cells = G_Dereference(frame.air_cells); RWTexture2D ground_densities = G_Dereference(frame.ground_densities); RWTexture2D air_densities = G_Dereference(frame.air_densities); @@ -705,201 +832,154 @@ PixelShader(V_CompositePS, V_CompositePSOutput, V_CompositePSInput input) // } ////////////////////////////// - //- Albedo + //- Tile - Vec4 albedo_color = 0; + // TODO: Remove this + + b32 tile_is_wall = 0; + Vec4 tile_color = 0; { - ////////////////////////////// - //- Tile + P_TileKind tile_tl = tiles[Vec2(tile_pos.x - 0.99, tile_pos.y - 0.99)]; + P_TileKind tile_tr = tiles[Vec2(tile_pos.x + 0.99, tile_pos.y - 0.99)]; + P_TileKind tile_br = tiles[Vec2(tile_pos.x + 0.99, tile_pos.y + 0.99)]; + P_TileKind tile_bl = tiles[Vec2(tile_pos.x - 0.99, tile_pos.y + 0.99)]; + P_TileKind tile_t = tiles[Vec2(tile_pos.x, tile_pos.y - 0.99)]; + P_TileKind tile_r = tiles[Vec2(tile_pos.x + 0.99, tile_pos.y)]; + P_TileKind tile_b = tiles[Vec2(tile_pos.x, tile_pos.y + 0.99)]; + P_TileKind tile_l = tiles[Vec2(tile_pos.x - 0.99, tile_pos.y)]; - // TODO: Remove this + f32 tile_edge_dist = Inf; + P_TileKind edge_tile = tile; + if (tile_tl != tile) { edge_tile = tile_tl; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(floor(tile_pos.x), floor(tile_pos.y)))); } + if (tile_tr != tile) { edge_tile = tile_tr; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(ceil(tile_pos.x), floor(tile_pos.y)))); } + if (tile_br != tile) { edge_tile = tile_br; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(ceil(tile_pos.x), ceil(tile_pos.y)))); } + if (tile_bl != tile) { edge_tile = tile_bl; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(floor(tile_pos.x), ceil(tile_pos.y)))); } + if (tile_l != tile) { edge_tile = tile_l; tile_edge_dist = min(tile_edge_dist, frac(tile_pos.x)); } + if (tile_r != tile) { edge_tile = tile_r; tile_edge_dist = min(tile_edge_dist, 1.0 - frac(tile_pos.x)); } + if (tile_t != tile) { edge_tile = tile_t; tile_edge_dist = min(tile_edge_dist, frac(tile_pos.y)); } + if (tile_b != tile) { edge_tile = tile_b; tile_edge_dist = min(tile_edge_dist, 1.0 - frac(tile_pos.y)); } - b32 tile_is_wall = 0; - Vec4 tile_color = 0; + if (tile == P_TileKind_Wall) { - P_TileKind tile_tl = tiles[Vec2(tile_pos.x - 0.99, tile_pos.y - 0.99)]; - P_TileKind tile_tr = tiles[Vec2(tile_pos.x + 0.99, tile_pos.y - 0.99)]; - P_TileKind tile_br = tiles[Vec2(tile_pos.x + 0.99, tile_pos.y + 0.99)]; - P_TileKind tile_bl = tiles[Vec2(tile_pos.x - 0.99, tile_pos.y + 0.99)]; - P_TileKind tile_t = tiles[Vec2(tile_pos.x, tile_pos.y - 0.99)]; - P_TileKind tile_r = tiles[Vec2(tile_pos.x + 0.99, tile_pos.y)]; - P_TileKind tile_b = tiles[Vec2(tile_pos.x, tile_pos.y + 0.99)]; - P_TileKind tile_l = tiles[Vec2(tile_pos.x - 0.99, tile_pos.y)]; - - f32 tile_edge_dist = Inf; - P_TileKind edge_tile = tile; - if (tile_tl != tile) { edge_tile = tile_tl; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(floor(tile_pos.x), floor(tile_pos.y)))); } - if (tile_tr != tile) { edge_tile = tile_tr; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(ceil(tile_pos.x), floor(tile_pos.y)))); } - if (tile_br != tile) { edge_tile = tile_br; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(ceil(tile_pos.x), ceil(tile_pos.y)))); } - if (tile_bl != tile) { edge_tile = tile_bl; tile_edge_dist = min(tile_edge_dist, length(tile_pos - Vec2(floor(tile_pos.x), ceil(tile_pos.y)))); } - if (tile_l != tile) { edge_tile = tile_l; tile_edge_dist = min(tile_edge_dist, frac(tile_pos.x)); } - if (tile_r != tile) { edge_tile = tile_r; tile_edge_dist = min(tile_edge_dist, 1.0 - frac(tile_pos.x)); } - if (tile_t != tile) { edge_tile = tile_t; tile_edge_dist = min(tile_edge_dist, frac(tile_pos.y)); } - if (tile_b != tile) { edge_tile = tile_b; tile_edge_dist = min(tile_edge_dist, 1.0 - frac(tile_pos.y)); } - - if (tile == P_TileKind_Wall) - { - Vec4 outer = LinearFromSrgb(Vec4(0.05, 0.05, 0.05, 1)); - Vec4 inner = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1)); - tile_color = lerp(outer, inner, smoothstep(0, 1, tile_edge_dist / 0.375)); - tile_is_wall = 1; - } - else if (tile != P_TileKind_Empty) - { - V_TileDesc tile_desc = frame.tile_descs[tile]; - Texture2D tile_tex = G_Dereference(tile_desc.tex); - Vec2 tile_samp_uv = lerp(tile_desc.tex_slice_uv.p0, tile_desc.tex_slice_uv.p1, frac(world_pos)); - tile_color = tile_tex.SampleLevel(clamp_sampler, tile_samp_uv, 0); - } - // Checkered grid - else if (tile == P_TileKind_Empty) - { - i32 color_idx = 0; - Vec4 colors[2] = { - LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1)), - LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1)) - }; - const f32 checker_size = 0.5; - Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(checker_size * 2, checker_size * 2)); - if (world_pos_modded.x < checker_size) - { - color_idx = !color_idx; - } - if (world_pos_modded.y < checker_size) - { - color_idx = !color_idx; - } - if (world_pos.x < 0) - { - color_idx = !color_idx; - } - if (world_pos.y < 0) - { - color_idx = !color_idx; - } - tile_color = colors[color_idx]; - } + Vec4 outer = LinearFromSrgb(Vec4(0.05, 0.05, 0.05, 1)); + Vec4 inner = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1)); + tile_color = lerp(outer, inner, smoothstep(0, 1, tile_edge_dist / 0.375)); + tile_is_wall = 1; } - - ////////////////////////////// - //- Stain - - Vec4 stain_color = 0; + else if (tile != P_TileKind_Empty) { - f32 dryness = drynesses[cell_pos]; - Vec4 stain = stains[cell_pos]; - stain_color = V_DryColor(stain, dryness); - stain_color.rgb *= 1.0 - (0.75 * tile_is_wall); // Darken wall stains + V_TileDesc tile_desc = frame.tile_descs[tile]; + Texture2D tile_tex = G_Dereference(tile_desc.tex); + Vec2 tile_samp_uv = lerp(tile_desc.tex_slice_uv.p0, tile_desc.tex_slice_uv.p1, frac(world_pos)); + tile_color = tile_tex.SampleLevel(clamp_sampler, tile_samp_uv, 0); } - - ////////////////////////////// - //- Albedo tex - - Vec4 albedo_tex_color = albedo_tex[screen_pos]; - - ////////////////////////////// - //- Compose albedo - - if (!tile_is_wall) + // Checkered grid + else if (tile == P_TileKind_Empty) { - albedo_color = BlendPremul(tile_color, albedo_color); // Blend floor tile - albedo_color = BlendPremul(stain_color, albedo_color); // Blend floor stain - } - albedo_color = BlendPremul(albedo_tex_color, albedo_color); - if (tile_is_wall) - { - albedo_color = BlendPremul(tile_color, albedo_color); // Blend wall tile - albedo_color = BlendPremul(stain_color, albedo_color); // Blend wall stain + i32 color_idx = 0; + Vec4 colors[2] = { + LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1)), + LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1)) + }; + const f32 checker_size = 0.5; + Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(checker_size * 2, checker_size * 2)); + if (world_pos_modded.x < checker_size) + { + color_idx = !color_idx; + } + if (world_pos_modded.y < checker_size) + { + color_idx = !color_idx; + } + if (world_pos.x < 0) + { + color_idx = !color_idx; + } + if (world_pos.y < 0) + { + color_idx = !color_idx; + } + tile_color = colors[color_idx]; } } ////////////////////////////// - //- Ground particle + //- Albedo tex - // TODO: Remove this + Vec4 albedo_tex_color = albedo_tex[screen_pos]; + ////////////////////////////// + //- Particles + + Vec4 stain_particle_color = 0; Vec4 ground_particle_color = 0; - { - Vec4 color = 0; - { - u32 packed = ground_cells[cell_pos]; - V_ParticleKind kind = (V_ParticleKind)((packed >> 24) & 0xFF); - if (kind != V_ParticleKind_None) - { - u32 particle_idx = packed & ((1 << 24) - 1); - if (particle_idx < V_ParticlesCap) - { - u64 seed = MixU64(P_ParticleCompositeBasis ^ particle_idx); - f32 rand_color = Norm16(seed >> 0); - if (kind == V_ParticleKind_Debris || kind == V_ParticleKind_Test) - { - color.rgb = Color_Orange.rgb; - } - else if (kind == V_ParticleKind_Smoke) - { - color.rgb = Vec3(0.15, 0.15, 0.15); - } - color.rgb += (rand_color - 0.5) * 0.025; - { - f32 density = ground_densities[cell_pos]; - // f32 t = saturate(density / 10.0); - f32 t = smoothstep(-10, 32, density); - color.a = lerp(0, 0.85, t); - } - } - } - } - ground_particle_color = color; - ground_particle_color.rgb *= ground_particle_color.a; - } - - ////////////////////////////// - //- Air particle - - // TODO: Remove this - Vec4 air_particle_color = 0; { - Vec4 color = 0; + //- Stain { - u32 packed = air_cells[cell_pos]; - V_ParticleKind kind = (V_ParticleKind)((packed >> 24) & 0xFF); - if (kind != V_ParticleKind_None) { - u32 particle_idx = packed & ((1 << 24) - 1); - if (particle_idx < V_ParticlesCap) + u32 packed = stain_cells[cell_pos]; + V_ParticleKind particle_kind = (V_ParticleKind)((packed >> 24) & 0x7F); + if (particle_kind != V_ParticleKind_None) { - u64 seed = MixU64(P_ParticleCompositeBasis ^ particle_idx); - f32 rand_color = Norm16(seed >> 0); - if (kind == V_ParticleKind_Debris || kind == V_ParticleKind_Test) - { - color.rgb = Color_Orange.rgb; - } - else if (kind == V_ParticleKind_Smoke) - { - color.rgb = Vec3(0.15, 0.15, 0.15); - } - color.rgb += (rand_color - 0.5) * 0.025; - { - f32 density = air_densities[cell_pos]; - // f32 t = saturate(density / 10.0); - f32 t = smoothstep(-10, 32, density); - color.a = lerp(0, 0.85, t); - } + u32 particle_idx = packed & ((1 << 24) - 1); + u32 density = stain_densities[cell_pos]; + f32 dryness = drynesses[cell_pos]; + stain_particle_color = V_ColorFromParticle(particle_kind, particle_idx, density, dryness); } } + stain_particle_color.rgb *= 1.0 - (0.75 * tile_is_wall); // Darken wall stains + stain_particle_color.rgb *= stain_particle_color.a; + } + //- Ground + { + { + u32 packed = ground_cells[cell_pos]; + V_ParticleKind particle_kind = (V_ParticleKind)((packed >> 24) & 0x7F); + if (particle_kind != V_ParticleKind_None) + { + u32 particle_idx = packed & ((1 << 24) - 1); + u32 density = ground_densities[cell_pos]; + ground_particle_color = V_ColorFromParticle(particle_kind, particle_idx, density, 0); + } + } + ground_particle_color.rgb *= ground_particle_color.a; + } + //- Air + { + { + u32 packed = air_cells[cell_pos]; + V_ParticleKind particle_kind = (V_ParticleKind)((packed >> 24) & 0x7F); + if (particle_kind != V_ParticleKind_None) + { + u32 particle_idx = packed & ((1 << 24) - 1); + u32 density = air_densities[cell_pos]; + air_particle_color = V_ColorFromParticle(particle_kind, particle_idx, density, 0); + } + } + air_particle_color.rgb *= air_particle_color.a; } - air_particle_color = color; - air_particle_color.rgb *= air_particle_color.a; } - ////////////////////////////// //- Compose world // world_color = BlendPremul(shade_color, world_color); - world_color = BlendPremul(albedo_color, world_color); + world_color = BlendPremul(stain_particle_color, world_color); world_color = BlendPremul(ground_particle_color, world_color); + if (!tile_is_wall) + { + world_color = BlendPremul(tile_color, world_color); // Blend ground tile + world_color = BlendPremul(stain_particle_color, world_color); // Blend ground stain + world_color = BlendPremul(ground_particle_color, world_color); // Blend ground particle + } + world_color = BlendPremul(albedo_tex_color, world_color); + if (tile_is_wall) + { + world_color = BlendPremul(tile_color, world_color); // Blend wall tile + world_color = BlendPremul(stain_particle_color, world_color); // Blend wall stain + world_color = BlendPremul(ground_particle_color, world_color); // Blend wall particle + } world_color = BlendPremul(air_particle_color, world_color); } diff --git a/src/pp/pp_vis/pp_vis_gpu.gh b/src/pp/pp_vis/pp_vis_gpu.gh index 9c0407ac..37b5ca9a 100644 --- a/src/pp/pp_vis/pp_vis_gpu.gh +++ b/src/pp/pp_vis/pp_vis_gpu.gh @@ -5,6 +5,7 @@ Struct(V_QuadPSInput) { Semantic(Vec4, sv_position); Semantic(nointerpolation u32, quad_idx); + Semantic(Vec2, world_pos); Semantic(Vec2, samp_uv); }; @@ -44,7 +45,7 @@ Struct(V_DVertPSOutput) //~ Helpers f32 V_RandFromPos(Vec3 pos); -Vec4 V_DryColor(Vec4 color, f32 dryness); +Vec4 V_ColorFromParticle(V_ParticleKind particle_kind, u32 particle_idx, u32 density, f32 dryness); //////////////////////////////////////////////////////////// //~ Shaders diff --git a/src/pp/pp_vis/pp_vis_shared.cg b/src/pp/pp_vis/pp_vis_shared.cg new file mode 100644 index 00000000..625d6cdf --- /dev/null +++ b/src/pp/pp_vis/pp_vis_shared.cg @@ -0,0 +1,42 @@ +//////////////////////////////////////////////////////////// +//~ Helpers + +V_ParticleDesc V_DescFromParticleKind(V_ParticleKind kind) +{ + if (kind < 0 || kind >= V_ParticleKind_COUNT) + { + kind = V_ParticleKind_None; + } + + V_ParticleDesc result; + { + PERSIST Readonly V_ParticleFlag flags[V_ParticleKind_COUNT] = { + #define X(name, flags, r, g, b, a) flags, + V_ParticlesXList(X) + #undef X + }; + PERSIST Readonly f32 r[V_ParticleKind_COUNT] = { + #define X(name, flags, r, g, b, a) r, + V_ParticlesXList(X) + #undef X + }; + PERSIST Readonly f32 g[V_ParticleKind_COUNT] = { + #define X(name, flags, r, g, b, a) g, + V_ParticlesXList(X) + #undef X + }; + PERSIST Readonly f32 b[V_ParticleKind_COUNT] = { + #define X(name, flags, r, g, b, a) b, + V_ParticlesXList(X) + #undef X + }; + PERSIST Readonly f32 a[V_ParticleKind_COUNT] = { + #define X(name, flags, r, g, b, a) a, + V_ParticlesXList(X) + #undef X + }; + result.flags = flags[kind]; + result.color = LinearFromSrgb(VEC4(r[kind], g[kind], b[kind], a[kind])); + } + return result; +} diff --git a/src/pp/pp_vis/pp_vis_shared.cgh b/src/pp/pp_vis/pp_vis_shared.cgh index 6c3eaf89..7caed4a2 100644 --- a/src/pp/pp_vis/pp_vis_shared.cgh +++ b/src/pp/pp_vis/pp_vis_shared.cgh @@ -146,9 +146,10 @@ Struct(V_SharedFrame) G_StructuredBufferRef emitters; G_RWStructuredBufferRef particles; - G_RWTexture2DRef stains; + G_RWTexture2DRef stain_cells; G_RWTexture2DRef ground_cells; G_RWTexture2DRef air_cells; + G_RWTexture2DRef stain_densities; G_RWTexture2DRef ground_densities; G_RWTexture2DRef air_densities; G_RWTexture2DRef drynesses; @@ -164,6 +165,7 @@ Struct(V_SharedFrame) Enum(P_OccluderKind) { P_OccluderKind_None, + P_OccluderKind_Guy, P_OccluderKind_Wall, }; @@ -171,24 +173,58 @@ Enum(P_OccluderKind) //~ Particle types #define P_ParticleSimBasis 0xb49f2d9e406873b9ull -#define P_ParticleCompositeBasis 0x569aa8341ecc0ea3ull +#define P_ParticleColorBasis 0x569aa8341ecc0ea3ull + +Enum(V_ParticleFlag) +{ + V_ParticleFlag_None = 0, + V_ParticleFlag_Stain = (1 << 0), + V_ParticleFlag_Ground = (1 << 1), + V_ParticleFlag_Air = (1 << 2), +}; + +// NOTE: Higher particle enum values take priority over lower ones +#define V_ParticlesXList(X) \ + X( \ + None, \ + V_ParticleFlag_None, \ + 0, 0, 0, 0 \ + ) \ + /* Ground particles */ \ + X( \ + Blood, \ + V_ParticleFlag_Stain, \ + 0.5, 0.1, 0.1, 0.5 \ + ) \ + X( \ + Debris, \ + V_ParticleFlag_Ground | V_ParticleFlag_Stain, \ + 1, 0.5, 0, 1 \ + ) \ + /* Air particles */ \ + X( \ + Smoke, \ + V_ParticleFlag_Air, \ + 0.15, 0.15, 0.15, 0.5 \ + ) \ + X( \ + BulletTrail, \ + V_ParticleFlag_Air, \ + 1, 0, 1, 1 \ + ) \ + /* Test particles */ \ + X( \ + Test, \ + V_ParticleFlag_Stain, \ + 1, 1, 0, 1 \ + ) \ +/* -------------------------------------------------------------------------------- */ -// NOTE: Higher particle kinds draw over lower ones Enum(V_ParticleKind) { - V_ParticleKind_None, - - //- Ground particles - V_ParticleKind_Blood, - V_ParticleKind_Debris, - - //- Air particles - V_ParticleKind_Smoke, - V_ParticleKind_BulletTrail, - - //- Test particles - V_ParticleKind_Test, - + #define X(name, ...) V_ParticleKind_##name, + V_ParticlesXList(X) + #undef X V_ParticleKind_COUNT, }; @@ -214,6 +250,12 @@ Struct(V_Particle) Vec2 velocity; }; +Struct(V_ParticleDesc) +{ + V_ParticleFlag flags; + Vec4 color; +}; + #if IsCpu Struct(V_EmitterNode) { @@ -233,6 +275,7 @@ Enum(V_QuadFlag) Struct(V_Quad) { V_QuadFlag flags; + P_OccluderKind occluder; Affine quad_uv_to_world_af; G_Texture2DRef tex; Rng2 tex_slice_uv; @@ -252,3 +295,5 @@ Struct(V_DVert) #define V_ThreadGroupSizeFromBufferSize(buffer_size) VEC3I32((((buffer_size) + 63) / 64), 1, 1) #define V_ThreadGroupSizeFromTexSize(tex_size) VEC3I32(((tex_size).x + 7) / 8, ((tex_size).y + 7) / 8, 1) + +V_ParticleDesc V_DescFromParticleKind(V_ParticleKind kind);