《The CG Tutorial》阅读笔记——动画 Animation

这段时间阅读了英文版的NVidia官方的《The CG Tutorial》,通过它来学习基本的图形学知识和着色器编程。

在此做一个阅读记录。


动画 Animation

一、基于时间的运动 Movement in Time

实现动画渲染,需要应用程序对时间进行监测,并将它作为一个全局变量传递给着色器。

尽量在GPU上使用顶点着色器执行动画计算是一种高效的动画实现方式,它能够释放CPU,让CPU处理更多的复杂计算,比如碰撞检测,人工智能与游戏玩法。

二、一个做脉冲运动的对象 A Pulsating Object

1.  简介

下面会展示一个如何让一个对象周期性膨胀变形的例子。这个例子的目标是将时间作为输入参数,然后根据时间修改对象几何体的顶点位置。更确切地说,你需要沿着表面法线移动表面顶点的位置。

根据时间的变化,改变顶点位移Displacement的幅度,就可以创建一个膨胀或脉冲效果。

2.  位移计算 Displacement Calculation

在上述动画实现的过程中,一个重要的过程是计算顶点在法线方向上的位移displacement。你可以为displacement选择一个你喜欢的函数,比如:

displacement = time

也许你不希望物体随时间无限变大,那么你可以选择使用一个周期函数来计算displacement,比如sin函数:

displacement = 0.5 * (sin(time) + 1)

  • 在着色器中,sin/cos函数的效率等同于加减法。

此外,你还可以通过为displacement增加参数来控膨胀的幅度、频率,甚至改变膨胀效果:

displacement = scaleFactor * 0.5 * (sin(position.y * frequency * time) + 1)

  • scaleFactor表示了膨胀的幅度
  • frequency表示膨胀的频率
  • position.y用于让不同的顶点具有不同的膨胀幅度,也可以替换成其他任意与顶点属性有关的函数
  • 在这里,我们可以对scaleFactor * 0.5做预计算,并将它储存为一个全局变量,以降低顶点着色器的运算压力。

三、粒子系统 Particle Systems

1.  简介

有时,不同于让顶点在网格中运动,我们希望把每一个顶点都作为一个独立的对象,或者称为一个粒子。而遵循某种规律运动的粒子的集合被称为粒子系统。粒子系统会随着时间发生变化。

粒子的运动规律可以使用一个方程来描述,比如物理中的简单矢量运动学方程:

pfinal = pinitial + v * t + 0.5 * a * t2

  • pfinal是粒子的最终位置
  • pinitial­是粒子的初始位置
  • v是粒子的初始速度
  • a是粒子的加速度
  • t是时间

2.  计算粒子位置

在程序中,通过监测全局时间globaTime并将它作为统一参数传递给顶点着色器,当每个粒子被创建时,粒子创建的时间被作为一个变量tInitial传递给顶点着色器。为了知道粒子被激活的时间,你需要使用globaTime减去tInitial:

float t = globaTime - tInitial

再将t代入上一节中的公式:

float4 pFinal = pInitial + vInitial * t + 0.5f * acceleration * t * t

对象空间的位置还需要被转换到裁剪空间:

oPosition = mul(ModelViewProjectionMatrix, pFinal)

3.  计算粒子尺寸 Computing the Particle Size

顶点着色器提供了一个名为PSIZE的输出语义Semantic用于表示顶点的尺寸,当你将一个点Point渲染到屏幕中时,具有该语义的输出参数将会决定该点在像素上的宽高。

4.  美化粒子系统 Dressing Up Your Particle System

我们可以通过使用点精灵Point Sprites提升粒子的外观。使用点精灵时,硬件将会把每个点当作由四个顶点组成矩形来渲染,而不是一个单独的顶点。点精灵的每个顶点会被自动分配纹理坐标,它允许你通过纹理贴图改变粒子的外观。

点精灵能够在不绘制额外三角形的情况下创建复杂的几何效果。

四、关键帧插值 Key-Frame Interpolation

1.  简介

关键帧这个术语来自卡通动画。画家在绘制动画时,会先勾勒出一个粗糙的帧序列动画,其中并没有包含每一帧的画面,而只有重要的,也就是“关键”的帧。随后,画家会补上缺失的帧。有了关键帧做参考,这些中间帧会更容易画。

计算机动画也使用了类似的技术,3D建模师会为动画角色的每个姿势制作一个关键帧。每个关键帧都需要使用相同个数的顶点,且每个顶点必须互相对应。也就是一个关键帧中的顶点必须能够对应到这个模型其他关键帧中相同的点。对于这种关键帧模型,游戏会取模型的两个关键帧,然后将其中每对对应的顶点混合。

