0%

【Games 101】HomeWork 8:质点弹簧系统

HomeWork 8:质点弹簧系统

这节课比较简单,都是套公式就行;

有个点得提一下,一开始sb了,在src里面间的build文件夹,然后cmake,结果一直报错…

实际上是应该在上一级建的…(也就是 build 和 src 文件夹同一级)

知识点参考的课程:

【Games 101】Lec 21:动画

rope.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
构造函数
这个构造函数应该可以创建一个新的绳子(Rope) 对象,该对象从start 开始,end 结束,包含 num_nodes 个节点。

- start:起始节点的坐标
- end:最终节点的坐标
- num_nodes:节点个数
- node_mass:节点的质量
- k:弹簧的弪度系数
- pinned_nodes:弹簧两个端点的索引
*/
Rope::Rope(Vector2D start, Vector2D end, int num_nodes, float node_mass, float k, vector<int> pinned_nodes)
{
// 初始化质点
for (int i = 0; i < num_nodes; ++i)
{
// 转成double再做除法,否则就会截断成int
Vector2D pos = start + (end - start) * ((double)i / ((double)num_nodes - 1.0));
masses.push_back(new Mass(pos, node_mass, false));
}

// 初始化弹簧
for (int i = 0; i < num_nodes - 1; ++i)
{
springs.push_back(new Spring(masses[i], masses[i + 1], k));
}

for (auto &i : pinned_nodes)
{
masses[i]->pinned = true;
}
}

// 显式 / 半隐式欧拉法
void Rope::simulateEuler(float delta_t, Vector2D gravity)
{
// 实现胡克定律:遍历所有的弹簧,根据拉伸的长度计算拉力(套公式就行)
for (auto &s : springs)
{
double len = (s->m1->position - s->m2->position).norm();
s->m1->forces += -(s->k) * (s->m1->position - s->m2->position) / len * (len - s->rest_length);
s->m2->forces += -(s->k) * (s->m2->position - s->m1->position) / len * (len - s->rest_length);
}

// 更新 加速度,速度,位置(也是套公式就行)
for (auto &m : masses)
{
if (!m->pinned)
{
/*
加上重力作用下的力,然后计算新的速度和位置
隐式:先更新速度,再更新位置
显式:先更新位置,再更新速度(直接就飞出去了...)
*/

// 加上质点的重力,重力 = 重力加速度 * 质量
m->forces += gravity * m->mass;

// 增加全局阻尼力
float k_d = 0.005;
Vector2D f_d = -k_d * m->velocity;

// 加上阻尼的力
m->forces += f_d;

// 加速度
Vector2D a = m->forces / m->mass;

m->velocity += a * delta_t; // 再更新速度
m->position += m->velocity * delta_t; // 先更新位置,就是用上一时刻的速度更新
}

// Reset all forces on each mass
m->forces = Vector2D(0, 0);
}
}

// 显式 Verlet 法
void Rope::simulateVerlet(float delta_t, Vector2D gravity)
{
for (auto &s : springs)
{
double len = (s->m1->position - s->m2->position).norm();
s->m1->forces += -(s->k) * (s->m1->position - s->m2->position) / len * (len - s->rest_length);
s->m2->forces += -(s->k) * (s->m2->position - s->m1->position) / len * (len - s->rest_length);
}

for (auto &m : masses)
{
if (!m->pinned)
{
/*
显示Verlet法:把弹簧的长度保持原长作为约束,移动每个质点的位置
也是套公式就行:
x(t+1) = x(t) + (1 - damping_factor) * ([x(t) - x(t-1)] + a * dt * dt)
可以看到需要 x(t-1),所以得先存一下
*/

// 加上质点的重力,重力 = 重力加速度 * 质量
m->forces += gravity * m->mass;
Vector2D a = m->forces/m->mass;

// x(t-1)
Vector2D lastPosition = m->position;

// 阻尼
float damping_factor = 0.00005f;

// x(t+1) = x(t) + (1 - damping_factor) * ([x(t) - x(t-1)] + a * dt * dt)
m->position += (1 - damping_factor) * (m->position - m->last_position + a * delta_t * delta_t);
m->last_position = lastPosition;
}
m->forces = Vector2D(0, 0);
}
}

结果:

欢迎关注我的其它发布渠道