参考:http://www.paulsprojects.net/opengl/cloth/cloth.html

写给我的室友:

布料仿真的常用方法就是将布料表达为三维网格,然后通过弹力+外力进行布料仿真。它在游戏中有着广泛的应用,如衣袖的摆动,旗帜的飘动;此外,也可以用于模拟试衣等。

模型初始化

在质点弹簧模型中,整个布料由网格点表达,而点与点之间的连线,我们称之为弹簧。

布料一个点的位置是由它周围的12个点控制的。其中8个是相邻点,另外4个是隔点的邻接点。

对于弹簧而言,存在三种弹簧,一种是结构弹簧,一种是剪切弹簧,还有一种是弯曲弹簧。它们分别与上图12根弹簧对应。

其中,结构弹簧模拟的是横向的力,为了阻止布料在横向产生较大的拉压变形。

剪切弹簧同理,是为了阻止布料在斜向产生较大的拉压变形。

而弯曲弹簧连接了两个相隔的点,它主要作用是在布料发生弯曲形变的时候,阻止它的过度弯曲,导致布料的角一下子坍塌。这个系数有时候也可以不考虑。

在具体的代码实现中,我们初始化点的位置、速度(初始化为0),质量(设为统一的值),以及是否固定(固定后将不受外力,即重力)。

接下来,执行六个循环,来初始化弹簧,对应于上图,可以避免重复边计算。对于弹簧,我们需要指定两个端点,弹性系数(所有的弹簧都一样),初始长度(跟位置有关,上下左右为1,斜边为根号2,红色边为2)

 //The first (gridSize-1)*gridSize springs go from one ball to the next,//excluding those on the right hand edgefor(int i=0; i<gridSize; ++i){for(int j=0; j<gridSize-1; ++j){currentSpring->ball1=i*gridSize+j;currentSpring->ball2=i*gridSize+j+1;currentSpring->springConstant=springConstant;currentSpring->naturalLength=naturalLength;++currentSpring;}}//The next (gridSize-1)*gridSize springs go from one ball to the one below,//excluding those on the bottom edgefor(int i=0; i<gridSize-1; ++i){for(int j=0; j<gridSize; ++j){currentSpring->ball1=i*gridSize+j;currentSpring->ball2=(i+1)*gridSize+j;currentSpring->springConstant=springConstant;currentSpring->naturalLength=naturalLength;++currentSpring;}}//The next (gridSize-1)*(gridSize-1) go from a ball to the one below and right//excluding those on the bottom or rightfor(int i=0; i<gridSize-1; ++i){for(int j=0; j<gridSize-1; ++j){currentSpring->ball1=i*gridSize+j;currentSpring->ball2=(i+1)*gridSize+j+1;currentSpring->springConstant=springConstant;currentSpring->naturalLength=naturalLength*sqrt(2.0f);++currentSpring;}}//The next (gridSize-1)*(gridSize-1) go from a ball to the one below and left//excluding those on the bottom or rightfor(int i=0; i<gridSize-1; ++i){for(int j=1; j<gridSize; ++j){currentSpring->ball1=i*gridSize+j;currentSpring->ball2=(i+1)*gridSize+j-1;currentSpring->springConstant=springConstant;currentSpring->naturalLength=naturalLength*sqrt(2.0f);++currentSpring;}}//The first (gridSize-2)*gridSize springs go from one ball to the next but one,//excluding those on or next to the right hand edgefor(int i=0; i<gridSize; ++i){for(int j=0; j<gridSize-2; ++j){currentSpring->ball1=i*gridSize+j;currentSpring->ball2=i*gridSize+j+2;currentSpring->springConstant=springConstant;currentSpring->naturalLength=naturalLength*2;++currentSpring;}}//The next (gridSize-2)*gridSize springs go from one ball to the next but one below,//excluding those on or next to the bottom edgefor(int i=0; i<gridSize-2; ++i){for(int j=0; j<gridSize; ++j){currentSpring->ball1=i*gridSize+j;currentSpring->ball2=(i+2)*gridSize+j;currentSpring->springConstant=springConstant;currentSpring->naturalLength=naturalLength*2;++currentSpring;}}

加入外力并更新位置


对于布料,维护两个状态,当前状态和下一状态,每隔10ms,我们根据已知的当前信息,根据公式计算下一状态信息,然后把下一状态转移为当前状态。

