今天我们来到了101的最后一个章节:动画与模拟。
什么是动画?这个问题其实也不用我多赘述了,简单来说就是一系列图片连续播放得到的就是动画,比如电影:一秒二十四张图片,视频:一秒三十张图片,一些FPS游戏要求甚至更高:CS我的FPS是300多(嘿嘿)。
中间插播了一些动画发展的历史,为了防止我被维权警告,就跳过了。直接进入:关键帧动画。
所谓的关键帧动画:由动画制作者制作关键帧,关键帧与关键帧之间的帧由电脑或者人工生成。
本质上来说,关键帧与关键帧之间的补帧就像之前我们学会的插值一样,不过是在两个参数之间补充过渡的这么一个过程罢了,但是帧这个东西本身的复杂程度绝对不是一两个参数可以概括的。
显然,对于这种关键帧之间的插值,傻傻地使用线性插值只会显得突兀,我们更好的方法是让这些插值后的曲线更平滑。
物理上的仿真
事实上,更多的时候我们使用物理仿真来计算模拟物体的变化。
比如一个小球,我们可以根据抛物线方程和重力来模拟一个小球被抛出后的位置变化。
在这里我们要引入一个比较经典的模拟系统:质点弹簧系统。
都是初中物理就学过的东西:胡克定律,拉伸时互相对另一个质点产生反方向的作用力,其中ks应该是弹簧的一个系数。
这里说明一下:在字符头上打点在这里表示导数的意思:比如用x来表示点的位置的话,x的一阶导表示速度,二阶导表示加速度。
在我们之前的模型中,有一个很大的问题:我们没有设置摩檫力,这将导致我们的质点弹簧系统永远地运动下去,而这显然不是我们想要地,所以我们也要设置一个摩檫力。但显然我们也不能直接生搬硬套在所有情况,更多地还是考虑质点间的相互作用。
于是我们进一步优化,最后面的归一化是计算方向,而前面的红框内的数直接将b的速度(向量)与a的速度相减,这里也突出了一点就是摩檫力是速度差距引起的,而之前弹簧引起的相互作用力则是由长度差距引起的。
有了以上的理论基础,我们就可以把简单的两个点连接一条弹簧的基础的系统扩展到更大的、更复杂的结构里。
可是这样的结构有一些与现实中的差距:他们缺少剪切力的部分。
总的来说,我们针对两个对角线方向的力分别添加了弹簧,这样沿对角线方向拉的话新加的弹簧就能阻止这种变化,同时我们将中间相隔一个点的两个点也连接起来,这样当我们想要用折叠的方式时也会受到弹簧的阻碍。
显然虽然我们已经进行了很多改进,但是这种质点弹簧系统本身还是适用性不强,我们这里再介绍一个新的系统:粒子系统。
粒子系统的概念也非常简单:就是由大量的体积足够小的粒子组合而成,在游戏中的无论雾,爆炸,尘埃等效果基本都是用粒子系统实现。那显然粒子系统最大的问题就是有时我们生成的具体的粒子的数量过大,那么这个时候如何管理粒子与粒子之间的作用力以及如何加速生成粒子就是一个很重要的问题了。
对于动画中的每一帧:
创建新粒子,计算粒子间作用力,更新粒子状态,移除“死”粒子,渲染。显然,这其中最困难的部分就在于:如何计算粒子间的作用力。
这里列举了一系列的作用力如:重力,电磁力,摩檫力,万有引力等。
进入下一个主题:运动学。
运动学研究的就是我们在制作动画时,如何模拟人体的运动方法。
比如现在的这样一个模型,主要由几种结构组合而成,有的可旋转,有的可伸长等。
这样的运动学可以随时任意地调整自己的身体结构的位置与角度,可以说非常方便。但是这对于需要适用这样一副骨骼模型的设计者来说,就非常的僵硬了:设计者往往为了实现某个特定的动作而不断地去试错性地调整骨骼,非常麻烦。能不能让骨骼自己运动以做出某个动作呢?
这就是所谓的逆运动学:我们先确认最后结构的位置,然后反向解骨骼需要移动的方法,这个过程可以说非常复杂(就像一个是出题人出题,而逆运动学就是去解题的那个人),且有时候这个解不唯一(当然也有可能无解)。
接下来进入下一个部分:rigging,骨骼绑定。
其实绑骨这个翻译,就已经能够充分说明rigging的作用了。
还有一种方法是重新开始运用之前插值的内容,假设我们已经有两种物理状态,我们只需要在这两种状态之间插值即可。
动捕,这也是老生常谈了。
我们让真人演员穿上动捕的装备,接着通过读取演员身上设备的数据,就可以得到需求的动画与仿真。
显然,动捕的好处和坏处都非常明显:足够真实,足够大的数据样本呢,但是花费高昂,且对动捕演员的要求极高。
这里展示了一部(动画)电影的从零开始全部实现的一个流程,具体细节就不展开讲了,大家也都看得懂。
接下来的部分,是一些更难的、更具体的、涉及到数学与计算的部分。
我们假设有一个理想的速度场:在这个场内任何一点的速度都是已知的,更准确地说:只要知道了时间和位置信息,我们就可以知道这个场内的任何一个点的速度。
我们可以用一个常微分方程来写出这个速度。
于是,我们只要有一个初始位置,之后在特定时间的位置就可以由速度和时间表示出来(我们的速度是已知的常微分方程),就可以计算出属于他的轨迹。
具体来说怎么计算呢?我们可以先有一个时间间隔,然后我们分别计算初始时间经过一个时间间隔后的位置和速度即可,只要这个时间间隔足够小(采样率足够大),我们就可以完整地算出运动轨迹。而所谓的欧拉方法指的其实就是:我们所有的计算所需的值都使用上一帧的,也就是全部用上一帧的参数来计算下一帧的参数。
欧拉方法有两个比较显著的缺点:具有误差与不稳定性。
如何理解呢?
第一张图中的螺旋线的速度场,无论我们的时间间隔怎么取,他的速度作为速度场的切线,都一定不会沿着螺旋线的原方向走;第二张图中的收敛线,甚至会产生正反馈现象:速度方向距离速度场真正的方向越来越远。
对于误差,我们只需要降低时间间隔就可以尽量减小,但是对于不稳定,这就是一个问题,是必须从源头上解决的问题。
既然有问题,我们就去解决问题。
这里提供了若干解决的办法。
中点法:当我们计算出下一帧的位置与速度时,我们不取最终得到的值,而是取步长一半的值,将这个中点的速度应用在我们原来的点,就能得到一个新的速度和位置的结果。本质上依然是欧拉方法,只是把原来一次的工作量扩张到了两次。
自适应步长:我们在原来一次欧拉方法的时间里执行两次欧拉方法,一次计算正常的速度,第二次从第一次欧拉方法得到的位置的中点处重新计算一次,如果这两次计算结果差异过大则需要细分。
隐式欧拉方法:简单地说因为我们已知物体受力情况的前提下,我们根据下一时刻的加速度来列方程求解现在时刻的速度和位置,这样可以解决欧拉方法的不稳定性问题。
话说回来,我们一直在反复强调的:稳定性,具体的定义到底是什么呢?
首先,我们有一个局部的截断误差(每一步产生的误差)和一个总和误差(误差的总和),我们一般用这两个误差值来衡量稳定性。当然我们的关注点其实并不是在误差本身,不是说误差小就是稳定的而误差大的就是不稳定的(当然我们肯定希望误差尽可能小),我们更多的关注在误差的阶。比如隐式欧拉方法的误差我们认为是一阶(常量级),其中的h既是步长。什么意思呢?对于一阶的误差,我们将步长缩小一半,则误差也会缩小一半。
既然涉及到数值的计算,我们肯定绕不开:龙格库塔方法。这个是一类方法,主要用于求解常微分方程的初解,其中比较常用的是RK4的方法,具体的原理就不展开说了,大家可以自行查阅,ppt上将算式列了出来,RK4的意思就是误差是四阶的,这意味着步长减小一半的话,误差能减小到十六分之一,可见对于误差是能够大大减小的。
刚体的模拟:不会发生形变的物体,本质上和粒子系统中的一个小粒子区别不大,不过最大的区别可能就是刚体要比粒子更大(绕口令是吗),所以对于刚体有更多的状态信息如角速度等。
既然说完了刚体,让我们再来讨论有关流体的模拟。在这之前,我们需要先提出一个思想:
verlet integration:是一种数值分析方法,他的思路就是直接使用物体的位置而不使用速度来计算物体的运动轨迹。这种算法的好处在于避免了计算速度带来的误差累积,在保守的场中非常好用。
而显然,我们在模拟流体时,无论是力的方向还是整个场的大小,都相对来说比较稳定,这就非常使用使用verlet 积分方法。
在这个基础上,我们再对流体做一些基本的假设:比如水,水是不可压缩的,也是不可拉伸的(谁能拉伸水?),水的密度都是一样的。所以我们对于模拟过程中,如果有出现密度与预料中不同的情况,我们直接进行值的更迭。可问题是我们如何更迭呢?答案很简单:梯度下降!
最后值得一提的是:verlet积分并不是基于物理的方法,也就缺乏物理上能量的假设,所以关于能量的损失,有可能有,如果没有我们也需要人为地添加一些能量的损失。
在这里我们列举出两大有关模拟的方法:欧拉法和拉格朗日方法。(这里的欧拉法与之前用于解常微分方程的欧拉法并不是一个东西)
欧拉法又称网格法,具体来说就是将整个场看作一个个网格组成的场,我们不研究具体的物体运动状态变化,而是专注在网格内状态的变化;拉格朗日法就是我们之前的模拟思路:我们就一个个生成需要研究的物体,然后一个个地独立研究运动轨迹并考虑相互作用这样。
至此,我们的计算机图形学入门也算是彻底学完了。哦么得多!其实我自己都没想到花这么久才学完这些东西,主要是中途遇到了诸如开题的学校方面的事,也花了点时间去尝试找找实习,所以学习过程也一拖再拖,现在也只是把内容才浏览完,事实上作业也都还没做。就我个人而言,首先这门课肯定是有趣的,他涉猎非常广,又与我们生活有紧密联系,其次这门课其实挺难的,我看每次的作业的框架都要看好久才看得懂,更别提还要涉及到一些如物理和数学上的知识了。还有就是学这门课让我明白一个道理就是:不要把学习的周期拉太长,知识的遗漏只是一方面,主要是你的思维没有去熟悉这门课的体系,很多时候你只是在接受知识,而很难自己去举一反三,这样的学习只能说效率非常一般,所以如果是再给我一次选择的机会,我宁愿压缩到两到三周来全部学完这门课的知识,相信这样我的记忆会深刻很多。
我会尽快把作业八在内的所有作业全部做完,接着希望能在过年前再突击GAMES202,加油!