emitter updates, optimize fluid simulator
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user