首先,我们计算每根弹簧形变产生的压力。

     //Calculate the tensions in the springs//计算弹簧压力for(int i=0; i<numSprings; ++i){//弹簧长度 = 前一个弹簧的位置 - 下一个弹簧的位置float springLength=( currentBalls[springs[i].ball1].position-currentBalls[springs[i].ball2].position).GetLength();//伸展 = 现在长度 - 原始长度float extension=springLength-springs[i].naturalLength;//压力 = 弹性系数 * 伸展 / 自然长度springs[i].tension=springs[i].springConstant*extension/springs[i].naturalLength;}

在这里使用的公式为 F = k△x。在上面代码中,又除以了自然长度,这是因为对于物理上完全相同的弹簧而言,弹性系数与长度有关,而在这里我们设为相同的,为了抵消这种影响,需要消除这个参数。

我们需要注意k的选取:如果k太小,布料就会过于柔软,呈现凹陷的状态,而如果k太大,布料会显得过于僵硬。

如果该点不是固定的,那么需要计算外力。

对于每个弹簧,我们对它的两个端点加上这个弹簧的弹力以及外力(在这里,外力为重力)。

//Loop through springs
for (int j = 0; j<numSprings; ++j)
{//If this ball is "ball1" for this spring, add the tension to the forceif (springs[j].ball1 == i){VECTOR3D tensionDirection = currentBalls[springs[j].ball2].position -currentBalls[i].position;tensionDirection.Normalize();//把力加上去force += springs[j].tension*tensionDirection;}//Similarly if the ball is "ball2"if (springs[j].ball2 == i){VECTOR3D tensionDirection = currentBalls[springs[j].ball1].position -currentBalls[i].position;tensionDirection.Normalize();force += springs[j].tension*tensionDirection;}
}

得到外力后,我们根据牛顿第二定律计算每个控制点新的位置:

//计算加速度
//Calculate the acceleration
VECTOR3D acceleration = force / currentBalls[i].mass;//更新速度
//Update velocity
nextBalls[i].velocity = currentBalls[i].velocity + acceleration*
(float)timePassedInSeconds;
//减少速度
//Damp the velocity
nextBalls[i].velocity *= dampFactor;//Calculate new position
//计算新的位置
nextBalls[i].position = currentBalls[i].position +
(nextBalls[i].velocity + currentBalls[i].velocity)*(float)timePassedInSeconds / 2;

之后,对球体和地板进行碰撞检测:

//Check against sphere (at origin)
//计算球体
if (nextBalls[i].position.GetSquaredLength()<sphereRadius*1.08f*sphereRadius*1.08f)nextBalls[i].position = nextBalls[i].position.GetNormalized()*sphereRadius*1.08f;//Check against floor
if (nextBalls[i].position.y<-8.5f)nextBalls[i].position.y = -8.5f;

最终把下一状态转移到当前状态,并计算新的法线(根据两个矢量的叉积):

//Calculate the normals on the current balls
for (int i = 0; i<gridSize - 1; ++i)
{for (int j = 0; j<gridSize - 1; ++j){VECTOR3D & p0 = currentBalls[i*gridSize + j].position;VECTOR3D & p1 = currentBalls[i*gridSize + j + 1].position;VECTOR3D & p2 = currentBalls[(i + 1)*gridSize + j].position;VECTOR3D & p3 = currentBalls[(i + 1)*gridSize + j + 1].position;VECTOR3D & n0 = currentBalls[i*gridSize + j].normal;VECTOR3D & n1 = currentBalls[i*gridSize + j + 1].normal;VECTOR3D & n2 = currentBalls[(i + 1)*gridSize + j].normal;VECTOR3D & n3 = currentBalls[(i + 1)*gridSize + j + 1].normal;//Calculate the normals for the 2 triangles and add onVECTOR3D normal = (p1 - p0).CrossProduct(p2 - p0);n0 += normal;n1 += normal;n2 += normal;normal = (p1 - p2).CrossProduct(p3 - p2);n1 += normal;n2 += normal;n3 += normal;}
}//Normalize normals
for (int i = 0; i<numBalls; ++i)currentBalls[i].normal.Normalize();
}

拓展

以上算法提供了布料模拟的一个基本雏形,在可扩展性上,可以考虑到这些问题:

1) 没有精确的碰撞检测(包括自身的碰撞建策 和 与外物的碰撞检测)

2) 布料是纯光滑的,没有考虑到质地(摩擦因子)

3) 外力较为单一

4)把布料抽象为面片,没有考虑到厚度

