作业8
题目:
模拟绳子动画,包括基于物理的,和非物理的,应该修改的函数是:rope.cpp 中的void Rope::simulateEuler(... Rope::rope(...),,void Rope::simulateVerlet(...)
代码框架:
main:负责接收命令行参数,通过getopt捕获参数设置AppConfig的数据-》创建Application,设置渲染器
-
int getopt(int argc,char * const argv[ ],const char * optstring);用来分析命令行参数
-
参数argc和argv分别代表参数个数和内容,跟main( )函数的命令行参数是一样的。
-
参数 optstring为选项字符串
-
单个字符,表示选项
-
单个字符后接一个冒号:表示该选项后必须跟一个参数
-
单个字符后跟两个冒号,表示该选项后可以跟一个参数,也可以不跟。
-
-
全域变量optarg指向当前选项参数(如果有)的指针。
-
CGL:命名空间,包含应用层一下所有模块
application:应用程序
-
AppConfig类,负责从命令行接收信息,mass质点的质量,ks弹簧劲度系数,gravity重力,steps_per_frame每帧仿真步长数
-
Application类继承CGL库中的Renderer
-
初始化,调用rope的构造函数
-
渲染,通过opengl库:渲染点(质点),线(弹簧)
-
void Application::render() {
//Simulation loops
for (int i = 0; i < config.steps_per_frame; i++) {/* 每帧仿真步长数 */
/* 对两个绳子更新这一帧的位置 */
ropeEuler->simulateEuler(1 / config.steps_per_frame, config.gravity);
ropeVerlet->simulateVerlet(1 / config.steps_per_frame, config.gravity);
}
// Rendering ropes
Rope *rope;
/* 分别两个绳子,并设置颜色,和当前绳子*/
for (int i = 0; i < 2; i++) {
if (i == 0) {
glColor3f(0.0, 0.0, 1.0);
rope = ropeEuler;
} else {
glColor3f(0.0, 1.0, 0.0);
rope = ropeVerlet;
}
/* 绘制质点 */
glBegin(GL_POINTS);
for (auto &m : rope->masses) {
Vector2D p = m->position;
glVertex2d(p.x, p.y);
}
glEnd();
/* 绘制弹簧 */
glBegin(GL_LINES);
for (auto &s : rope->springs) {
Vector2D p1 = s->m1->position;
Vector2D p2 = s->m2->position;
glVertex2d(p1.x, p1.y);
glVertex2d(p2.x, p2.y);
}
glEnd();
/* 提交缓冲区到前台 */
glFlush();
}
}
rope:绳子类,包括质点数组,弹簧数组,simulateEuler基于物理的仿真,simulateVerlet非物理的仿真
mass:质点类,包含一个质点,质量,pinned是否是固定的,开始位置,速度,受力等
spring:弹簧类,包括一个弹簧,2个质点,长度,劲度系数k等
解:
首先安装库
sudo apt i n s t a l l l i b g l u 1 −mesa−dev f r e e g l u t 3 −dev \\
mesa−common−dev
sudo apt i n s t a l l xorg−dev #会自 动 安装 l i b f r e e t y p e 6 −dev
创建绳子
在构造函数中,遍历每个质点,调用mass:类和spring类的构造,并存放到rope的数组中,并且根据参数值,设置mass:对象和spring对象的属性
Vector2D step = (end - start) / (num_nodes - 1);
for(int i = 0; i < num_nodes; i++){
masses.push_back(new Mass(start + step * i, node_mass, true));
if(i){
springs.push_back(new Spring(masses[i-1], masses[i], k));
masses[i]->pinned = false;
}
}
simulateEuler()
首先遍历绳子的每个弹簧,根据胡可定律计算受到的力,包括ab质点的相互作用力,fr摩擦力,以及空气阻力,带入公式就好
然后遍历每个质点,如果非固定属性,那么根据首先根据f = ma 计算加速度,再根据欧拉方法计算质点的这一步长的位置,+=是上一步长的位置 + 这一步长的增量
for (auto &s : springs)
{
float length = (s->m2->position - s->m1->position).norm();
Vector2D dis = s->m2->position - s->m1->position;
Vector2D force = s->k * (length - s->rest_length) * dis / length;
s->m1->forces += force;
s->m2->forces -= force;
// damping
Vector2D reve = s->m2->velocity - s->m1->velocity;
Vector2D force1 = 0.05 * (reve.x * dis.x + reve.y * dis.y) * dis / length;
s->m1->forces += force1;
s->m2->forces -= force1;
// air damping
s->m1->forces -= 0.005 * s->m1->velocity;
s->m2->forces -= 0.005 * s->m2->velocity;
for (auto &m : masses)
{
if (!m->pinned)
{
m->forces += gravity;
m->velocity += m->forces / m->mass * delta_t;
m->position += m->velocity * delta_t;
m->forces = Vector2D(0, 0);
}
simulateVerlet()
作业告诉了这个公式, 带入就好
for (auto &s : springs)
{
float length = (s->m2->position - s->m1->position).norm();
Vector2D dis = s->m2->position - s->m1->position;
Vector2D force = s->k * (length - s->rest_length) * dis / length;
s->m1->forces += force;
s->m2->forces -= force;
}
for (auto &m : masses)
{
if (!m->pinned)
{
Vector2D temp_position = m->position;
m->forces += gravity;
Vector2D pos = m->position;
m->position += (1 - 0.00005) * (m->position - m->last_position) + m->forces / m->mass * delta_t * delta_t;
m->last_position = pos;
}
m->forces = Vector2D(0, 0);
}