关键帧插值假定了顶点在每个关键帧中的个数和顺序都是相同的,这样一来就能够保证顶点着色器总能够正确地将顶点进行配对与混合。

2.  插值方法 Interpolation Approaches

插值的方法有很多,常见的两种是线性插值和二次插值。

1)        线性插值 Linear Interpolation

通过线性插值,位置会以固定的速率转。线性插值公式为:

BlendedPosition = positionA * (1 - f) + position * f

2)        二次插值 Quadratic Interpolation

如果想让转换速率根据时间变化,可以采用二次插值,公式为:

IntermediatePosition = positionA * (1 – f * f) + position * f * f

3)        其他插值方法

除了上诉两种插值公式以外,你还可以使用阶梯函数、样条函数或者指数函数进行插值。

3.  带有光照的关键帧插值 Key-Frame Interpolation with Lighting

当你需要对关键帧模型进行照明时,关键帧插值就不仅涉及到位置的混合,还包括了顶点法线的混合。混合后的法线可能不再是单位向量,因此此时需要对它进行标准化。

五、顶点蒙皮 Vertex Skinning

1.  简介

动画的另一种实现方式是顶点蒙皮,这项技术也被称为“矩阵调色混合“。

不同于关键帧动画,顶点蒙皮动画存储了模型的一个默认姿势和大量用于旋转与移动默认姿势的多边形网格子区域的矩阵,通常,这些矩阵变换被称为骨骼Bones。

每个默认姿势中多边形网格的顶点都被一个或多个这种矩阵所控制,每个矩阵都没指定了一个权重因子,用于指明当前矩阵对顶点的影响程度。假定每个顶点的所有控制矩阵的权重和为1。

当你需要渲染拥有顶点蒙皮的模型时,你需要使用每个顶点的骨骼集合中的所有矩阵对这个顶点执行变换,再将它们按照权重进行混合,得到的位置就是蒙皮顶点的位置。

当所有的矩阵都是单位矩阵时,模型展现的就是默认姿势——通常是站立面向前方,手脚分开向外伸展。

2.  通过矩阵构建姿势 Constructing Poses from Matrices

通过控制矩阵,你能够创建出不一样的姿势。3D建模师需要将矩阵和对应的权重分配到模型的默认姿势上,而创建姿势的过程就变成了操作矩阵的过程,而不是控制每个独立的顶点。通过这种方式,改变模型的姿势与制作动画变得非常简单。

通常,影响一个顶点的矩阵不会超过4个。

对于角色模型来说,最重要的矩阵被用来代表人物身体中骨骼的移动和旋转,因此顶点蒙皮矩阵被称为骨骼。而顶点则代表了皮肤上的点。

3.  光照 Lighting

同关键帧插值动画一样,在启用光照时,顶点蒙皮动画也需要对法向量进行变换。需要注意的是你需要对每个法向量只需要使用旋转缩放矩阵而不是原矩阵进行变换。同时,混合后也需要对法向量进行标准化。

4.  在存储需求上对比关键帧动画 Storage Requirements Compared with Key Frames

关键帧动画中,每个姿势都需要不同的顶点位置与法向量集合,当角色动作很多时,采用这种方式存储动画是非常不明智的。

顶点蒙皮动画,只需要存储一个默认姿势的顶点位置与法向量集合以及每个姿势的矩阵。通常对一个模型来说,因为矩阵的个数远少于顶点个数,所以使用顶点蒙皮的方式存储动画需要的空间远远少于关键帧动画。

存储顶点蒙皮动画时,每个顶点还需要额外存储控制该顶点的矩阵索引,以及相应的权重因子。

顶点蒙皮非常适合用于存储和回放动作捕捉序列。我们可以将每一个动作捕捉的帧作为骨骼矩阵集合存储起来,并将它应用在拥有相同默认姿势的不同模型上。反向运动学求解器Inverse Kinematics Solver也可以通过生成骨骼矩阵来实现。反向运动学求解器的目的是找到一个能让一个姿势真实自然地过渡到另一个姿势的增量骨骼矩阵序列。

时间: 2025-01-15 08:06:22

《The CG Tutorial》阅读笔记——动画 Animation的相关文章

Head First HTML与CSS阅读笔记(二)

上一篇Head First HTML与CSS阅读笔记(一)中总结了<Head First HTML与CSS>前9章的知识点,本篇则会将剩下的10~15章内容进行总结,具体如下所示. div与Span 此元素应该是HTML中使用频度最高的元素之一,div将属于一个逻辑区的元素包含起来: 如果添加div有助于将页面分解为逻辑区,保证结构清晰并便于指定样式,则应该使用div,否则,过多的div只会让页面更加复杂,不应滥用: width属性指定元素内容区的宽度,不包括内边距,边框和外边距的宽度: 整个