[图形学] 布料仿真(质点弹簧模型)相关推荐

  1. Bullet 布料仿真的底层算法分析

    Bullet 可变物体的底层算法分析 1. 计算机图形学中可变建模方法 1.1 质点-弹簧模型(离散) 1.2 有限元连续体模型(连续) 2. 布料模拟的两种主要算法 2.1 隐式时间积分 2.2 基 ...

  2. 图形学笔记(十九)动画 —— 动画的历史、关键帧插值、物理仿真、质点弹簧系统、粒子系统、(反向IK)动力学、Rigging 绑定、Blend Shapes、动作捕捉

    图形学笔记(十八)光场.颜色和感知-- 光场相机(全光函数.光线和光场的定义).可见光谱.谱功率密度.颜色的生物学基础.Tristimulus Theory.同色异谱.加色与减色系统.颜色空间SPD ...

  3. 计算机图形学【GAMES-101】14、动画(物理模拟、质点弹簧系统、粒子系统、运动学、动作捕捉、欧拉方法)

    快速跳转: 1.矩阵变换原理Transform(旋转.位移.缩放.正交投影.透视投影) 2.光栅化(反走样.傅里叶变换.卷积) 3.着色计算(深度缓存.着色模型.着色频率) 4.纹理映射(重心坐标插值 ...

  4. [计算机图形学]动画与模拟:关键帧动画、质点弹簧系统、运动学与绑定(前瞻预习/复习回顾)

    一.动画的简要概念 动画和语言一样,一开始都是作为传达信息的工具.什么是动画呢?简单的理解就是让画面变成"活的",也就是让它们能够动起来,其次需要一定的美观.在图形学上,我们可以把 ...

  5. adams与matlab联合仿真天线,雷达天线模型MATLAB与ADAMS联合仿真 实验.doc

    雷达天线模型MATLAB与ADAMS联合仿真 实验 雷达天线模型MATLAB与ADAMS联合仿真实验 1.导入雷达天线机械系统模型 启动ADAMS,弹出如图1所示的对话框,选择"Open a ...

  6. 基于质点弹簧系统的布料模拟修改版

    修改了上一篇博客的一些算法,质点的存储方式,弹簧的存储方式等,是一次大改. 不再直接画点和线,而是使用三角形进行绘制. 增加了简单的光照,渲染,一些最简单的碰撞检测. 添加了风力. 这次的效果比之前好 ...

  7. Unity 布料仿真笔记2 欧拉积分 Verlet积分分析

    前言 An object at rest remains at rest, unless acted upon by an exterior force. An object in motion re ...

  8. 计算机图形学流体仿真mac网格,用于图形学的流体仿真20教程.docx

    第I部分 基础流体方程流体无所不在,从我们呼吸的空气到占据整个地球三分之二的海洋,就是它,形成了我们这个星球上那些最美丽最震撼景象:从溅起的水花,到冲天的火焰,到缭绕的烟雾,流体仿真已经成为了计算机图 ...

  9. Simulink学习——弹球仿真三维动画模型(Simulink3D演示动画学习01)

    前有一期我们学习了如何进行物理世界的简单建模仿真,如弹球仿真 https://onebigsoap.blog.csdn.net/article/details/108503788 ,其中可以看到弹球的 ...

最新文章

  1. iOS-----Xcode-Debug尝试
  2. Spring核心--IOCAOP
  3. 实现微服务架构-微服务架构需要解决的问题
  4. 前滴滴产品总监刘滢:从这里了解新零售的本质
  5. jeecg公开培训课马上开始8点30
  6. python学习课后练习题_python初步学习-练习题
  7. sf | 判断点线面等几何对象的空间位置关系
  8. Uva 10061 进制问题
  9. android中的oom,Android OOM Adjustments
  10. kafka下载及安装
  11. 关于笔记本电脑网卡出问题的简单解决
  12. 面试:JavaScript基础概念
  13. logstash中无法解析nginx日志中的\x09类似字符导致服务停止
  14. Go开发微信小程序第三方SDK
  15. java怎么开发图形界面_Java Swing 图形界面开发简介
  16. STM32寄存器ODR,BSRR和BRR
  17. 推荐一些国内的jQuery CDN免费服务[转]
  18. Cascade CNN
  19. python爬虫设计模式_Python爬虫进阶一之爬虫框架概述
  20. 企业信息化有什么价值?如何实现?

热门文章

  1. 全网最全json数据结构可视化工具汇总
  2. redis常用linux指令(无介绍快使用)
  3. arm开发板用无线网卡连接ap
  4. win10怎么取消登录密码
  5. Ubuntu 20.04 设置窗口打不开或者不显示解决方法
  6. excel中用Index函数取出数组中任意一个位置的值
  7. [Android 基础] -- Android 属性系统简介
  8. 【NLP】用ML实现中文短文本分类(二分类)
  9. 常用工具箱:打开手机日志界面
  10. 概率论—贝叶斯定理 解析