bloomy bullet particles

This commit is contained in:
jacob 2026-04-04 07:09:38 -05:00
parent faf033bf5a
commit 2a3a446fdc
5 changed files with 253 additions and 128 deletions

View File

@ -3212,7 +3212,7 @@ void P_StepFrame(P_Frame *frame)
f32 speed_falloff = 0;
if (weapon->is_uzi)
{
initial_speed = TweakFloat("Bullet speed", 50, 1, 100);
initial_speed = TweakFloat("Bullet speed", 75, 1, 100);
}
else if (weapon->is_launcher)
{

View File

@ -3018,6 +3018,7 @@ void V_TickForever(WaveLaneCtx *lane)
{
if (event->is_first_observation)
{
f64 seconds_since_observation = SecondsFromNs(frame->time_ns - event->initial_observation_time_ns);
u64 angle_offset_basis = 0xf55070bae6e1d70c;
f32 rand_angle = (Norm24(MixU64s(event->key.v, angle_offset_basis)) * 2 - 1);
@ -3033,51 +3034,159 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2 p0 = event->trail_p0;
Vec2 p1 = event->trail_p1;
//- Smoke trail particles
f32 trail_len = Vec2Len(SubVec2(p1, p0));
Vec2 trail_velocity = MulVec2(SubVec2(p1, p0), SIM_TICKS_PER_SECOND);
f32 trail_speed = Vec2Len(trail_velocity);
//- Bullet smoke
{
f32 particles_count = trail_len * frame->dt * Kibi(16); // Particles per meter per second
particles_count = MaxF32(particles_count, 1);
f32 angle = AngleFromVec2(PerpVec2(SubVec2(p1, p0)));
// f32 angle = AngleFromVec2(NegVec2(SubVec2(p1, p0)));
V_Emitter emitter = Zi;
{
emitter.kind = V_ParticleKind_BulletSmoke;
emitter.count = particles_count;
f32 angle_spread = Tau / 4.0;
emitter.angle.min = angle - angle_spread / 2;
emitter.angle.max = angle + angle_spread / 2;
emitter.pos.p0 = p0;
emitter.pos.p1 = p1;
// emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
emitter.speed.min = -1;
emitter.speed.max = 1;
}
V_PushParticles(emitter);
}
//- Bullet trail
// {
// f32 trail_len = Vec2Len(SubVec2(p1, p0));
// f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second
// particles_count = MaxF32(particles_count, 1);
// f32 angle = AngleFromVec2(PerpVec2(SubVec2(p1, p0)));
// // f32 angle = AngleFromVec2(NegVec2(SubVec2(p1, p0)));
// V_Emitter emitter = Zi;
// {
// emitter.kind = V_ParticleKind_BulletTrail;
// emitter.count = particles_count;
// emitter.kind = V_ParticleKind_BulletTrail;
// f32 particles_count = trail_len * P_CellsPerMeter;
// emitter.count = particles_count;
// f32 angle_spread = Tau / 4.0;
// f32 angle = AngleFromVec2(SubVec2(p1, p0));
// emitter.angle.min = angle - angle_spread / 2;
// emitter.angle.max = angle + angle_spread / 2;
// // f32 angle_spread = Tau / 4.0;
// emitter.pos.p0 = p0;
// emitter.pos.p1 = p1;
// // emitter.angle.min = angle - angle_spread / 2;
// // emitter.angle.max = angle + angle_spread / 2;
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// emitter.angle.min =
// emitter.angle.max = angle;
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
// emitter.pos.p0 = p0;
// emitter.pos.p1 = p1;
// // emitter.speed.min =
// // emitter.speed.max =
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
// // emitter.speed.min =
// // emitter.speed.max = Vec2Len(SubVec2(p1, p0)) / frame->dt;
// emitter.speed.min = -1;
// emitter.speed.max = 1;
// }
// V_PushParticles(emitter);
// }
// //- Bullet particle
// {
// V_Emitter emitter = Zi;
// emitter.kind = V_ParticleKind_Bullet;
// // emitter.count = MaxF32(1000 * frame->dt, 1);
// emitter.count = 1;
// f32 angle = AngleFromVec2(SubVec2(p1, p0));
// // f32 angle_spread = Tau / 4.0;
// // emitter.angle.min = angle - angle_spread / 2;
// // emitter.angle.max = angle + angle_spread / 2;
// emitter.angle.min =
// emitter.angle.max = angle;
// emitter.pos.p0 =
// emitter.pos.p1 = p1;
// // emitter.speed.min =
// // emitter.speed.max =
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
// emitter.speed.min =
// emitter.speed.max = Vec2Len(SubVec2(p1, p0)) / frame->dt;
// V_PushParticles(emitter);
// }
if (event->is_first_trail)
{
//- Muzzle spark particles
//- Bullet particle
{
V_Emitter emitter = Zi;
emitter.kind = V_ParticleKind_Flash;
emitter.kind = V_ParticleKind_Bullet;
// emitter.count = MaxF32(1000 * frame->dt, 1);
emitter.count = 1;
f32 angle = AngleFromVec2(SubVec2(p1, p0));
// f32 angle_spread = Tau / 4.0;
// emitter.angle.min = angle - angle_spread / 2;
// emitter.angle.max = angle + angle_spread / 2;
emitter.angle.min =
emitter.angle.max = angle;
emitter.pos.p0 =
emitter.pos.p1 = p0;
// emitter.speed.min =
// emitter.speed.max =
// emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
emitter.speed.min =
emitter.speed.max = trail_speed;
V_PushParticles(emitter);
}
//- Wide muzzle flash particles
{
V_Emitter emitter = Zi;
emitter.kind = V_ParticleKind_MuzzleWide;
// emitter.count = MaxF32(1000 * frame->dt, 1);
// emitter.count = 128;
emitter.count = 8;
f32 angle = AngleFromVec2(SubVec2(p1, p0));
f32 angle_spread = Tau / 2;
f32 angle_spread = Tau / 3;
emitter.angle.min = angle - angle_spread / 2;
emitter.angle.max = angle + angle_spread / 2;
@ -3091,7 +3200,7 @@ void V_TickForever(WaveLaneCtx *lane)
// emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
emitter.speed.min = 5;
emitter.speed.min = 10;
emitter.speed.max = 20;
// emitter.angle.min = angle - angle_spread / 2;
@ -3116,53 +3225,53 @@ void V_TickForever(WaveLaneCtx *lane)
}
//- Wide muzzle flash particles
{
V_Emitter emitter = Zi;
emitter.kind = V_ParticleKind_Spark;
// emitter.count = MaxF32(1000 * frame->dt, 1);
// emitter.count = 128;
emitter.count = 1;
//- Spark particles
// {
// V_Emitter emitter = Zi;
// emitter.kind = V_ParticleKind_Spark;
// // emitter.count = MaxF32(1000 * frame->dt, 1);
// // emitter.count = 128;
// emitter.count = 1;
f32 angle = AngleFromVec2(SubVec2(p1, p0));
// f32 angle = AngleFromVec2(SubVec2(p1, p0));
f32 angle_spread = Tau / 2;
emitter.angle.min = angle - angle_spread / 2;
emitter.angle.max = angle + angle_spread / 2;
// f32 angle_spread = Tau / 2;
// emitter.angle.min = angle - angle_spread / 2;
// emitter.angle.max = angle + angle_spread / 2;
emitter.pos.p0 =
emitter.pos.p1 = p0;
// emitter.pos.p0 =
// emitter.pos.p1 = p0;
// emitter.lifetime.min = 0.1
// emitter.lifetime.max = 0.5;
// // emitter.lifetime.min = 0.1
// // emitter.lifetime.max = 0.5;
// emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
emitter.speed.min = 20;
emitter.speed.max = 100;
// emitter.speed.min = 20;
// emitter.speed.max = 100;
// emitter.angle.min = angle - angle_spread / 2;
// emitter.angle.max = angle + angle_spread / 2;
// // emitter.angle.min = angle - angle_spread / 2;
// // emitter.angle.max = angle + angle_spread / 2;
// emitter.angle.min =
// emitter.angle.max = angle;
// // emitter.angle.min =
// // emitter.angle.max = angle;
emitter.pos.p0 =
emitter.pos.p1 = p0;
// emitter.pos.p0 =
// emitter.pos.p1 = p0;
// emitter.speed.min =
// emitter.speed.max =
// // emitter.speed.min =
// // emitter.speed.max =
// emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
// emitter.speed.min =
// emitter.speed.max = Vec2Len(SubVec2(p1, p0)) / frame->dt;
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
// // emitter.speed.min =
// // emitter.speed.max = Vec2Len(SubVec2(p1, p0)) / frame->dt;
V_PushParticles(emitter);
}
// V_PushParticles(emitter);
// }
@ -3173,7 +3282,7 @@ void V_TickForever(WaveLaneCtx *lane)
//- Narrow muzzle flash particles
{
V_Emitter emitter = Zi;
emitter.kind = V_ParticleKind_Flash;
emitter.kind = V_ParticleKind_MuzzleNarrow;
// emitter.count = MaxF32(1000 * frame->dt, 1);
// emitter.count = 128;
emitter.count = 1;
@ -3181,7 +3290,7 @@ void V_TickForever(WaveLaneCtx *lane)
f32 angle = AngleFromVec2(SubVec2(p1, p0));
// f32 angle_spread = Tau / 64;
f32 angle_spread = 0;
f32 angle_spread = Tau / 64;
emitter.angle.min = angle - angle_spread / 2;
emitter.angle.max = angle + angle_spread / 2;
@ -3195,8 +3304,8 @@ void V_TickForever(WaveLaneCtx *lane)
// emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
emitter.speed.min = 35;
emitter.speed.max = 35;
emitter.speed.min = 45;
emitter.speed.max = 45;
// emitter.angle.min = angle - angle_spread / 2;
// emitter.angle.max = angle + angle_spread / 2;
@ -3275,43 +3384,6 @@ void V_TickForever(WaveLaneCtx *lane)
// V_PushParticles(emitter);
// }
//- Test bullet particle
// {
// V_Emitter emitter = Zi;
// emitter.kind = V_ParticleKind_Fire;
// // emitter.count = MaxF32(1000 * frame->dt, 1);
// emitter.count = 1;
// f32 angle = AngleFromVec2(SubVec2(p1, p0));
// // f32 angle_spread = Tau / 4.0;
// // emitter.angle.min = angle - angle_spread / 2;
// // emitter.angle.max = angle + angle_spread / 2;
// emitter.angle.min =
// emitter.angle.max = angle;
// emitter.pos.p0 =
// emitter.pos.p1 = p0;
// // emitter.speed.min =
// // emitter.speed.max =
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
// emitter.speed.min =
// emitter.speed.max = Vec2Len(SubVec2(p1, p0)) / frame->dt;
// V_PushParticles(emitter);
// }
}
}

View File

@ -9,12 +9,19 @@ f32 V_RandFromPos(Vec3 pos)
return rand;
}
Vec4 V_ColorFromParticle(V_ParticleDesc desc, u32 particle_idx, f32 alive_seconds, u32 density)
f32 V_LifetimeFromParticleDesc(V_ParticleDesc desc, u32 particle_idx)
{
u64 seed = MixU64(V_ParticleLifetimeBasis ^ particle_idx);
f32 rand_lifetime = Norm16(seed >> 16);
f32 result = desc.lifetime_min + (desc.lifetime_max - desc.lifetime_min) * rand_lifetime;
return result;
}
Vec4 V_ColorFromParticleDesc(V_ParticleDesc desc, u32 particle_idx, f32 alive_seconds, u32 density)
{
Vec4 result = 0;
u64 seed = MixU64(V_ParticleColorBasis ^ particle_idx);
f32 rand_color = Norm16(seed >> 0);
f32 rand_lifetime = Norm16(seed >> 16);
result = desc.base_color;
@ -49,16 +56,12 @@ Vec4 V_ColorFromParticle(V_ParticleDesc desc, u32 particle_idx, f32 alive_second
result.rgb = result.rgb + (rand_color - 0.5) * 0.05;
if (desc.lifetime_min < Inf && alive_seconds > 0)
if (desc.flags & V_ParticleFlag_FadeLifetime && desc.lifetime_min < Inf && alive_seconds > 0)
{
f32 lifetime = desc.lifetime_min + (desc.lifetime_max - desc.lifetime_min) * rand_lifetime;
f32 lifetime = V_LifetimeFromParticleDesc(desc, particle_idx);
f32 lifetime_complete = saturate(alive_seconds / lifetime);
f32 v = lifetime_complete * lifetime_complete;
// result.a *= 1.0 - lifetime_complete;
result.a *= 1.0 - v;
// result.rgb *= 1.0 - lifetime_complete;
}
@ -171,7 +174,7 @@ ComputeShader(V_PrepareCellsCS)
u32 particle_idx = packed & ((1 << 24) - 1);
f32 alive_seconds = 0;
Vec4 base_color = V_ColorFromParticle(desc, particle_idx, 0, density);
Vec4 base_color = V_ColorFromParticleDesc(desc, particle_idx, 0, density);
Vec4 dry_color = base_color * desc.dry_factor;
base_color.rgb *= base_color.a;
@ -481,6 +484,15 @@ ComputeShader(V_SimParticlesCS)
particle.pos = lerp(emitter.pos.p0, emitter.pos.p1, rand_offset);
particle.velocity = Vec2(cos(initial_angle), sin(initial_angle)) * initial_speed;
}
else
{
V_ParticleDesc desc = V_DescFromParticleKind((V_ParticleKind)particle.kind);
particle.alive_seconds += frame.dt;
if (particle.alive_seconds > V_LifetimeFromParticleDesc(desc, particle_idx))
{
prune = 1;
}
}
//////////////////////////////
//- Simulate
@ -618,6 +630,10 @@ ComputeShader(V_SimParticlesCS)
collision = 1;
done = 1;
{
if (AnyBit(desc.flags, V_ParticleFlag_HaltOnCollision))
{
particle.velocity = Vec2(0, 0);
}
if (stepped_x)
{
if (!AnyBit(desc.flags, V_ParticleFlag_NoReflect))
@ -702,8 +718,6 @@ ComputeShader(V_SimParticlesCS)
particle.pos = p0 + (p1 - p0) * t;
}
particle.alive_seconds += frame.dt;
}
//////////////////////////////
@ -934,7 +948,7 @@ ComputeShader(V_CompositeCS)
u32 density = densities[cell_pos];
V_ParticleDesc desc = V_DescFromParticleKind(particle_kind);
Vec4 cell_color = V_ColorFromParticle(desc, particle_idx, particle.alive_seconds, density);
Vec4 cell_color = V_ColorFromParticleDesc(desc, particle_idx, particle.alive_seconds, density);
cell_color.rgb *= cell_color.a;
if (layer == V_ParticleLayer_Ground)
@ -986,7 +1000,7 @@ ComputeShader(V_CompositeCS)
// u32 density = densities[cell_pos];
// V_ParticleDesc desc = V_DescFromParticleKind(particle_kind);
// u32 particle_idx = packed & ((1 << 24) - 1);
// Vec4 cell_color = V_ColorFromParticle(desc, particle_idx, density);
// Vec4 cell_color = V_ColorFromParticleDesc(desc, particle_idx, density);
// cell_color.rgb *= cell_color.a;
// if (layer == V_ParticleLayer_Ground)
@ -1277,8 +1291,13 @@ ComputeShader(V_BloomDownCS)
f32 knee_weight = 1;
if (is_first_pass)
{
// f32 luminance = LuminanceFromColor(src);
// f32 max_rgb = max(max(src.r, src.g), src.b); // So that we can get bloom on colors with high rgb, not just high luminance
f32 luminance = LuminanceFromColor(src);
f32 max_rgb = max(max(src.r, src.g), src.b); // So that we can get bloom on colors with high rgb, not just high luminance
f32 max_rgb = -1; // So that we can get bloom on colors with high rgb, not just high luminance
f32 bright = max(luminance, (max_rgb - 1.0) * 0.5);
if (bright > 0)
{

View File

@ -45,7 +45,8 @@ Struct(V_DVertPSOutput)
//~ Helpers
f32 V_RandFromPos(Vec3 pos);
Vec4 V_ColorFromParticle(V_ParticleDesc desc, u32 particle_idx, f32 alive_seconds, u32 density);
f32 V_LifetimeFromParticleDesc(V_ParticleDesc desc, u32 particle_idx);
Vec4 V_ColorFromParticleDesc(V_ParticleDesc desc, u32 particle_idx, f32 alive_seconds, u32 density);
////////////////////////////////////////////////////////////
//~ Shaders

View File

@ -24,6 +24,7 @@ G_DeclRegister(i32, V_GpuReg_MipIdx, 4);
#define V_ParticleColorBasis 0x569aa8341ecc0ea3ull
#define V_ParticleCellBasis 0xf60c0cff344b0c5dull
#define V_ParticleStainBasis 0x3c64e8226d98d376ull
#define V_ParticleLifetimeBasis 0x4969cf8f60816abfull
Enum(V_ParticleFlag)
{
@ -32,6 +33,8 @@ Enum(V_ParticleFlag)
V_ParticleFlag_NoReflect = (1 << 2),
V_ParticleFlag_OnlyCollideWithWalls = (1 << 3),
V_ParticleFlag_GasBlend = (1 << 4),
V_ParticleFlag_FadeLifetime = (1 << 5),
V_ParticleFlag_HaltOnCollision = (1 << 6),
};
Enum(V_ParticleLayer)
@ -102,26 +105,16 @@ Enum(V_ParticleLayer)
/* Flags */ V_ParticleFlag_StainWhenPruned, \
/* Layer */ V_ParticleLayer_Mid, \
/* Stain rate, pen chance */ 0, 0, \
/* Lifetime */ 0.2, 5, \
/* Lifetime */ 0.2, 0.3, \
/* Prune speed threshold */ 0.1, \
/* Base color */ VEC4(2, 0.5, 0, 1), \
/* Dry color factor */ VEC4(0.2, 0.1, 0.0, 1) \
) \
\
/* Air particles */ \
X( \
/* Name */ BulletTrail, \
/* Flags */ V_ParticleFlag_OnlyCollideWithWalls | V_ParticleFlag_GasBlend, \
/* Layer */ V_ParticleLayer_Mid, \
/* Stain rate, pen chance */ 0, 0, \
/* Lifetime */ 0.075, 0.075, \
/* Prune speed threshold */ 0.01, \
/* Base color */ VEC4(0.8, 0.6, 0.2, 0.25), \
/* Dry color factor */ VEC4(1, 1, 1, 1) \
) \
X( \
/* Name */ Smoke, \
/* Flags */ V_ParticleFlag_OnlyCollideWithWalls | V_ParticleFlag_GasBlend, \
/* Flags */ V_ParticleFlag_OnlyCollideWithWalls | V_ParticleFlag_GasBlend | V_ParticleFlag_FadeLifetime, \
/* Layer */ V_ParticleLayer_Air, \
/* Stain rate, pen chance */ 0, 0, \
/* Lifetime */ Inf, Inf, \
@ -130,7 +123,17 @@ Enum(V_ParticleLayer)
/* Dry color factor */ VEC4(1, 1, 1, 1) \
) \
X( \
/* Name */ Flash, \
/* Name */ BulletSmoke, \
/* Flags */ V_ParticleFlag_OnlyCollideWithWalls | V_ParticleFlag_GasBlend | V_ParticleFlag_FadeLifetime, \
/* Layer */ V_ParticleLayer_Mid, \
/* Stain rate, pen chance */ 0, 0, \
/* Lifetime */ 0.075, 0.2, \
/* Prune speed threshold */ 0.00, \
/* Base color */ VEC4(0.8, 0.6, 0.2, 0.005), \
/* Dry color factor */ VEC4(1, 1, 1, 1) \
) \
X( \
/* Name */ MuzzleWide, \
/* Flags */ V_ParticleFlag_None, \
/* Layer */ V_ParticleLayer_Air, \
/* Stain rate, pen chance */ 0, 0, \
@ -138,6 +141,36 @@ Enum(V_ParticleLayer)
/* Prune speed threshold */ 0, \
/* Base color */ VEC4(10, 3.5, 0, 1), \
/* Dry color factor */ VEC4(0.2, 0.1, 0.0, 1) \
) \
X( \
/* Name */ MuzzleNarrow, \
/* Flags */ V_ParticleFlag_None, \
/* Layer */ V_ParticleLayer_Air, \
/* Stain rate, pen chance */ 0, 0, \
/* Lifetime */ 0.0, 0.05, \
/* Prune speed threshold */ 0, \
/* Base color */ VEC4(10, 3.5, 0, 1), \
/* Dry color factor */ VEC4(0.2, 0.1, 0.0, 1) \
) \
X( \
/* Name */ BulletTrail, \
/* Flags */ V_ParticleFlag_OnlyCollideWithWalls | V_ParticleFlag_FadeLifetime, \
/* Layer */ V_ParticleLayer_Air, \
/* Stain rate, pen chance */ 0, 0, \
/* Lifetime */ 0.075, 0.075, \
/* Prune speed threshold */ 0, \
/* Base color */ VEC4(3, 1.5, 0, 1), \
/* Dry color factor */ VEC4(0.2, 0.1, 0.0, 1) \
) \
X( \
/* Name */ Bullet, \
/* Flags */ V_ParticleFlag_NoReflect | V_ParticleFlag_HaltOnCollision, \
/* Layer */ V_ParticleLayer_Air, \
/* Stain rate, pen chance */ 0, 0, \
/* Lifetime */ Inf, Inf, \
/* Prune speed threshold */ 0.01, \
/* Base color */ VEC4(5, 1.75, 0.75, 1), \
/* Dry color factor */ VEC4(0.2, 0.1, 0.0, 1) \
) \
\
/* Test particles */ \