案例位置:examples/Cuda/SPH/GL_GhostSPH
功能介绍:SPH方法存在虚假的数值表面张力伪影、不满足质量守恒约束,无法获取液体与固体的真实内力等问题。Ghost SPH方案通过一种新的粒子采样算法解决了这一问题。该算法在周围的空气和固体中创建了一个狭窄的Ghost粒子层作为边界条件,以更加真实的模拟物理现象。
案例说明: 案例展示了GhostParticles的流体仿真功能。如下图所示流体粒子从Ghost粒子层滑落,没有发生穿透现象。
接下来介绍案例的实现过程:
std::shared_ptr<GhostParticles<DataType3f>> createGhostParticles()
{
auto ghost = std::make_shared<GhostParticles<DataType3f>>();
std::vector<Vec3f> host_pos;
std::vector<Vec3f> host_vel;
std::vector<Vec3f> host_force;
std::vector<Vec3f> host_normal;
std::vector<Attribute> host_attribute;
Vec3f low(-0.2, -0.015, -0.2);
Vec3f high(0.2, -0.005, 0.2);
Real s = 0.005f;
int m_iExt = 0;
float omega = 1.0f;
float half_s = -s / 2.0f;
int num = 0;
for (float x = low.x - m_iExt * s; x <= high.x + m_iExt * s; x += s) {
for (float y = low.y - m_iExt * s; y <= high.y + m_iExt * s; y += s) {
for (float z = low.z - m_iExt * s; z <= high.z + m_iExt * s; z += s) {
Attribute attri;
attri.setFluid();
attri.setDynamic();
host_pos.push_back(Vec3f(x, y, z));
host_vel.push_back(Vec3f(0));
host_force.push_back(Vec3f(0));
host_normal.push_back(Vec3f(0, 1, 0));
host_attribute.push_back(attri);
}
}
}
ghost->statePosition()->resize(num);
ghost->stateVelocity()->resize(num);
ghost->stateForce()->resize(num);
ghost->stateNormal()->resize(num);
ghost->stateAttribute()->resize(num);
ghost->statePosition()->assign(host_pos);
ghost->stateVelocity()->assign(host_vel);
ghost->stateForce()->assign(host_force);
ghost->stateNormal()->assign(host_normal);
ghost->stateAttribute()->assign(host_attribute);
host_pos.clear();
host_vel.clear();
host_force.clear();
host_normal.clear();
host_attribute.clear();
return ghost;
}
std::shared_ptr<SceneGraph> scn = std::make_shared<SceneGraph>();
scn->setUpperBound(Vec3f(0.5, 1, 0.5));
scn->setLowerBound(Vec3f(-0.5, 0, -0.5));
auto boundary = scn->addNode(std::make_shared<StaticBoundary<DataType3f>>());
boundary->loadCube(Vec3f(-0.1f, 0.0f, -0.1f), Vec3f(0.1f, 1.0f, 0.1f), 0.005, true);
auto fluid = scn->addNode(std::make_shared<ParticleSystem<DataType3f>>());
fluid->loadParticles(Vec3f(-0.1, 0.0, -0.1), Vec3f(0.105, 0.1, 0.105), 0.005);
auto ghost = scn->addNode(createGhostParticles());
auto incompressibleFluid = scn->addNode(std::make_shared<GhostFluid<DataType3f>>());
fluid->connect(incompressibleFluid->importFluidParticles());
ghost->connect(incompressibleFluid->importBoundaryParticles());
incompressibleFluid->connect(boundary->importParticleSystems());
{
auto calculateNorm = std::make_shared<CalculateNorm<DataType3f>>();
auto colorMapper = std::make_shared<ColorMapping<DataType3f>>();
colorMapper->varMax()->setValue(5.0f);
fluid->stateVelocity()->connect(calculateNorm->inVec());
calculateNorm->outNorm()->connect(colorMapper->inScalar());
fluid->graphicsPipeline()->pushModule(calculateNorm);
fluid->graphicsPipeline()->pushModule(colorMapper);
auto ptRender = std::make_shared<GLPointVisualModule>();
ptRender->setColor(Vec3f(1, 0, 0));
ptRender->setColorMapMode(GLPointVisualModule::PER_VERTEX_SHADER);
fluid->statePointSet()->connect(ptRender->inPointSet());
colorMapper->outColor()->connect(ptRender->inColor());
fluid->graphicsPipeline()->pushModule(ptRender);
// A simple color bar widget for node
auto colorBar = std::make_shared<ImColorbar>();
colorBar->varMax()->setValue(5.0f);
calculateNorm->outNorm()->connect(colorBar->inScalar());
// add the widget to app
fluid->graphicsPipeline()->pushModule(colorBar);
}
{
auto ghostRender = std::make_shared<GLPointVisualModule>();
ghostRender->setColor(Vec3f(1, 0.5, 0));
ghostRender->setColorMapMode(GLPointVisualModule::PER_OBJECT_SHADER);
ghost->statePointSet()->connect(ghostRender->inPointSet());
ghost->graphicsPipeline()->pushModule(ghostRender);
}