ADD: HQ particles
This commit is contained in:
@@ -1,7 +1,27 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0) uniform SceneData
|
||||
{
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 viewproj;
|
||||
} sceneData;
|
||||
|
||||
layout(location = 0) in vec4 v_color;
|
||||
layout(location = 1) in vec2 v_uv;
|
||||
layout(location = 2) in float v_view_depth;
|
||||
layout(location = 3) in float v_seed;
|
||||
|
||||
layout(set = 2, binding = 0) uniform sampler2D gbufPosTex;
|
||||
layout(set = 3, binding = 0) uniform sampler2D flipbookTex;
|
||||
layout(set = 3, binding = 1) uniform sampler2D noiseTex;
|
||||
|
||||
layout(push_constant) uniform ParticlePush
|
||||
{
|
||||
vec4 screen; // x=invW, y=invH, z=softDepthDistance, w=timeSeconds
|
||||
vec4 flipbook; // x=cols, y=rows, z=fps, w=intensity
|
||||
vec4 noise; // x=scale, y=strength, z=scrollX, w=scrollY
|
||||
} pc;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
@@ -16,6 +36,63 @@ void main()
|
||||
c.rgb *= mask;
|
||||
c.a *= mask;
|
||||
|
||||
// Flipbook sampling + noise UV distortion (atlas).
|
||||
float cols = max(pc.flipbook.x, 1.0);
|
||||
float rows = max(pc.flipbook.y, 1.0);
|
||||
float frames = cols * rows;
|
||||
float fps = max(pc.flipbook.z, 0.0);
|
||||
float intensity = max(pc.flipbook.w, 0.0);
|
||||
|
||||
vec2 uv = v_uv;
|
||||
float noiseScale = max(pc.noise.x, 0.0);
|
||||
float noiseStrength = max(pc.noise.y, 0.0);
|
||||
if (noiseScale > 0.0 && noiseStrength > 0.0)
|
||||
{
|
||||
vec2 nUV = uv * noiseScale + pc.screen.w * pc.noise.zw;
|
||||
vec2 n = texture(noiseTex, nUV).rg * 2.0 - 1.0;
|
||||
vec2 cell = vec2(1.0 / cols, 1.0 / rows);
|
||||
uv = clamp(uv + n * noiseStrength * cell, 0.0, 1.0);
|
||||
}
|
||||
|
||||
uint frame = 0u;
|
||||
if (frames > 0.5 && fps > 0.0)
|
||||
{
|
||||
float ff = pc.screen.w * fps + v_seed * frames;
|
||||
frame = uint(ff) % uint(frames);
|
||||
}
|
||||
uint cols_u = uint(cols);
|
||||
uint rows_u = uint(rows);
|
||||
uint fx = frame % cols_u;
|
||||
// Flipbook sheets are usually laid out with row 0 at the top.
|
||||
uint fy = (rows_u > 0u) ? (rows_u - 1u - (frame / cols_u)) : 0u;
|
||||
|
||||
vec2 cell = vec2(1.0 / cols, 1.0 / rows);
|
||||
vec2 atlas_uv = uv * cell + vec2(float(fx), float(fy)) * cell;
|
||||
|
||||
vec3 flip = texture(flipbookTex, atlas_uv).rgb;
|
||||
// BC6H has no alpha; approximate mask from luminance.
|
||||
float flip_a = clamp(dot(flip, vec3(0.2126, 0.7152, 0.0722)), 0.0, 1.0);
|
||||
|
||||
c.rgb *= flip * intensity;
|
||||
c.a *= flip_a;
|
||||
|
||||
// Soft particles: fade out near opaque geometry intersections.
|
||||
float soft = 1.0;
|
||||
float softDist = pc.screen.z;
|
||||
if (softDist > 0.0)
|
||||
{
|
||||
vec2 suv = gl_FragCoord.xy * pc.screen.xy;
|
||||
vec4 scenePos = texture(gbufPosTex, suv);
|
||||
if (scenePos.w > 0.0)
|
||||
{
|
||||
float sceneDepth = -(sceneData.view * vec4(scenePos.xyz, 1.0)).z;
|
||||
float delta = sceneDepth - v_view_depth;
|
||||
soft = clamp(delta / softDist, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
c.rgb *= soft;
|
||||
c.a *= soft;
|
||||
|
||||
if (c.a <= 0.001)
|
||||
{
|
||||
discard;
|
||||
@@ -23,4 +100,3 @@ void main()
|
||||
|
||||
outColor = c;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,15 @@ layout(std430, set = 1, binding = 0) readonly buffer ParticlePool
|
||||
Particle particles[];
|
||||
} pool;
|
||||
|
||||
layout(std430, set = 1, binding = 1) readonly buffer DrawIndices
|
||||
{
|
||||
uint indices[];
|
||||
} drawIndices;
|
||||
|
||||
layout(location = 0) out vec4 v_color;
|
||||
layout(location = 1) out vec2 v_uv;
|
||||
layout(location = 2) out float v_view_depth;
|
||||
layout(location = 3) out float v_seed;
|
||||
|
||||
vec2 quad_corner(uint vidx)
|
||||
{
|
||||
@@ -39,7 +46,7 @@ vec2 quad_corner(uint vidx)
|
||||
|
||||
void main()
|
||||
{
|
||||
uint particle_index = gl_InstanceIndex;
|
||||
uint particle_index = drawIndices.indices[uint(gl_InstanceIndex)];
|
||||
Particle p = pool.particles[particle_index];
|
||||
|
||||
float life = max(p.vel_life.w, 1e-6);
|
||||
@@ -61,6 +68,7 @@ void main()
|
||||
vec3 pos = p.pos_age.xyz + (cam_right * corner.x + cam_up * corner.y) * size;
|
||||
|
||||
v_color = vec4(p.color.rgb * fade, p.color.a * fade);
|
||||
v_view_depth = -(sceneData.view * vec4(pos, 1.0)).z;
|
||||
v_seed = p.misc.y;
|
||||
gl_Position = sceneData.viewproj * vec4(pos, 1.0);
|
||||
}
|
||||
|
||||
|
||||
46
shaders/particles_build_indices.comp
Normal file
46
shaders/particles_build_indices.comp
Normal file
@@ -0,0 +1,46 @@
|
||||
#version 450
|
||||
|
||||
// Output is a global indices[] buffer, indexed by gl_InstanceIndex.
|
||||
|
||||
layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
layout(std430, set = 0, binding = 0) readonly buffer SortedBlocks
|
||||
{
|
||||
uint blocks[];
|
||||
} sortedBlocks;
|
||||
|
||||
layout(std430, set = 0, binding = 1) writeonly buffer DrawIndices
|
||||
{
|
||||
uint indices[];
|
||||
} outIndices;
|
||||
|
||||
layout(push_constant) uniform Push
|
||||
{
|
||||
uvec4 header; // x=base, y=count, z=flags (bit0=identity)
|
||||
} pc;
|
||||
|
||||
const uint BLOCK_SIZE = 256u;
|
||||
|
||||
void main()
|
||||
{
|
||||
uint i = gl_GlobalInvocationID.x;
|
||||
uint count = pc.header.y;
|
||||
if (i >= count) return;
|
||||
|
||||
uint base = pc.header.x;
|
||||
uint flags = pc.header.z;
|
||||
|
||||
uint outIdx = base + i;
|
||||
uint particleIdx = base + i;
|
||||
|
||||
if ((flags & 1u) == 0u)
|
||||
{
|
||||
uint blockRank = i / BLOCK_SIZE;
|
||||
uint within = i - blockRank * BLOCK_SIZE;
|
||||
uint block = sortedBlocks.blocks[blockRank];
|
||||
particleIdx = base + block * BLOCK_SIZE + within;
|
||||
}
|
||||
|
||||
outIndices.indices[outIdx] = particleIdx;
|
||||
}
|
||||
|
||||
101
shaders/particles_sort_blocks.comp
Normal file
101
shaders/particles_sort_blocks.comp
Normal file
@@ -0,0 +1,101 @@
|
||||
#version 450
|
||||
|
||||
// Each system dispatch sorts up to 512 blocks of 256 particles by max view-space depth.
|
||||
|
||||
layout(local_size_x = 512, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
struct Particle
|
||||
{
|
||||
vec4 pos_age;
|
||||
vec4 vel_life;
|
||||
vec4 color;
|
||||
vec4 misc;
|
||||
};
|
||||
|
||||
layout(std430, set = 0, binding = 0) readonly buffer ParticlePool
|
||||
{
|
||||
Particle particles[];
|
||||
} pool;
|
||||
|
||||
layout(std430, set = 0, binding = 1) writeonly buffer SortedBlocks
|
||||
{
|
||||
uint blocks[];
|
||||
} outBlocks;
|
||||
|
||||
layout(push_constant) uniform Push
|
||||
{
|
||||
uvec4 header; // x=base, y=count
|
||||
mat4 view;
|
||||
} pc;
|
||||
|
||||
const uint BLOCK_SIZE = 256u;
|
||||
const uint MAX_BLOCKS = 512u;
|
||||
|
||||
shared float s_key[512];
|
||||
shared uint s_block[512];
|
||||
|
||||
void main()
|
||||
{
|
||||
uint tid = gl_LocalInvocationID.x;
|
||||
|
||||
uint count = pc.header.y;
|
||||
uint blockCount = (count + BLOCK_SIZE - 1u) / BLOCK_SIZE;
|
||||
blockCount = min(blockCount, MAX_BLOCKS);
|
||||
|
||||
float key = 1e20;
|
||||
if (tid < blockCount)
|
||||
{
|
||||
uint blockStart = pc.header.x + tid * BLOCK_SIZE;
|
||||
uint localCount = min(BLOCK_SIZE, count - tid * BLOCK_SIZE);
|
||||
|
||||
float maxDepth = -1e20;
|
||||
for (uint i = 0u; i < localCount; ++i)
|
||||
{
|
||||
uint idx = blockStart + i;
|
||||
vec4 viewPos = pc.view * vec4(pool.particles[idx].pos_age.xyz, 1.0);
|
||||
float depth = -viewPos.z;
|
||||
maxDepth = max(maxDepth, depth);
|
||||
}
|
||||
|
||||
// Sort ascending by -depth => farthest first.
|
||||
key = -maxDepth;
|
||||
}
|
||||
|
||||
s_key[tid] = key;
|
||||
s_block[tid] = tid;
|
||||
barrier();
|
||||
|
||||
// Bitonic sort ascending for 512 elements.
|
||||
for (uint k = 2u; k <= MAX_BLOCKS; k <<= 1u)
|
||||
{
|
||||
for (uint j = (k >> 1u); j > 0u; j >>= 1u)
|
||||
{
|
||||
uint ixj = tid ^ j;
|
||||
if (ixj > tid)
|
||||
{
|
||||
bool ascending = ((tid & k) == 0u);
|
||||
|
||||
float a = s_key[tid];
|
||||
float b = s_key[ixj];
|
||||
uint ai = s_block[tid];
|
||||
uint bi = s_block[ixj];
|
||||
|
||||
bool swap = (ascending && (a > b)) || (!ascending && (a < b));
|
||||
if (swap)
|
||||
{
|
||||
s_key[tid] = b;
|
||||
s_key[ixj] = a;
|
||||
s_block[tid] = bi;
|
||||
s_block[ixj] = ai;
|
||||
}
|
||||
}
|
||||
barrier();
|
||||
}
|
||||
}
|
||||
|
||||
if (tid < blockCount)
|
||||
{
|
||||
outBlocks.blocks[tid] = s_block[tid];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user