当前位置: 首页 > 运动 > 正文
2019-11-19 02:04:06 作者:大师 手机看新闻

三体运动模拟 N体运动的程序模拟

[提要] &nbp; &nbp; &nbp; 这依然是与《三体》有关的一篇文章。空间中三个星体在万有引力作用下的运动被称之为三体问题,参见我的上一篇文章:三体运动的程序模拟。而这一节,对三体问题进行了扩展,...

      这依然是与《三体》有关的一篇文章。空间中三个星体在万有引力作用下的运动被称之为三体问题,参见我的上一篇文章:三体运动的程序模拟。而这一节,对三体问题进行了扩展,实现了空间中N个星体在万有引力下的运动模拟。

程序中使用了两个物理定律:

(1)万有引力定律

这是牛顿发现的:任意两个质点有通过连心线方向上的力相互吸引。该引力的大小与它们的质量乘积成正比,与它们距离的平方成反比,与两物体的化学本质或物理状态以及中介物质无关。

以数学表示为:

其中:

依照国际单位制,F的单位为牛顿(N),m1m2的单位为千克(kg),r 的单位为米(m),常数G近似地等于6.67 × 1011 N m2 kg2(牛顿米的平方每千克的平方)。当然程序中G不可能设置为这么小的一个数,用户可以自己设置万有引力常数,以实现合理的运动.

(2)势能与动能守恒定律

引力位能或引力势能是指物体因为大质量物体的万有引力而具有的位能,其大小与其到大质量的距离有关。

其中G为万有引力常数,M、m分别为两物体质量,r为两者距离。

动能,简单的说就是指物体因运动而具有的能量。数值上等于(1/2)Mv^2. 动能是能量的一种,它的国际单位制下单位是焦耳(J),简称焦。

势能与动能守恒即在整个运动过程中,其总能量是不变的,即N体的动能与势能之和为固定值.

     本来我只打算使用万有引力定律,没有考虑势能动能守恒定律.但程序运行后发现,由于没有使用微积分,即使采样间隔时间再小,误差也很大.这让我想起当年学大学物理的时候,大一上学期开高数,然后下学期开了物理.现在对大学物理讲的东西,只记得是用高数中的微积分解物理问题.可那时,我高数学得就是个渣,所以物理也没办法学好.当然就算物理学好了,现在也早忘光了.没有用微积分,这个程序中算法误差不小,模拟一下吧,不要太计较.

      核心代码也发一下,希望有兴趣的人可以对其做出优化:

