我正在尝试使用弹簧粒子系统来实现布料模拟,但是我的物理学并不完全正确。当我进行模拟时,布料按预期绘制,但随着时间的推移,重力将布料无限地向下拉。换句话说,由弹簧引起的力不能正确累积以克服重力的向下拉力,我最终得到了......:
它继续无限下降。从我所做的所有调试中我所看到的是,当重力引起拉伸增加时,由附着在其上的所有弹簧引起的粒子上的力的累积不能正确地求和。我无法弄清楚我在物理学中忽略了什么。
我的布料使用此功能每次更新
void Cloth::updateGeometry(atlas::utils::Time const& t) {
for (int i = 0; i < mSprings.size(); ++i) {
mSprings[i].calculateForces();
}
for (int i = 0; i < mParticles.size(); ++i) {
mParticles[i].updateGeometry(t);
}
}
我的弹簧使用以下函数更新,其中p1和p2是指向此弹簧所连接的每个粒子的指针。
void Spring::calculateForces() {
glm::vec3 springVector = normalize((p2->getCurrentPosition() - p1->getCurrentPosition()));
GLfloat stretchLength = length(p2->getCurrentPosition(), p1->getCurrentPosition());
GLfloat displacementFromRest = restLength - stretchLength;
glm::vec3 springForce = -k * displacementFromRest * normalize(springVector);
//Multiply the displacements by the spring constant to get
//A vector which represents the force on each spring
p1->addToSumOfSpringForces(springForce);
p2->addToSumOfSpringForces(-springForce);
}
最后我的粒子使用。更新。
void Particle::updateGeometry(atlas::utils::Time const& t) {
if (!stationary) {
previousPosition = currentPosition;
glm::vec3 forceOfGravity = mass * gravity;
glm::vec3 totalForce = forceOfGravity + (mass * totalSpringForces) - velocity*Damping;
acceleration = totalForce / mass;
//Perform Euler Integration
currentPosition += t.deltaTime * velocity;
velocity += t.deltaTime * acceleration;
//============== End Euler==============//
//Reset the forces acting on the particle from all of the springs
//So that a new accumulated total can be calculated.
totalSpringForces = glm::vec3{ 0.0f, 0.0f, 0.0f };
}
}
totalSpringForces
变量由弹簧更新功能中的addToSumOfSpringForces(springForce);
调用更新。我们的想法是,每个弹簧首先根据每个粒子的当前位置进行评估,然后使用
totalSpringForce
变量。
void Particle::addToSumOfSpringForces(glm::vec3 force) {
totalSpringForces += force;
}
只是为了补充说明,布料是根据this描述使用结构,弯曲和剪切弹簧构造的。这可能是不必要的,但我在下面包含了我的布料构造函数。
Cloth::Cloth(GLfloat width_, GLfloat height_, GLuint numParticlesWide_, GLuint numParticlesHigh_) :
width(width_),
height(height_),
numParticlesHigh(numParticlesHigh_),
numParticlesWide(numParticlesWide_),
clothRotationVector{0.0f, 0.0f, 0.0f},
clothPosition{ 0.0f, 5.0f, 0.0f },
clothRotationAngle(0.0f)
{
USING_ATLAS_MATH_NS;
USING_ATLAS_GL_NS;
glm::vec3 clothColour{1.0f, 0.5f, 0.2f};
//Create Particles
GLuint count = 0;
restLength = (width * (1 / (float)numParticlesWide));
for (GLuint y = 0; y < numParticlesHigh; ++y) {
for (GLuint x = 0; x < numParticlesWide; ++x) {
glm::vec3 pos = {(width * (x / (float)numParticlesWide)), (-height * (y / (float)numParticlesHigh)), 0.0f};
mParticles.push_back(Particle(pos, count, clothColour));
++count;
}
}
//Create Springs
for (GLuint x = 0; x < numParticlesWide; ++x) {
for (GLuint y = 0; y < numParticlesHigh; ++y) {
//============ Structural springs ==========//
//Connect to the particle to the immediate right of the current particle
if (x < numParticlesWide - 1) mSprings.push_back(Spring(getParticle(x,y), getParticle(x+1,y)));
//Connect to the particle that is immediately below the current particle
if (y < numParticlesHigh - 1) mSprings.push_back(Spring(getParticle(x,y), getParticle(x,y+1)));
//============ Shear Springs ================//
//Connect the shear springs to make the X pattern
if (x < numParticlesWide - 1 && y < numParticlesHigh - 1) {
mSprings.push_back(Spring(getParticle(x, y), getParticle(x + 1, y + 1)));
mSprings.push_back(Spring(getParticle(x+1, y), getParticle(x, y+1)));
}
//============ Bend Springs ===============//
//Connect the current particle to the second particle over to the right
if (x < numParticlesWide - 2) mSprings.push_back(Spring(getParticle(x,y), getParticle(x+2,y)));
//Connect the current particle to the particle two below
if (y < numParticlesHigh - 2) mSprings.push_back(Spring(getParticle(x,y), getParticle(x, y+2)));
////Create the X pattern
//if (x < numParticlesWide - 2 && y < numParticlesHigh - 2) {
// mSprings.push_back(Spring(getParticle(x, y), getParticle(x+2,y+2)));
// mSprings.push_back(Spring(getParticle(x+2,y), getParticle(x,y+2)));
//};
}
}
//Set the top left and right as stationary
getParticle(0, 0)->makeStationary();
getParticle(numParticlesWide - 1, 0)->makeStationary();
//Make Indices for Particles
for (GLuint row = 0; row < numParticlesWide - 1; ++row) {
for (GLuint col = 0; col < numParticlesHigh - 1; ++col) {
//Triangle one
mParticleIndices.push_back(getParticle(row,col)->getIndex());
mParticleIndices.push_back(getParticle(row,col+1)->getIndex());
mParticleIndices.push_back(getParticle(row+1, col)->getIndex());
//Triangle two
mParticleIndices.push_back(getParticle(row, col+1)->getIndex());
mParticleIndices.push_back(getParticle(row+1, col+1)->getIndex());
mParticleIndices.push_back(getParticle(row+1, col)->getIndex());
}
}
glGenBuffers(1, &clothVertexBufferID);
glGenBuffers(1, &clothIndexBufferID);
sendDataToGPU();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, clothIndexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mParticleIndices.size() * sizeof(GLushort), &mParticleIndices[0], GL_STATIC_DRAW);
defineVAO();
std::string shaderDir = generated::ShaderPaths::getShaderDirectory();
std::vector<ShaderInfo> shaders
{
{ GL_VERTEX_SHADER, shaderDir + "Cloth.vs.glsl" },
{ GL_FRAGMENT_SHADER, shaderDir + "Cloth.fs.glsl" }
};
mModel = glm::translate(Matrix4(1.0f), clothPosition);
mShaders.push_back(ShaderPointer(new Shader));
mShaders[0]->compileShaders(shaders);
mShaders[0]->linkShaders();
mUniforms.insert(UniformKey("mvpMat",mShaders[0]->getUniformVariable("mvpMat")));
mShaders[0]->disableShaders();
}
修改的
我已经确认totalSpringForces
变量确实在变化。我在粒子更新功能中添加了一个打印语句,您可以在下图中看到。仅选择totalSpringForces
仅用于粒子#55,在这种情况下是粒子0下面的粒子,它是静止的并且不允许移动。打印输出也是在大约20-25次迭代之后。如您所见,y方向上的totalSpringForces
具有0.7037的正值(即抵消重力)。我让它跑了半个小时,它只到了5。
目前我的常量是
k = 2.0f
mass = 0.1f
damping = 0.55f
gravity{ 0.0f, -9.81f, 0.0f },