计算机图形学-动画Animation-仿真物理模拟Simulation
*本节是games101课程记录的学习笔记 *
本节大致介绍:
- 动画原理
- 物理模拟方法有什么
- 怎么模拟物体受力
- 怎么计算出受力之后物体的位置
1 动画
动画最通俗的讲,就是将很多图片按照时间顺序呈现出来
那动画是怎么制作出来的呢?
关键帧法
就是在关键位置的动画图像绘制出来,中间的部分根据关键部分补充,其实也可以叫做差值
所以关键帧即是本质上也叫做差值,区别于线性插值
物理模拟动画
就是根据物理公式,计算下一时刻他应该在的位置,比如牛顿的定律\
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> F = m a F = ma </math>F=ma
其实就算是布料模拟动画出来的话,其实只需要将布料细分成一个一个的网格 , 只要我知道了每一个点与其他点的受力情况, 我就可以计算他的加速度, 从而更新他的位置, 实现仿真的动画
还有一个就是流体的动画,一般考虑粒子 法, 这种动画其实不是一个一个k出来的,就是按照物理规律自然的实现了这个动画过程,不需要人为的手动的去k帧
注意, 一般水这种模拟我们的制作其实是分两步的去看的
- 根据物理受力, 计算模拟水的这些所有的点的受力是如何, 就是它在时刻t应该在哪一个地方
- 然后便利所有的点, 根据你想要的效果去渲染每一个点,使其像真实的水外观
所以,我们发现,只需要我们实现一个物体每一个微元的受力情况,计算他的状态, 那就可以实现物理仿真
2 质点弹簧系统
综上, 我们可以引入一个很简单的模型实现简单的物理模拟,模拟时,我们不需要过度关心渲染的好不好看 , 而是注意力集中到物体在力的作用下运动的真不真实
上图就是质点模拟受力情况呈现的仿真悬挂
2.1公式
弹簧质点系统就是用的胡克定律,假如只有两点a 和 b 的话,他之间其中a 往b方向去的力就是:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f a − > b = − k s ( b ⃗ − a ⃗ ) f_{a-> b} =- k_s(\vec{b} -\vec{a}) </math>fa−>b=−ks(b −a )
其中b 到a的力就和上式相反
但是弹簧有长度, 所以更准确的表示应该是算上弹簧的变化量的大小\
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f a − > b = − k s b ⃗ − a ⃗ ∣ ∣ b ⃗ − a ⃗ ∣ ∣ ( ∣ ∣ b ⃗ − a ⃗ ∣ ∣ − L ) f_{a-> b} =- k_s\frac{\vec{b} -\vec{a}}{||\vec{b} -\vec{a}||} (||\vec{b} -\vec{a}|| - L) </math>fa−>b=−ks∣∣b −a ∣∣b −a (∣∣b −a ∣∣−L)
- L是原来弹簧的长度
- ||b - a||表示弹簧伸长之后的长度大小
- 中间的比值表示归一化,表示力的方向
但是如果我们运用这个公式的话,由于没有能量损失, 会一直运动下去,所以我们就可以给他们加上一个与速度方向相反的力,使他停下来,
在此之前,声明一下模拟中怎么表示多阶导数 , 以下分别表示位移x , 位移的一阶导数速度v , 以及二阶导数加速度 a
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x x ˙ x ¨ x \dot{x} \ddot{x} </math>xx˙x¨
现在对于b物体, 如果想让它停下来,就加一个与他速度相反的力 f, 大小就速度乘上一个系数。注意b点表示的是b的速度
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f ⃗ = − k d b ⃗ ˙ \vec{f} = -k_d\dot{\vec{b}} </math>f =−kdb ˙
现在我们把这个思想加入到弹簧公式中,假如a b被拉长了, 那么b受到的恢复原状的力:\
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f a − > b = − k d b ⃗ − a ⃗ ∣ ∣ b ⃗ − a ⃗ ∣ ∣ ⋅ ( b ˙ − a ˙ ) ⋅ b ⃗ − a ⃗ ∣ ∣ b ⃗ − a ⃗ ∣ ∣ f_{a-> b} =- k_d\frac{\vec{b} -\vec{a}}{||\vec{b} -\vec{a}||}\cdot (\dot{b} - \dot{a})\cdot \frac{\vec{b} -\vec{a}}{||\vec{b} -\vec{a}||} </math>fa−>b=−kd∣∣b −a ∣∣b −a ⋅(b˙−a˙)⋅∣∣b −a ∣∣b −a
其中\
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> − k d b ⃗ − a ⃗ ∣ ∣ b ⃗ − a ⃗ ∣ ∣ -k_d\frac{\vec{b} -\vec{a}}{||\vec{b} -\vec{a}||} </math>−kd∣∣b −a ∣∣b −a
表示b受到的恢复原状的力的方向, 由b指向a , 没问题,而其中\
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ( b ˙ − a ˙ ) ⋅ b ⃗ − a ⃗ ∣ ∣ b ⃗ − a ⃗ ∣ ∣ (\dot{b} - \dot{a})\cdot \frac{\vec{b} -\vec{a}}{||\vec{b} -\vec{a}||} </math>(b˙−a˙)⋅∣∣b −a ∣∣b −a
表示b与a的速度的差值在ab方向上的投影的大小,是一个数,所以让弹簧趋于停下来的力 的大小其实是由这个决定的,如果速度没有差值, 也就是没有相对运行, 那这个让弹簧趋于停下来的力就不存在 (注意不是ab之间的弹力),所以这个让弹簧趋于停下来的力的大小由相对运动决定的, 而弹簧力的大小由相对位移决定
2.2布料模拟
如果我们使用简单的弹簧质点模拟一块布,是不行的,下图单纯的网格是不行的,没有考虑布的抵抗发生切变的力,就是非平面去弯曲它,它有趋向复原的特性
现在我们改一下布料的表示结构使其加上对角的线,但是这个还是不可以实现非平面外的弯曲复原效果,比如你竖着对折, 他就不会产生弯曲复原效果的力出来, 因为没有任何一个线发生了拉伸
所以,再改进一下,就是每一个点, 都与隔着自己一个位置的点链接一个线。这样, 不论你怎么弯曲折叠,都会有线条发生拉伸。会产生力
注意,红线产生的力是很小的,因为虽然有抵抗发生切变的力 , 但是不还是一般会自己发生小的弯曲的,而蓝线的力是非常大的。这样的结果就很好的表示布料了
3.粒子系统
使用粒子表示物体。 只需要考虑粒子之间的相互作用就行。适合模拟水, 烟雾, 魔法特效什么的。
粒子模拟一个物体动画时考虑的是两个重要问题:
- 怎么表示物体粒子之间的力,将这个力模拟清楚,比如你是要模拟水还是灰尘
- 怎么解出来这个粒子位置位于哪里
之后才是更具你模拟出来的粒子的位置渲染出来,比如渲染成玻璃还是渲染成水,还是绿色蓝色的。
比如天体系统
水仿真
4.解受力方程-欧拉方法
上一节我们知道了使用什么样的结果和受力的公式表示这些物体的位置, 问题来了,我们怎么使用这些公式计算出来, 粒子或者网格受力之后的位置在哪呢?答案是欧拉方法
速度场
使用 V (x , t)表示一个粒子在位置x ,时刻t的速度是多少,这就形成一个速度场
微分方程
所以速度场的方程可以写成这样的微分方程形式
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> d x d t = x ˙ = v ( x ⃗ , t ) \frac{dx}{dt}=\dot{x} = v(\vec{x} , t) </math>dtdx=x˙=v(x ,t)
现在使用任意位置的速度和欧拉方法计算任意时刻的位置:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> x t + Δ t = x t + Δ t ⋅ v ( x ⃗ , t ) x_{t + \Delta{t}} = x_t + \Delta{t}\cdot v(\vec{x} , t) </math>xt+Δt=xt+Δt⋅v(x ,t)
其中速度表示为
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> v ( x ⃗ , t ) = x ˙ t + Δ t = x ˙ t + Δ t ⋅ x ¨ t v(\vec{x} , t) = \dot{x}_{t + \Delta{t}} = \dot{x}_t + \Delta{t}\cdot \ddot{x}_t </math>v(x ,t)=x˙t+Δt=x˙t+Δt⋅x¨t
相当于就是使用当前的速度和加速度,作用在原来现在所在的位置上,计算出下一个位置
误差+解决办法
不难看出欧拉方法是有误差和不稳定性的,比如步长不同,最后的运动轨迹就差很远
怎么解决这些不稳定性呢?有很多方法可以减小这些误差和不稳定性:
中点法
根据步长先假设的走一次,得到下一个位置 a,然后我们真正产生位移使用的速度不是原来的位置的,而是使用中点 b 处的速度,将这个速度作用在原来位置, 最后产生的位移就是c , 这样减小误差
适应性步长法
其实就是根据给定步长先走一步, 再根据给定步长的一半的步长走两步,对比两个的位置,如果差的很远, 就使用给定步长的一半的步长走两步,这样可以减小误差
最后,更使用这些减小误差的方法计算出位置,就可以实现出模拟动画了!