From c967df400be6060f96d2c03ac7559685dfd39668 Mon Sep 17 00:00:00 2001 From: turanszkij Date: Sat, 19 May 2018 14:27:06 +0100 Subject: [PATCH] emitter updates, optimize fluid simulator --- Editor/EmitterWindow.cpp | 19 +++++- Editor/EmitterWindow.h | 1 + WickedEngine/ArchiveVersionHistory.txt | 1 + WickedEngine/ShaderInterop_EmittedParticle.h | 27 ++++---- WickedEngine/emittedparticle_simulateCS.hlsl | 4 +- .../emittedparticle_sphdensityCS.hlsl | 65 +++++++++---------- WickedEngine/emittedparticle_sphforceCS.hlsl | 40 +++++++----- WickedEngine/wiArchive.cpp | 2 +- WickedEngine/wiEmittedParticle.cpp | 28 +++++++- WickedEngine/wiEmittedParticle.h | 1 + WickedEngine/wiVersion.cpp | 2 +- 11 files changed, 119 insertions(+), 71 deletions(-) diff --git a/Editor/EmitterWindow.cpp b/Editor/EmitterWindow.cpp index c024551fc..49b10fc74 100644 --- a/Editor/EmitterWindow.cpp +++ b/Editor/EmitterWindow.cpp @@ -18,7 +18,7 @@ EmitterWindow::EmitterWindow(wiGUI* gui) : GUI(gui) float screenH = (float)wiRenderer::GetDevice()->GetScreenHeight(); emitterWindow = new wiWindow(GUI, "Emitter Window"); - emitterWindow->SetSize(XMFLOAT2(800, 1024)); + emitterWindow->SetSize(XMFLOAT2(800, 1100)); emitterWindow->SetEnabled(false); GUI->AddWidget(emitterWindow); @@ -296,6 +296,22 @@ EmitterWindow::EmitterWindow(wiGUI* gui) : GUI(gui) + timestepSlider = new wiSlider(-1, 0.016f, -1, 100000, "Timestep: "); + timestepSlider->SetSize(XMFLOAT2(360, 30)); + timestepSlider->SetPos(XMFLOAT2(x, y += step*2)); + timestepSlider->OnSlide([&](wiEventArgs args) { + auto emitter = GetEmitter(); + if (emitter != nullptr) + { + emitter->FIXED_TIMESTEP = args.fValue; + } + }); + timestepSlider->SetEnabled(false); + timestepSlider->SetTooltip("Adjust timestep for emitter simulation. -1 means variable timestep, positive means fixed timestep."); + emitterWindow->AddWidget(timestepSlider); + + + //////////////// SPH //////////////////////////// @@ -463,6 +479,7 @@ void EmitterWindow::SetObject(Object* obj) emitLifeRandomnessSlider->SetValue(emitter->random_life); emitMotionBlurSlider->SetValue(emitter->motionBlurAmount); emitMassSlider->SetValue(emitter->mass); + timestepSlider->SetValue(emitter->FIXED_TIMESTEP); sph_h_Slider->SetValue(emitter->SPH_h); sph_K_Slider->SetValue(emitter->SPH_K); diff --git a/Editor/EmitterWindow.h b/Editor/EmitterWindow.h index fa2ac6616..dc72633be 100644 --- a/Editor/EmitterWindow.h +++ b/Editor/EmitterWindow.h @@ -50,6 +50,7 @@ public: wiSlider* emitLifeRandomnessSlider; wiSlider* emitMotionBlurSlider; wiSlider* emitMassSlider; + wiSlider* timestepSlider; wiSlider* sph_h_Slider; wiSlider* sph_K_Slider; wiSlider* sph_p0_Slider; diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index eba77d7bb..bbc7fa06c 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -1,5 +1,6 @@ This file contains changelog of wiArchive versions +18: serialized emitter properties: sph properties, fixed timestep 17: serialized light scattering property 16: environment probes are now serialized into Model 15: removed redundant matrix store from hair paticle serializator diff --git a/WickedEngine/ShaderInterop_EmittedParticle.h b/WickedEngine/ShaderInterop_EmittedParticle.h index ce40031a0..92e7c048e 100644 --- a/WickedEngine/ShaderInterop_EmittedParticle.h +++ b/WickedEngine/ShaderInterop_EmittedParticle.h @@ -6,9 +6,9 @@ struct Particle { float3 position; - float rotationalVelocity; - float3 force; float mass; + float3 force; + float rotationalVelocity; float3 velocity; float maxLife; float life; @@ -52,19 +52,20 @@ CBUFFER(EmittedParticleCB, CBSLOT_OTHER_EMITTEDPARTICLE) float xEmitterOpacity; uint xEmitterMaxParticleCount; - float xSPH_h; // smoothing radius - float xSPH_h_rcp; // 1.0f / smoothing radius - float xSPH_h2; // smoothing radius ^ 2 - float xSPH_h3; // smoothing radius ^ 3 + float xSPH_h; // smoothing radius + float xSPH_h_rcp; // 1.0f / smoothing radius + float xSPH_h2; // smoothing radius ^ 2 + float xSPH_h3; // smoothing radius ^ 3 - float xSPH_h6; // smoothing radius ^ 6 - float xSPH_h9; // smoothing radius ^ 9 - float xSPH_K; // pressure constant - float xSPH_p0; // reference density + float xSPH_poly6_constant; // precomputed Poly6 kernel constant term + float xSPH_spiky_constant; // precomputed Spiky kernel function constaant term + float xSPH_K; // pressure constant + float xSPH_p0; // reference density - float xSPH_e; // viscosity constant - uint xSPH_ENABLED; - float2 __padding; + float xSPH_e; // viscosity constant + uint xSPH_ENABLED; // is SPH enabled? + float xEmitterFixedTimestep; // we can force a fixed timestep (>0) onto the simulation to avoid blowing up + float __padding; }; diff --git a/WickedEngine/emittedparticle_simulateCS.hlsl b/WickedEngine/emittedparticle_simulateCS.hlsl index 1feaf7662..dd62f61a8 100644 --- a/WickedEngine/emittedparticle_simulateCS.hlsl +++ b/WickedEngine/emittedparticle_simulateCS.hlsl @@ -49,8 +49,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) if (DTid.x < aliveCount) { - //const float dt = g_xFrame_DeltaTime; - const float dt = 0.016f; // fixed time step, otherwise simulation can just blow up + // simulation can be either fixed or variable timestep: + const float dt = xEmitterFixedTimestep >= 0 ? xEmitterFixedTimestep : g_xFrame_DeltaTime; uint particleIndex = aliveBuffer_CURRENT[DTid.x]; Particle particle = particleBuffer[particleIndex]; diff --git a/WickedEngine/emittedparticle_sphdensityCS.hlsl b/WickedEngine/emittedparticle_sphdensityCS.hlsl index acc6e88d3..fd502a1e6 100644 --- a/WickedEngine/emittedparticle_sphdensityCS.hlsl +++ b/WickedEngine/emittedparticle_sphdensityCS.hlsl @@ -9,7 +9,10 @@ STRUCTUREDBUFFER(cellOffsetBuffer, uint, 4); RWSTRUCTUREDBUFFER(densityBuffer, float, 0); +#ifndef SPH_USE_ACCELERATION_GRID +// grid structure is not a good fit to exploit shared memory because one threadgroup can load from different initial cells :( groupshared float4 positions_masses[THREADCOUNT_SIMULATION]; +#endif // SPH_USE_ACCELERATION_GRID [numthreads(THREADCOUNT_SIMULATION, 1, 1)] void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, uint3 Gid : SV_GroupID ) @@ -18,8 +21,6 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui const float h = xSPH_h; // smoothing radius const float h2 = xSPH_h2; // smoothing radius ^ 2 const float h3 = xSPH_h3; // smoothing radius ^ 3 - const float h6 = xSPH_h6; // smoothing radius ^ 6 - const float h9 = xSPH_h9; // smoothing radius ^ 9 const float K = xSPH_K; // pressure constant const float p0 = xSPH_p0; // reference density const float e = xSPH_e; // viscosity constant @@ -27,17 +28,17 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui uint aliveCount = counterBuffer.Load(PARTICLECOUNTER_OFFSET_ALIVECOUNT); uint particleIndexA; - Particle particleA; + float3 positionA; if (DTid.x < aliveCount) { particleIndexA = aliveBuffer_CURRENT[DTid.x]; - particleA = particleBuffer[particleIndexA]; + positionA = particleBuffer[particleIndexA].position; } else { particleIndexA = 0xFFFFFFFF; - particleA = (Particle)0; + positionA = 0; } @@ -48,10 +49,10 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui #ifdef SPH_USE_ACCELERATION_GRID // Grid cell is of size [SPH smoothing radius], so position is refitted into that - float3 remappedPos = particleA.position * xSPH_h_rcp; - - int3 cellIndex = floor(remappedPos); + const float3 remappedPos = positionA * xSPH_h_rcp; + const int3 cellIndex = floor(remappedPos); + // iterate through all [27] neighbor cells: [loop] for (int i = -1; i <= 1; ++i) { @@ -61,39 +62,38 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui [loop] for (int k = -1; k <= 1; ++k) { + // hashed cell index is retrieved: const int3 neighborIndex = cellIndex + int3(i, j, k); - const uint flatNeighborIndex = SPH_GridHash(neighborIndex); + // look up the offset into particle list from neighbor cell: uint neighborIterator = cellOffsetBuffer[flatNeighborIndex]; + // iterate through neighbor cell particles (if iterator offset is valid): [loop] while (neighborIterator != 0xFFFFFFFF && neighborIterator < aliveCount) { - //if (neighborIterator != DTid.x) // actually, without this check, the whole thing is just more stable + uint particleIndexB = aliveBuffer_CURRENT[neighborIterator]; + if ((uint)cellIndexBuffer[particleIndexB] != flatNeighborIndex) { - uint particleIndexB = aliveBuffer_CURRENT[neighborIterator]; - if ((uint)cellIndexBuffer[particleIndexB] != flatNeighborIndex) - { - break; - } - - // SPH Density evaluation: - { - Particle particleB = particleBuffer[particleIndexB]; - - float3 diff = particleA.position - particleB.position; - float r2 = dot(diff, diff); // distance squared - - if (r2 < h2) - { - float W = (315.0f / (64.0f * PI * h9)) * pow(h2 - r2, 3); // poly6 smoothing kernel - - density += particleB.mass * W; - } - } + // here means we stepped out of the neighbor cell list! + break; } + // SPH Density evaluation: + { + Particle particleB = particleBuffer[particleIndexB]; + + float3 diff = positionA - particleB.position; + float r2 = dot(diff, diff); // distance squared + + if (r2 < h2) + { + float W = xSPH_poly6_constant * pow(h2 - r2, 3); // poly6 smoothing kernel + + density += particleB.mass * W; + } + } neighborIterator++; } @@ -130,12 +130,12 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui { float3 positionB = positions_masses[i].xyz; - float3 diff = particleA.position - positionB; + float3 diff = positionA - positionB; float r2 = dot(diff, diff); // distance squared if (r2 < h2) { - float W = (315.0f / (64.0f * PI * h9)) * pow(h2 - r2, 3); // poly6 smoothing kernel + float W = xSPH_poly6_constant * pow(h2 - r2, 3); // poly6 smoothing kernel float mass = positions_masses[i].w; @@ -160,7 +160,6 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui // Store the results: densityBuffer[particleIndexA] = density; - } diff --git a/WickedEngine/emittedparticle_sphforceCS.hlsl b/WickedEngine/emittedparticle_sphforceCS.hlsl index bf4dc07b4..926a322ce 100644 --- a/WickedEngine/emittedparticle_sphforceCS.hlsl +++ b/WickedEngine/emittedparticle_sphforceCS.hlsl @@ -11,9 +11,12 @@ STRUCTUREDBUFFER(cellOffsetBuffer, uint, 4); RWSTRUCTUREDBUFFER(particleBuffer, Particle, 0); +#ifndef SPH_USE_ACCELERATION_GRID +// grid structure is not a good fit to exploit shared memory because one threadgroup can load from different initial cells :( groupshared float4 positions_densities[THREADCOUNT_SIMULATION]; groupshared float4 velocities_pressures[THREADCOUNT_SIMULATION]; groupshared float masses[THREADCOUNT_SIMULATION]; +#endif // SPH_USE_ACCELERATION_GRID [numthreads(THREADCOUNT_SIMULATION, 1, 1)] void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, uint3 Gid : SV_GroupID ) @@ -22,8 +25,6 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui const float h = xSPH_h; // smoothing radius const float h2 = xSPH_h2; // smoothing radius ^ 2 const float h3 = xSPH_h3; // smoothing radius ^ 3 - const float h6 = xSPH_h6; // smoothing radius ^ 6 - const float h9 = xSPH_h9; // smoothing radius ^ 9 const float K = xSPH_K; // pressure constant const float p0 = xSPH_p0; // reference density const float e = xSPH_e; // viscosity constant @@ -60,10 +61,10 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui #ifdef SPH_USE_ACCELERATION_GRID // Grid cell is of size [SPH smoothing radius], so position is refitted into that - float3 remappedPos = particleA.position * xSPH_h_rcp; - - int3 cellIndex = floor(remappedPos); + const float3 remappedPos = particleA.position * xSPH_h_rcp; + const int3 cellIndex = floor(remappedPos); + // iterate through all [27] neighbor cells: [loop] for (int i = -1; i <= 1; ++i) { @@ -73,12 +74,14 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui [loop] for (int k = -1; k <= 1; ++k) { + // hashed cell index is retrieved: const int3 neighborIndex = cellIndex + int3(i, j, k); - const uint flatNeighborIndex = SPH_GridHash(neighborIndex); + // look up the offset into particle list from neighbor cell: uint neighborIterator = cellOffsetBuffer[flatNeighborIndex]; + // iterate through neighbor cell particles (if iterator offset is valid): [loop] while (neighborIterator != 0xFFFFFFFF && neighborIterator < aliveCount) { @@ -87,27 +90,28 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui uint particleIndexB = aliveBuffer_CURRENT[neighborIterator]; if ((uint)cellIndexBuffer[particleIndexB] != flatNeighborIndex) { + // here means we stepped out of the neighbor cell list! break; } // SPH Force evaluation: { - float3 positionB = particleBuffer[particleIndexB].position.xyz; + const float3 positionB = particleBuffer[particleIndexB].position.xyz; - float3 diff = particleA.position - positionB; - float r2 = dot(diff, diff); // distance squared - float r = sqrt(r2); + const float3 diff = particleA.position - positionB; + const float r2 = dot(diff, diff); // distance squared + const float r = sqrt(r2); if (r < h) { - float3 velocityB = particleBuffer[particleIndexB].velocity.xyz; - float densityB = densityBuffer[particleIndexB]; - float pressureB = K * (densityB - p0); + const float3 velocityB = particleBuffer[particleIndexB].velocity.xyz; + const float densityB = densityBuffer[particleIndexB]; + const float pressureB = K * (densityB - p0); - float3 rNorm = normalize(diff); - float W = (-45 / (PI * h6)) * pow(h - r, 2); // spiky kernel smoothing function + const float3 rNorm = normalize(diff); + float W = xSPH_spiky_constant * pow(h - r, 2); // spiky kernel smoothing function - float mass = particleBuffer[particleIndexB].mass / particleA.mass; + const float mass = particleBuffer[particleIndexB].mass / particleA.mass; f_a += mass * ((pressureA + pressureB) / (2 * densityA * densityB)) * W * rNorm; @@ -173,7 +177,7 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui float pressureB = velocities_pressures[i].w; float3 rNorm = normalize(diff); - float W = (-45 / (PI * h6)) * pow(h - r, 2); // spiky kernel smoothing function + float W = xSPH_spiky_constant * pow(h - r, 2); // spiky kernel smoothing function float mass = masses[i] / particleA.mass; @@ -211,7 +215,7 @@ void main( uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex, ui #ifdef DEBUG_PRESSURE // debug pressure: - float3 color = lerp(float3(1, 1, 1), float3(1, 0, 0), saturate(abs(pressureA - p0) * 0.01f)) * 255; + float3 color = lerp(float3(1, 1, 1), float3(1, 0, 0), saturate(pressureA * 0.005f)) * 255; uint uColor = 0; uColor |= (uint)color.r << 0; uColor |= (uint)color.g << 8; diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index 8f1964bf5..841db8d4d 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -7,7 +7,7 @@ using namespace std; // this should always be only INCREMENTED and only if a new serialization is implemeted somewhere! -uint64_t __archiveVersion = 17; +uint64_t __archiveVersion = 18; // this is the version number of which below the archive is not compatible with the current version uint64_t __archiveVersionBarrier = 1; diff --git a/WickedEngine/wiEmittedParticle.cpp b/WickedEngine/wiEmittedParticle.cpp index 6878af5a8..62052ab7a 100644 --- a/WickedEngine/wiEmittedParticle.cpp +++ b/WickedEngine/wiEmittedParticle.cpp @@ -346,14 +346,17 @@ void wiEmittedParticle::UpdateRenderData(GRAPHICSTHREAD threadID) cb.xEmitterOpacity = material->alpha; cb.xParticleMass = mass; cb.xEmitterMaxParticleCount = MAX_PARTICLES; + cb.xEmitterFixedTimestep = FIXED_TIMESTEP; // SPH: cb.xSPH_h = SPH_h; cb.xSPH_h_rcp = 1.0f / SPH_h; cb.xSPH_h2 = SPH_h * SPH_h; cb.xSPH_h3 = cb.xSPH_h2 * SPH_h; - cb.xSPH_h6 = cb.xSPH_h2 * cb.xSPH_h2 * cb.xSPH_h2; - cb.xSPH_h9 = cb.xSPH_h3 * cb.xSPH_h3; + const float h6 = cb.xSPH_h2 * cb.xSPH_h2 * cb.xSPH_h2; + const float h9 = cb.xSPH_h3 * cb.xSPH_h3; + cb.xSPH_poly6_constant = (315.0f / (64.0f * XM_PI * h9)); + cb.xSPH_spiky_constant = (-45 / (XM_PI * h6)); cb.xSPH_K = SPH_K; cb.xSPH_p0 = SPH_p0; cb.xSPH_e = SPH_e; @@ -938,6 +941,16 @@ void wiEmittedParticle::Serialize(wiArchive& archive) archive >> tmp; shaderType = (PARTICLESHADERTYPE)tmp; } + if (archive.GetVersion() >= 18) + { + archive >> mass; + archive >> FIXED_TIMESTEP; + archive >> SPH_FLUIDSIMULATION; + archive >> SPH_h; + archive >> SPH_K; + archive >> SPH_p0; + archive >> SPH_e; + } } else @@ -968,5 +981,16 @@ void wiEmittedParticle::Serialize(wiArchive& archive) { archive << (int)shaderType; } + if (archive.GetVersion() >= 18) + { + archive << mass; + archive << FIXED_TIMESTEP; + archive << SPH_FLUIDSIMULATION; + archive << SPH_h; + archive << SPH_K; + archive << SPH_p0; + archive << SPH_e; + } + } } diff --git a/WickedEngine/wiEmittedParticle.h b/WickedEngine/wiEmittedParticle.h index 8ba4f1f81..ed585f8bb 100644 --- a/WickedEngine/wiEmittedParticle.h +++ b/WickedEngine/wiEmittedParticle.h @@ -85,6 +85,7 @@ public: bool SORTING = false; bool DEPTHCOLLISIONS = false; bool SPH_FLUIDSIMULATION = false; + float FIXED_TIMESTEP = -1.0f; // -1 : variable timestep; >=0 : fixed timestep PARTICLESHADERTYPE shaderType = SOFT; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 4180f8208..527c393c0 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 17; // minor bug fixes, alterations, refactors, updates - const int revision = 21; + const int revision = 22; long GetVersion()