/* Calculate the acceleration of each particle using a grid optimized approach. For each particle, only particles in the same grid cell and the (26) neighboring grid cells must be considered, since any particle beyond a grid cell distance away contributes no force. */ void PARTICLE_SYSTEM::calculateAcceleration() { /////////////////// // STEP 1: UPDATE DENSITY & PRESSURE OF EACH PARTICLE for (int x = 0; x < (*grid).xRes(); x++) { for (int y = 0; y < (*grid).yRes(); y++) { for (int z = 0; z < (*grid).zRes(); z++) { vector<PARTICLE>& particles = (*grid)(x,y,z); for (int p = 0; p < particles.size(); p++) { PARTICLE& particle = particles[p]; particle.density() = 0.0; // now iteratate through neighbors for (int offsetX = -1; offsetX <= 1; offsetX++) { if (x+offsetX < 0) continue; if (x+offsetX >= (*grid).xRes()) break; for (int offsetY = -1; offsetY <= 1; offsetY++) { if (y+offsetY < 0) continue; if (y+offsetY >= (*grid).yRes()) break; for (int offsetZ = -1; offsetZ <= 1; offsetZ++) { if (z+offsetZ < 0) continue; if (z+offsetZ >= (*grid).zRes()) break; vector<PARTICLE>& neighborGridCellParticles = (*grid)(x+offsetX, y+offsetY, z+offsetZ); for (int i = 0; i < neighborGridCellParticles.size(); i++) { PARTICLE& neighborParticle = neighborGridCellParticles[i]; vec3 diffPosition = particle.position() - neighborParticle.position(); float radiusSquared = dot(diffPosition, diffPosition); if (radiusSquared <= h*h) particle.density() += Wpoly6(radiusSquared); } } } } particle.density() *= PARTICLE_MASS; // p = k(density - density_rest) particle.pressure() = GAS_STIFFNESS * (particle.density() - REST_DENSITY); } } } } /////////////////// // STEP 2: COMPUTE FORCES FOR ALL PARTICLES for (int x = 0; x < (*grid).xRes(); x++) { for (int y = 0; y < (*grid).yRes(); y++) { for (int z = 0; z < (*grid).zRes(); z++) { vector<PARTICLE>& particles = (*grid)(x,y,z); for (int p = 0; p < particles.size(); p++) { PARTICLE& particle = particles[p]; //cout << "particle id: " << particle.id() << endl; vec3 f_pressure, f_viscosity, f_surface, f_gravity = gravityVector * particle.density(), colorFieldNormal; float colorFieldLaplacian; // now iteratate through neighbors for (int offsetX = -1; offsetX <= 1; offsetX++) { if (x+offsetX < 0) continue; if (x+offsetX >= (*grid).xRes()) break; for (int offsetY = -1; offsetY <= 1; offsetY++) { if (y+offsetY < 0) continue; if (y+offsetY >= (*grid).yRes()) break; for (int offsetZ = -1; offsetZ <= 1; offsetZ++) { if (z+offsetZ < 0) continue; if (z+offsetZ >= (*grid).zRes()) break; vector<PARTICLE>& neighborGridCellParticles = (*grid)(x+offsetX, y+offsetY, z+offsetZ); for (int i = 0; i < neighborGridCellParticles.size(); i++) { PARTICLE& neighbor = neighborGridCellParticles[i]; //if (particle.id() == neighbor.id()) continue; // SKIPPING COMPARISON OF THE SAME PARTICLE vec3 diffPosition = particle.position() - neighbor.position(); float radiusSquared = dot(diffPosition, diffPosition); if (radiusSquared <= h*h) { vec3 poly6Gradient, spikyGradient; Wpoly6Gradient(diffPosition, radiusSquared, poly6Gradient); WspikyGradient(diffPosition, radiusSquared, spikyGradient); if (particle.id() != neighbor.id()) { f_pressure += (float)(particle.pressure()/pow(particle.density(),2)+neighbor.pressure()/pow(neighbor.density(),2))*spikyGradient; f_viscosity += (neighbor.velocity() - particle.velocity()) * WviscosityLaplacian(radiusSquared) / neighbor.density(); } colorFieldNormal += poly6Gradient / neighbor.density(); colorFieldLaplacian += Wpoly6Laplacian(radiusSquared) / neighbor.density(); } } } } } // end of neighbor grid cell iteration f_pressure *= -PARTICLE_MASS * particle.density(); f_viscosity *= VISCOSITY * PARTICLE_MASS; colorFieldNormal *= PARTICLE_MASS; particle.normal = -1.0f * colorFieldNormal; colorFieldLaplacian *= PARTICLE_MASS; // surface tension force float colorFieldNormalMagnitude = colorFieldNormal.length(); if (colorFieldNormalMagnitude > SURFACE_THRESHOLD) { particle.flag() = true; f_surface = -SURFACE_TENSION * colorFieldNormal / colorFieldNormalMagnitude * colorFieldLaplacian; } else { particle.flag() = false; } // ADD IN SPH FORCES particle.acceleration() = (f_pressure + f_viscosity + f_surface + f_gravity) / particle.density(); // EXTERNAL FORCES HERE (USER INTERACTION, SWIRL) vec3 f_collision; collisionForce(particle, f_collision); } } } } }
/* Calculate the acceleration of every particle in a brute force manner (n^2). Used for debugging. */ void PARTICLE_SYSTEM::calculateAccelerationBrute() { /////////////////// // STEP 1: UPDATE DENSITY & PRESSURE OF EACH PARTICLE for (int i = 0; i < PARTICLE::count; i++) { // grab ith particle reference PARTICLE& particle = _particles[i]; // now iteratate through neighbors particle.density() = 0.0; for (int j = 0; j < PARTICLE::count; j++) { PARTICLE& neighborParticle = _particles[j]; vec3 diffPosition = particle.position() - neighborParticle.position(); float radiusSquared = dot(diffPosition, diffPosition); if (radiusSquared <= h*h) particle.density() += Wpoly6(radiusSquared); } particle.density() *= PARTICLE_MASS; // p = k(density - density_rest) particle.pressure() = GAS_STIFFNESS * (particle.density() - REST_DENSITY); //totalDensity += particle.density(); } /////////////////// // STEP 2: COMPUTE FORCES FOR ALL PARTICLES for (int i = 0; i < PARTICLE::count; i++) { PARTICLE& particle = _particles[i]; //cout << "particle id: " << particle.id() << endl; vec3 f_pressure, f_viscosity, f_surface, f_gravity(0.0, particle.density() * -9.80665, 0.0), n, colorFieldNormal, colorFieldLaplacian; // n is gradient of colorfield //float n_mag; for (int j = 0; j < PARTICLE::count; j++) { PARTICLE& neighbor = _particles[j]; vec3 diffPosition = particle.position() - neighbor.position(); vec3 diffPositionNormalized = normalize(diffPosition); // need? float radiusSquared = dot(diffPosition, diffPosition); if (radiusSquared <= h*h) { if (radiusSquared > 0.0) { //neighborsVisited++; //cout << neighborsVisited << endl; //cout << neighbor.id() << endl; vec3 gradient; Wpoly6Gradient(diffPosition, radiusSquared, gradient); f_pressure += (particle.pressure() + neighbor.pressure()) / (2.0f * neighbor.density()) * gradient; colorFieldNormal += gradient / neighbor.density(); } f_viscosity += (neighbor.velocity() - particle.velocity()) * WviscosityLaplacian(radiusSquared) / neighbor.density(); colorFieldLaplacian += Wpoly6Laplacian(radiusSquared) / neighbor.density(); } } f_pressure *= -PARTICLE_MASS; //totalPressure += f_pressure; f_viscosity *= VISCOSITY * PARTICLE_MASS; colorFieldNormal *= PARTICLE_MASS; particle.normal = -1.0f * colorFieldNormal; colorFieldLaplacian *= PARTICLE_MASS; // surface tension force float colorFieldNormalMagnitude = colorFieldNormal.length(); if (colorFieldNormalMagnitude > surfaceThreshold) { particle.flag() = true; f_surface = -SURFACE_TENSION * colorFieldLaplacian * colorFieldNormal / colorFieldNormalMagnitude; } else { particle.flag() = false; } // ADD IN SPH FORCES particle.acceleration() = (f_pressure + f_viscosity + f_surface + f_gravity) / particle.density(); // EXTERNAL FORCES HERE (USER INTERACTION, SWIRL) vec3 f_collision; collisionForce(particle, f_collision); } }
void CppParticleSimulator::updateSimulation(const Parameters ¶ms, float dt_seconds) { // Set forces to 0 and calculate densities for (int i = 0; i < positions.size(); ++i) { forces[i] = {0, 0, 0}; float density = 0; for (int j = 0; j < positions.size(); ++j) { glm::vec3 relativePos = positions[i] - positions[j]; density += params.get_particle_mass() * Wpoly6(relativePos, params.kernel_size); } densities[i] = density; } // Calculate forces for (int i = 0; i < positions.size(); ++i) { float iPressure = (densities[i] - params.rest_density) * params.k_gas; float cs = 0; glm::vec3 n = {0, 0, 0}; float laplacianCs = 0; glm::vec3 pressureForce = {0, 0, 0}; glm::vec3 viscosityForce = {0, 0, 0}; for (int j = 0; j < positions.size(); ++j) { glm::vec3 relativePos = positions[i] - positions[j]; // Particle j's pressure force on i float jPressure = (densities[j] - params.rest_density) * params.k_gas; pressureForce = pressureForce - params.get_particle_mass() * ((iPressure + jPressure) / (2 * densities[j])) * gradWspiky(relativePos, params.kernel_size); // Particle j's viscosity force in i viscosityForce += params.k_viscosity * params.get_particle_mass() * ((velocities[j] - velocities[i]) / densities[j]) * laplacianWviscosity(relativePos, params.kernel_size); // cs for particle j cs += params.get_particle_mass() * (1 / densities[j]) * Wpoly6(relativePos, params.kernel_size); // Gradient of cs for particle j n += params.get_particle_mass() * (1 / densities[j]) * gradWpoly6(relativePos, params.kernel_size); // Laplacian of cs for particle j laplacianCs += params.get_particle_mass() * (1 /densities[j]) * laplacianWpoly6(relativePos, params.kernel_size); } glm::vec3 tensionForce; if (glm::length(n) < params.k_threshold) { tensionForce = {0, 0, 0}; } else { tensionForce = params.sigma * (- laplacianCs / glm::length(n)) * n; } //glm::vec3 n = {0, 0, 0}; glm::vec3 boundaryForce = {0, 0, 0}; boundaryForce = calculateBoundaryForceGlass(params, i); // Add external forces on i forces[i] = pressureForce + viscosityForce + tensionForce + params.gravity + boundaryForce; // Euler time step velocities[i] += (forces[i] / densities[i]) * dt_seconds; positions[i] += velocities[i] * dt_seconds; } checkBoundariesGlass(params); glBindBuffer (GL_ARRAY_BUFFER, vbo_pos); glBufferData (GL_ARRAY_BUFFER, positions.size() * 3 * sizeof (float), positions.data(), GL_STATIC_DRAW); }