1 /****************************************************************
2
3 File name : NBodyKernel.h
4 Author : 叶峰
5 Version : 1.0a
6 Create Date : 2014/09/19
7 Description :
8
9 *****************************************************************/
10
11 // --------------------------------------------------------------------------------------
12
13 #ifndef _NBodyKernel_H_
14 #define _NBodyKernel_H_
15
16 // INCLUDES -----------------------------------------------------------------------------
17
18 #include "..\..\..\Why\YCommon_h\WhyMath.h"
19 #include "..\..\..\Why\YInterface\Others\YdD3D9Math.h"
20
21 // --------------------------------------------------------------------------------------
22
23 #define YD_ORBIT_VERTICES_COUNT 1024
24
25 // --------------------------------------------------------------------------------------
26
27 class Star
28 {
29 public:
30 Vector3 vPosition; // 位置
31 Vector3 vVelocity; // 速度
32 Vector3 vForce; // 受力
33 Yreal fWeight; // 质量
34 Yreal fRadiusScale; // 缩放
35 Yreal fKineticEnergy; // 动能
36 Ycolor color; // 颜色
37 Ybool bOrbitVisible; // 轨迹可见
38 Yuint id;
39 Vector3 verticesOrbit[YD_ORBIT_VERTICES_COUNT];
40 Star* pNext;
41
42 Star()
43 {
44 fWeight = 0.0f;
45 fRadiusScale = 1.0f;
46 fKineticEnergy = 0.0f;
47 color = 0xffffffff;
48 bOrbitVisible = YD_FALSE;
49 id = -1;
50 memset(verticesOrbit, 0, sizeof(verticesOrbit));
51 pNext = NULL;
52 }
53
54 // 根据速度计算动能
55 void CalculateKineticEnergyByVelocity()
56 {
57 Yreal sqv = D3DXVec3LengthSq(&vVelocity);
58 fKineticEnergy = 0.5f*fWeight*sqv;
59 }
60
61 // 根据动能计算速度
62 void CalculateVelocityByKineticEnergy()
63 {
64 float v = sqrt(2*fKineticEnergy/fWeight);
65 float w = D3DXVec3Length(&vVelocity);
66 vVelocity *= v/w;
67 };
68 };
69
70 // --------------------------------------------------------------------------------------
71
72 class CNBodyKernel
73 {
74 public:
75 CNBodyKernel();
76
77 ~CNBodyKernel();
78
79 // 更新N体
80 void UpdateNBody(Yreal deltaTime);
81
82 // 清空N体
83 void Clear();
84
85 // 设置引力系数
86 void SetGravitationCoefficient(Yreal c);
87
88 // 获得引力系数
89 Yreal GetGravitationCoefficient() const
90 {
91 return m_fGravitationCoefficient;
92 }
93
94 // 返回星体数目
95 Yuint GetStarsCount() const
96 {
97 return m_starsCount;
98 }
99
100 // 返回星体链表
101 const Star* GetStarsList() const
102 {
103 return m_starsList;
104 }
105
106 // 返回星体对象
107 const Star* GetStar(Yuint index) const;
108
109 // 移除星体
110 bool RemoveStar(Yuint index);
111
112 // 添加星体
113 void AddStar(
114 const Vector3& vPosition,
115 const Vector3& vVelocity,
116 const Yreal fWeight,
117 Ycolor color);
118
119
120 // 设置星体的重量
121 void SetStarWeight(Yuint index, Yreal weight);
122
123 // 设置星体的轨迹是否可见
124 void SetStarOrbitVisible(Yuint index, bool visible);
125
126 // 设置星体的颜色
127 void SetStarColor(Yuint index, Ycolor color);
128
129 Yuint GetCurrentOrbit() const
130 {
131 return m_currentOrbit;
132 }
133
134 const Vector3* GetStarPosition(Yuint index) const;
135
136 Yreal GetStarWeight(Yuint index) const;
137
138 bool GetStarOrbitVisible(Yuint index) const;
139
140 Ycolor GetStarColor(Yuint index) const;
141
142 Yuint GetStarID(Yuint index) const;
143
144 void GetSpaceCenter(Vector3& vCenter) const;
145
146 void GetWeightCenter(Vector3& vCenter) const;
147
148 private:
149 // 更新星体的位置,速度,动能
150 void UpdateStar(Star& star, Yreal t);
151
152 // 根据动能重新计算速度
153 void RecalculateStarVelocity(Star& star);
154
155 // 计算每个星体的当前受力
156 void CalculateStarsForce();
157
158 // 计算总势能
159 void CalculateAmountOfPotentialEnergy();
160
161 // 计算总动能
162 void CalculateAmountOfKineticEnergy();
163
164 // 计算总能量
165 void CalculateAmountEnergy()
166 {
167 CalculateAmountOfPotentialEnergy();
168 CalculateAmountOfKineticEnergy();
169 m_fAmountEnergy = m_fAmountOfPotentialEnergy + m_fAmountOfKineticEnergy;
170 }
171
172 private:
173 Yreal m_fGravitationCoefficient; // 引力系数
174 Yreal m_fAmountOfPotentialEnergy; // 当前总势能
175 Yreal m_fAmountOfKineticEnergy; // 当前总动能
176 Yreal m_fAmountEnergy; // 当前能量m_fAmountOfPotentialEnergy + m_fAmountOfKineticEnergy;
177
178 Star* m_starsList; // N体链表
179 Yuint m_starsCount; // N体数目
180
181 Yuint m_currentOrbit;
182 };
183
184 // --------------------------------------------------------------------------------------
185
186 #endif
1 /****************************************************************
2
3 File name : NBodyKernel.cpp
4 Author : 叶峰
5 Version : 1.0a
6 Create Date : 2014/09/19
7 Description :
8
9 *****************************************************************/
10
11 // --------------------------------------------------------------------------------------
12
13 #include "..\..\..\Why\YCommon_h\WhyMemoryDefines.h"
14 #include "NBodyKernel.h"
15 #include <assert.h>
16
17 // --------------------------------------------------------------------------------------
18
19 #define YD_ESP 0.00001f
20
21 // --------------------------------------------------------------------------------------
22
23 CNBodyKernel::CNBodyKernel()
24 {
25 m_fGravitationCoefficient = 100.0f;
26 m_fAmountOfPotentialEnergy = 0.0f;
27 m_fAmountOfKineticEnergy = 0.0f;
28 m_fAmountEnergy = 0.0f;
29
30 m_starsList = NULL;
31 m_starsCount = 0;
32
33 m_currentOrbit = 0;
34 }
35
36 CNBodyKernel::~CNBodyKernel()
37 {
38 Clear();
39 }
40
41 // 清空N体
42 void CNBodyKernel::Clear()
43 {
44 Star* pDel = m_starsList;
45 Star* pNext;
46 while (pDel)
47 {
48 pNext = pDel->pNext;
49 YD_DELETE(pDel);
50 pDel = pNext;
51 }
52
53 m_starsList = NULL;
54 m_starsCount = 0;
55 }
56
57 // 获得引力系数
58 void CNBodyKernel::SetGravitationCoefficient(Yreal c)
59 {
60 m_fGravitationCoefficient = c;
61
62 // 计算总能量
63 CalculateAmountEnergy();
64 }
65
66 // 计算每个星体的当前受力
67 void CNBodyKernel::CalculateStarsForce()
68 {
69 // 清空所有引力
70 Star* pStar = m_starsList;
71 while (pStar)
72 {
73 pStar->vForce = Vector3(0.0f, 0.0f, 0.0f);
74 pStar = pStar->pNext;
75 }
76
77 // 计算引力
78 Star* pCurrent = m_starsList;
79 Star* pNext;
80 while (pCurrent)
81 {
82 pNext = pCurrent->pNext;
83 while (pNext)
84 {
85 Vector3 vAB = pNext->vPosition - pCurrent->vPosition;
86 float disSqAB = D3DXVec3LengthSq(&vAB);
87 if (disSqAB < YD_ESP)
88 {
89 disSqAB = YD_ESP;
90 }
91 float disAB = sqrt(disSqAB);
92 Vector3 vForceDir = vAB/disAB;
93 Yreal fForce = m_fGravitationCoefficient*pCurrent->fWeight*pNext->fWeight/disSqAB;
94 Vector3 vForce = vForceDir*fForce;
95
96 pCurrent->vForce += vForce;
97 pNext->vForce -= vForce;
98
99 pNext = pNext->pNext;
100 }
101 pCurrent = pCurrent->pNext;
102 }
103 }
104
105 void CNBodyKernel::UpdateStar(Star& star, Yreal t)
106 {
107 // 加速度
108 const Vector3 acc = star.vForce/star.fWeight;
109
110 star.vPosition.x = star.vPosition.x + star.vVelocity.x*t + 0.5f*acc.x*t*t;
111 star.vPosition.y = star.vPosition.y + star.vVelocity.y*t + 0.5f*acc.y*t*t;
112 star.vPosition.z = star.vPosition.z + star.vVelocity.z*t + 0.5f*acc.z*t*t;
113
114 star.vVelocity.x += acc.x*t;
115 star.vVelocity.y += acc.y*t;
116 star.vVelocity.z += acc.z*t;
117
118 // 重新计算动能
119 star.CalculateKineticEnergyByVelocity();
120 }
121
122 void CNBodyKernel::UpdateNBody(Yreal deltaTime)
123 {
124 // 计算每个星体的当前受力
125 CalculateStarsForce();
126
127 // 更新星体的位置与速度
128 Star* pStar = m_starsList;
129 while (pStar)
130 {
131 UpdateStar(*pStar, deltaTime);
132 pStar = pStar->pNext;
133 }
134
135 // 能量守恒
136 CalculateAmountOfKineticEnergy();
137 CalculateAmountOfPotentialEnergy();
138
139 // 理论上的动能
140 Yreal fKineticEnergy = m_fAmountEnergy - m_fAmountOfPotentialEnergy;
141 if (fKineticEnergy < 0.0f)
142 {
143 fKineticEnergy = 0.0f;
144 }
145
146 Yreal r = fKineticEnergy/m_fAmountOfKineticEnergy;
147 pStar = m_starsList;
148 while (pStar)
149 {
150 if (r > YD_ESP)
151 {
152 pStar->fKineticEnergy *= r;
153 }
154 else
155 {
156 pStar->fKineticEnergy = 1.0f;
157 }
158 // 根据动能计算速度
159 pStar->CalculateVelocityByKineticEnergy();
160 pStar = pStar->pNext;
161 }
162
163 // 更新轨迹点
164 pStar = m_starsList;
165 while (pStar)
166 {
167 pStar->verticesOrbit[m_currentOrbit] = pStar->vPosition;
168 pStar = pStar->pNext;
169 }
170 m_currentOrbit++;
171 if (m_currentOrbit >= YD_ORBIT_VERTICES_COUNT)
172 {
173 m_currentOrbit = 0;
174 }
175 }
176
177 // 返回星体对象
178 const Star* CNBodyKernel::GetStar(Yuint index) const
179 {
180 if (index >= m_starsCount)
181 {
182 return NULL;
183 }
184
185 const Star* pStar = m_starsList;
186 while (index && pStar)
187 {
188 index--;
189 pStar = pStar->pNext;
190 }
191
192 return pStar;
193 }
194
195 // 移除星体
196 bool CNBodyKernel::RemoveStar(Yuint index)
197 {
198 if (index >= m_starsCount)
199 {
200 return false;
201 }
202
203 if (index == 0)
204 {
205 Star* pStar = m_starsList->pNext;
206 YD_DELETE(m_starsList);
207 m_starsList = pStar;
208 }
209 else
210 {
211 Star* pStar = m_starsList;
212 Yuint t = index - 1;
213 while (t && pStar)
214 {
215 t--;
216 pStar = pStar->pNext;
217 }
218 assert(pStar);
219
220 Star* pDel = pStar->pNext;
221 pStar->pNext = pDel->pNext;
222 YD_DELETE(pDel);
223 }
224
225 m_starsCount--;
226
227 Yuint id = 0;
228 Star* pStar = m_starsList;
229 while (pStar)
230 {
231 pStar->id = id;
232 id++;
233 pStar = pStar->pNext;
234 }
235
236 // 重算能量
237 CalculateAmountEnergy();
238
239 return true;
240 }
241
242 // 添加星体
243 void CNBodyKernel::AddStar(
244 const Vector3& vPosition,
245 const Vector3& vVelocity,
246 const Yreal fWeight,
247 Ycolor color)
248 {
249 Star* pNewStar = YD_NEW(Star);
250 pNewStar->vPosition = vPosition;
251 pNewStar->vVelocity = vVelocity;
252 pNewStar->fWeight = fWeight;
253 pNewStar->fRadiusScale = yf_pow(fWeight, 1.0f/3.0f);
254 pNewStar->CalculateKineticEnergyByVelocity();
255 pNewStar->color = color;
256 pNewStar->id = m_starsCount;
257 for (Yuint i = 0; i < YD_ORBIT_VERTICES_COUNT; i++)
258 {
259 pNewStar->verticesOrbit[i] = vPosition;
260 }
261
262 if (!m_starsList)
263 {
264 m_starsList = pNewStar;
265 }
266 else
267 {
268 Star* pStar = m_starsList;
269 while (pStar->pNext)
270 {
271 pStar = pStar->pNext;
272 }
273 pStar->pNext = pNewStar;
274 }
275
276 m_starsCount++;
277
278 // 重算能量
279 CalculateAmountEnergy();
280 }
281
282 // 设置星体的重量
283 void CNBodyKernel::SetStarWeight(Yuint index, Yreal weight)
284 {
285 if (index >= m_starsCount)
286 {
287 return;
288 }
289
290 Star* pStar = m_starsList;
291 while (index && pStar)
292 {
293 index--;
294 pStar = pStar->pNext;
295 }
296
297 pStar->fWeight = weight;
298 pStar->fRadiusScale = yf_pow(weight, 1.0f/3.0f);
299 pStar->CalculateKineticEnergyByVelocity();
300
301 // 重算能量
302 CalculateAmountEnergy();
303 }
304
305 // 设置星体的轨迹是否可见
306 void CNBodyKernel::SetStarOrbitVisible(Yuint index, bool visible)
307 {
308 if (index >= m_starsCount)
309 {
310 return;
311 }
312
313 Star* pStar = m_starsList;
314 while (index && pStar)
315 {
316 index--;
317 pStar = pStar->pNext;
318 }
319
320 pStar->bOrbitVisible = visible;
321 }
322
323 // 设置星体的颜色
324 void CNBodyKernel::SetStarColor(Yuint index, Ycolor color)
325 {
326 if (index >= m_starsCount)
327 {
328 return;
329 }
330
331 Star* pStar = m_starsList;
332 while (index && pStar)
333 {
334 index--;
335 pStar = pStar->pNext;
336 }
337
338 pStar->color = color;
339 }
340
341 // 计算总动能
342 void CNBodyKernel::CalculateAmountOfKineticEnergy()
343 {
344 // 动能
345 m_fAmountOfKineticEnergy = 0.0f;
346 Star* pStar = m_starsList;
347 while (pStar)
348 {
349 //pStar->CalculateKineticEnergyByVelocity();
350 m_fAmountOfKineticEnergy += pStar->fKineticEnergy;
351 pStar = pStar->pNext;
352 }
353 }
354
355 // 计算总势能
356 void CNBodyKernel::CalculateAmountOfPotentialEnergy()
357 {
358 m_fAmountOfPotentialEnergy = 0.0f;
359
360 Star* pCurrent = m_starsList;
361 Star* pNext;
362 while (pCurrent)
363 {
364 pNext = pCurrent->pNext;
365 while (pNext)
366 {
367 Vector3 vAB = pCurrent->vPosition - pNext->vPosition;
368 float disAB = D3DXVec3Length(&vAB);
369 float fPotentialEnergyAB = -m_fGravitationCoefficient*pCurrent->fWeight*pNext->fWeight/disAB;
370 m_fAmountOfPotentialEnergy += fPotentialEnergyAB;
371 pNext = pNext->pNext;
372 }
373 pCurrent = pCurrent->pNext;
374 }
375 }
376
377 const Vector3* CNBodyKernel::GetStarPosition(Yuint index) const
378 {
379 if (index >= m_starsCount)
380 {
381 return 0;
382 }
383
384 const Star* pStar = m_starsList;
385 while (index && pStar)
386 {
387 index--;
388 pStar = pStar->pNext;
389 }
390
391 assert(pStar);
392 return &pStar->vPosition;
393 }
394
395 Yreal CNBodyKernel::GetStarWeight(Yuint index) const
396 {
397 if (index >= m_starsCount)
398 {
399 return 0.0f;
400 }
401
402 const Star* pStar = m_starsList;
403 while (index && pStar)
404 {
405 index--;
406 pStar = pStar->pNext;
407 }
408
409 assert(pStar);
410 return pStar->fWeight;
411 }
412
413 bool CNBodyKernel::GetStarOrbitVisible(Yuint index) const
414 {
415 if (index >= m_starsCount)
416 {
417 return false;
418 }
419
420 const Star* pStar = m_starsList;
421 while (index && pStar)
422 {
423 index--;
424 pStar = pStar->pNext;
425 }
426
427 assert(pStar);
428 return pStar->bOrbitVisible != YD_FALSE;
429 }
430
431 Ycolor CNBodyKernel::GetStarColor(Yuint index) const
432 {
433 if (index >= m_starsCount)
434 {
435 return 0;
436 }
437
438 const Star* pStar = m_starsList;
439 while (index && pStar)
440 {
441 index--;
442 pStar = pStar->pNext;
443 }
444
445 assert(pStar);
446 return pStar->color;
447 }
448
449 Yuint CNBodyKernel::GetStarID(Yuint index) const
450 {
451 if (index >= m_starsCount)
452 {
453 return 0;
454 }
455
456 const Star* pStar = m_starsList;
457 while (index && pStar)
458 {
459 index--;
460 pStar = pStar->pNext;
461 }
462
463 assert(pStar);
464 return pStar->id;
465 }
466
467 void CNBodyKernel::GetSpaceCenter(Vector3& vCenter) const
468 {
469 vCenter = Vector3(0.0f, 0.0f, 0.0f);
470 if (!m_starsCount)
471 {
472 return;
473 }
474
475 Star* pStar = m_starsList;
476 while (pStar)
477 {
478 vCenter += pStar->vPosition;
479 pStar = pStar->pNext;
480 }
481
482 vCenter /= (Yreal)m_starsCount;
483 }
484
485 void CNBodyKernel::GetWeightCenter(Vector3& vCenter) const
486 {
487 vCenter = Vector3(0.0f, 0.0f, 0.0f);
488 if (!m_starsCount)
489 {
490 return;
491 }
492
493 Yreal weights = 0.0f;
494 Star* pStar = m_starsList;
495 while (pStar)
496 {
497 vCenter += pStar->vPosition*pStar->fWeight;
498 weights += pStar->fWeight;
499 pStar = pStar->pNext;
500 }
501
502 vCenter /= weights;
503 }

      程序启动后,会出现4个球体在运动。不知道大家有没有注意到:Win7的开机动画就是四个球体的运动,所以我这程序的默认也是生成四个球体。

通过UI控件,可以添加删除星体。没有上限,当然星体越多程序越慢。

可以显示每一个星体的运动轨迹

用户可以实时修改任意一个星体的质量

鼠标右键用于控制视角
键盘U用于开关UI用户界面.
通过UI用户界面可以设置三个球体的质量,设置万有引力系数,设置天体运行速度,设置球体的显示大小.

键盘0~9用于开关第N个球体运动轨迹的显示

键盘G,用于开关三维网格的显示
键盘C,用于开关坐标轴的显示
键盘P,用于暂停
键盘R,用于重置.

F11 全屏切换

标签