在這個教程里我們將模擬一段繩索,我們是在39課的基礎上進行的。
在物理模擬中,我們必須設置各個物理量,就像它們在自然界中的行為一樣。模擬中的運動并不一定和自然界相同,我們使用的運動模型,必須和我們需要模擬的目的有關,目的決定了它的精確度。要知道我們的目標不是模擬原子和分子,也不是模擬成千上萬的粒子系。首先我們需要確定我們模擬的目標,才能創建我們的物理模型。它和下面內容相關:
1. 運動的數學表示
2. 執行模擬的計算機的速度
1. 運動的數學表示:
這個問題決定了我們使用何種數學方程來模擬運動,使用經典力學還是量子力學。
2. 執行模擬的計算機的速度:
計算機的速度決定了我們可以模擬的精度。
設計繩索的物理模型:
我們在經典力學和高于500Mhz的計算機上模擬這個問題。首先我們需要設定需要的精度,我們使用一系列互相用彈簧連接的質點來模擬繩索,精度決定了我們用多少個點來模擬,當然越多越精確。在下面我決定用50或100個點來模擬繩子一段3或4m長的繩子,換句話說,我們的模擬精度就是3到8厘米。
設計運動模型:
在繩子中,施加給各個質點的力來自于自身的質量和相連的內力(參見大學里的普通力學)。如下我們用"O"表示質點,“—”表示連接質點的彈簧。
O----O----O----O
1 2 3 4
彈簧的力學公式如下:
力 = -k * x
k: 彈性系數
x: 相距平衡位置的位移
上面的公式說明,如果相鄰點的距離為平衡距離,那么它們不受到任何力的作用。如果我們設置平衡位置為5cm,那么100個點的繩子長5m。如果相連質點之間的位置小于5cm,它們受到排斥力。
上面的公式只是一個基礎,現在我們可以加上摩擦力,如果沒有這項,那么繩子將永遠動下去。
彈簧類:
這個類包含相連接的兩個物體,它們之間具有作用力。
class Spring
{
public:
Mass* mass1; // 質點1
Mass* mass2; // 質點2
float springConstant; // 彈性系數
float springLength; //彈簧長度
float frictionConstant; //摩擦系數
Spring(Mass* mass1, Mass* mass2,
// 構造函數
float springConstant, float springLength, float frictionConstant)
{
this->springConstant = springConstant;
this->springLength = springLength;
this->frictionConstant = frictionConstant;
this->mass1 = mass1;
this->mass2 = mass2;
}
void solve() // 計算各個物體的受力
{
Vector3D springVector = mass1->pos - mass2->pos;
float r = springVector.length(); // 計算兩個物體之間的距離
Vector3D force;
if (r != 0) // 計算力
force += -(springVector / r) * (r - springLength) * springConstant;
...
force += -(mass1->vel - mass2->vel) * frictionConstant; // 加上摩擦力
mass1->applyForce(force); // 給物體1施加力
mass2->applyForce(-force); // 給物體2施加力
}
下面我們把繩子釘在墻上,所以我們的模擬就多了一個萬有引力,空氣摩擦力。萬有引力的公式如下:
力 = (重力加速度) * 質量
萬有引力會作用在每一個質點上,地面也會給每個物體一個作用力。在我們的模型中將考慮繩子和地面之間的接觸,地面給繩子向上的力,并提供摩擦力。
設置模擬的初始值
現在我們已經設置好模擬環境了,長度單位是m,時間單位是秒,質量單位是kg。
為了設置初始值,我們必須提供供模擬開始的參數。我們定義一下參數:
1. 重力加速度: 9.81 m/s/s 垂直向下
2. 質點個數: 80
3. 相連質點的距離: 5 cm (0.05 meters)
4. 質量: 50 克(0.05 kg)
5. 繩子開始處于垂直狀態
下面計算繩子受到的力
f = (繩子質量) * (重力加速度) = (4 kg) * (9.81) ~= 40 N
彈簧必須平衡這個力 40 N,它伸長1cm,計算彈性系數:
合力= -k * x = -k * 0.01 m
合力應該為0 :
40 N + (-k * 0.01 meters) = 0
彈性系數 k 為:
k = 4000 N / m
設置彈簧的摩擦系數:
springFrictionConstant = 0.2 N/(m/s)
下面我們看看這個繩索類:
1. virtual void init() ---> 重置力
2. virtual void solve() ---> 計算各個質點的力
3. virtual void simulate(float dt) ---> 模擬一次
4. virtual void operate(float dt) ---> 執行一次操作
繩索類如下所示 :
class RopeSimulation : public Simulation //繩索類
{
public:
Spring** springs; // 彈簧類結構的數組的指針
Vector3D gravitation; // 萬有引力
Vector3D ropeConnectionPos; // 繩索的連接點
Vector3D ropeConnectionVel; //連接點的速度,我們使用這個移動繩子
float groundRepulsionConstant; //地面的反作用力
float groundFrictionConstant; //地面的摩擦系數
float groundAbsorptionConstant; //地面的緩沖力
float groundHeight; //地面高度
float airFrictionConstant; //空氣的摩擦系數
下面是它的構造函數
RopeSimulation(
int numOfMasses,
float m,
float springConstant,
float springLength,
float springFrictionConstant,
Vector3D gravitation,
float airFrictionConstant,
float groundRepulsionConstant,
float groundFrictionConstant,
float groundAbsorptionConstant,
float groundHeight
) : Simulation(numOfMasses, m)
{
this->gravitation = gravitation;
this->airFrictionConstant = airFrictionConstant;
this->groundFrictionConstant = groundFrictionConstant;
this->groundRepulsionConstant = groundRepulsionConstant;
this->groundAbsorptionConstant = groundAbsorptionConstant;
this->groundHeight = groundHeight;
for (int a = 0; a < numOfMasses; ++a) // 設置質點位置
{
masses[a]->pos.x = a * springLength;
masses[a]->pos.y = 0;
masses[a]->pos.z = 0;
}
springs = new Spring*[numOfMasses - 1];
for (a = 0; a < numOfMasses - 1; ++a) //創建各個質點之間的模擬彈簧
{
springs[a] = new Spring(masses[a], masses[a + 1],
springConstant, springLength, springFrictionConstant);
}
}
計算施加給各個質點的力
void solve() // 計算施加給各個質點的力
{
for (int a = 0; a < numOfMasses - 1; ++a) // 彈簧施加給各個物體的力
{
springs[a]->solve();
}
for (a = 0; a < numOfMasses; ++a) // 計算各個物體受到的其它的力
{
masses[a]->applyForce(gravitation * masses[a]->m); // 萬有引力
// 空氣的摩擦力
masses[a]->applyForce(-masses[a]->vel * airFrictionConstant);
if (masses[a]->pos.y < groundHeight) // 計算地面對質點的作用
{
Vector3D v;
v = masses[a]->vel; // 返回速度
v.y = 0; // y方向的速度為0
// 計算地面給質點的力
masses[a]->applyForce(-v * groundFrictionConstant);
v = masses[a]->vel;
v.x = 0;
v.z = 0;
if (v.y < 0) // 計算地面的緩沖力
masses[a]->applyForce(-v * groundAbsorptionConstant);
// 計算地面的反作用力
Vector3D force = Vector3D(0, groundRepulsionConstant, 0) *
(groundHeight - masses[a]->pos.y);
masses[a]->applyForce(force); // 施加地面對質點的力
}
}
}
下面的代碼完成整個模擬過程
void simulate(float dt) // 模擬一次
{
Simulation::simulate(dt); // 調用基類的模擬函數
ropeConnectionPos += ropeConnectionVel * dt; // 計算繩子的連接點
if (ropeConnectionPos.y < groundHeight)
{
ropeConnectionPos.y = groundHeight;
ropeConnectionVel.y = 0;
}
masses[0]->pos = ropeConnectionPos; // 更新繩子的連接點和速度
masses[0]->vel = ropeConnectionVel;
}
void setRopeConnectionVel(Vector3D ropeConnectionVel)
{
this->ropeConnectionVel = ropeConnectionVel;
}
有了上面的類,我們可以很方便的模擬繩子,代碼如下:
RopeSimulation* ropeSimulation =
new RopeSimulation(
80, // 80 質點
0.05f, // 每個質點50g
10000.0f, // 彈性系數
0.05f, // 質點之間的距離
0.2f, // 彈簧的內摩擦力
Vector3D(0, -9.81f, 0), // 萬有引力
0.02f, // 空氣摩擦力
100.0f, // 地面反作用系數
0.2f, // 地面摩擦系數
2.0f, // 地面緩沖系數
-1.5f); // 地面高度
下面的代碼在程序中執行繩子的模擬
float dt = milliseconds / 1000.0f; // 經過的秒數
float maxPossible_dt = 0.002f; // 模擬間隔
int numOfIterations = (int)(dt / maxPossible_dt) + 1; // 模擬次數
if (numOfIterations != 0)
dt = dt / numOfIterations;
for (int a = 0; a < numOfIterations; ++a) // 執行模擬
ropeSimulation->operate(dt);
我相信這一個教會了你很多,從最開始的模型的建立,到完成最后的代碼。有了這個基礎,相信你會創造出很多更有意思的代碼!