theano documentation 阅读笔记

作为帮助学习的工具 目标:较好地清楚地掌握theano的知识结构 学习内容:theano documentation 0.6,主要看tutorial 与 library ,看2遍以上. 学习时间6.29-6.30,(6.31也许需要完善下DAMS project的事)6.32-6.33 一共4天,大约40小时+. 这次是精读 1. install还需要搞定的是BLAS GPU-support 2. import pack 是把pack声明在当前环境,引用pack的内容需要pack.xxx fro

css3 动画(animation)-简单入门

css3之动画(animation) css3中我们可以使用动画,由于取代以前的gif图片,flash动画,以及部分javascript代码(相信有很多同学都用过jquery中的animate方法来做一些动画).具体如何使用呢??? 首先定义一个动画,然后引用动画. 定义一个动画要使用@keyframes,然后跟上你要定义的动画的名字.关键字"from"表示开始, "to"表示结束,等同于0% 和 100%.最好使用百分比来表示变化发生的时间,这样的话还可以定义从开

《构建之法阅读笔记02》

这次主要对<构建之法>的第四章“两人合作”作一次阅读笔记. 首先是代码规范问题. 我过去对于代码规范问题并没有做到注意.在编程中,许多变量和函数的命名都非常的简单而没有实际的意义.而且编程时不注意对齐缩进.很多时候也不加注释,导致对这些简单的变量名称不熟悉. 这样做会使得很多人读代码费劲,甚至是自己都要花时间再次阅读懂自己的代码.而且很多没必要的注释也会使得注释失去意义.当自己再次在原基础上编程时,可能要重新编程等问题. 因此,通过阅读“代码规范”,我找到一些解决方法.代码的风格要简明.易读.

《代码阅读方法与实践》阅读笔记之二

时间过得真快,一转眼,10天就过去了,感觉上次写阅读笔记的场景仿佛还历历在目.<代码阅读方法与实践>这本书真的很难写笔记,本来我看这本书的名字还以为书里大概写的都是些代码阅读的简易方法,心想着这就好写笔记了,没想到竟然好多都是我们之前学过的东西,这倒让我有点无从下手了.大概像我们这些还没有太多经历的大学生,总是习惯于尽量避免自己的工作量,总是试图找到一些完成事情的捷径吧.总之,尽管我不想承认,但我自己心里很清楚,我就是这种人.下面开始言归正传,说说接下来的几章内容归纳. 这本书在前面已经分析了

《大道至简》阅读笔记1

<大道至简>阅读笔记1 不知不觉间看完了第一章,从这个章节里我看到了一些我们都明白可是却自己很难做到的道理. 书中从愚公移山的故事和编程相结合给出了编程的精义就是顺序.分支.循环,这些都是我们所熟悉的,也是老师在教学中耳提面命的,可是我们又有几个人能做到呢. 我们总是在找着各种各样的学不好学不会理由,“它太难了”,“我太笨了”,认真的想一想难道真的是它太难了或者是自己太笨了么?不,答案是否定的,追根究底是懒惰,是没能坚持.从根本上来说,不存在会不会写程序的问题,除了先天智障和后天懒惰者,这要你

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index

构建之法阅读笔记05

2017.5.20 今天阅读的是<构建之法>第8章需求分析的阅读笔记,我们如果要开始做一个软件,最先要进行的就是需求分析,我们应该充分的了解我们这个软件是否具有前景,我们为用户提供的服务是不是用户所需要的,这一章详细的叙述了如何进行需求分析. 首先是获取和引导需求,我们应该找到软件的利益相关者,了解挖掘他们对软件的需求,引导他们表达出真实的需求.然后分析和定义需求,对各个方面的需求进行规整,定义需求内涵,从各个角度将需求量化,然后估计实现这些需求所需要的时间和资源,确定各个需求的优先级.紧接着

《构建之法》阅读笔记(2)

<构件之法>阅读笔记2 看了前面两章,我感觉我现阶段距离一个程序员还很远,软件工程师更是遥不可及.在学校的我学习了很多,如c++,数据结构,面向对象--学的多而不精,纵观现在我就是一个盲目学习的学生,上课时认真听了课后却没有花更多的时间去研究,遇到不懂的容易掉价死胡同,总是花很多时间闷闷思考,不到最后都没有去请教同学,去百度.看着其他很厉害的同学,自己就只能在一旁羡慕嫉妒恨.那现在在怎么样才能将自己对编程的兴趣提高,加强自己的编程思想?提高自己的价值?能够尽早地迈进程序员.软件工程师的行列之中