8.粒子模拟(Particle Simulations)
本章的作者:jryannel
** 注意: **
最新的构建时间:2016/03/21
这章的源代码能够在assetts folder找到。
粒子是可视化的某些图形效果的计算机图形技术。典型的效果可能是:落叶,火灾,爆炸,流星,云等。
它与其他图形渲染不同,因为粒子渲染基于模糊方面。结果在像素基础上不是完全可预测的。粒子系统的参数描述了随机模拟的边界。使用粒子呈现的现象通常难以用传统的渲染技术进行可视化。 好消息是,我们可以让 QML 元素与粒子系统进行交互。另外作为参数表示为属性,它们可以使用传统的动画技术进行动画化。
8.1 基本概念
粒子模拟的核心是控制共享时间线的 ParticleSystem。一个场景可以有几个粒子系统,每个粒子系统都有独立的时间线。使用 Emitter 元素发射粒子,并使用 ParticlePainter 可视化,它可以是图像、QML 元素或着色器元素。Emitter 还提供使用向量空间的粒子的方向。发射的粒子不能再被 Emitter 操纵了。粒子模块提供了 Affector。它允许在发射粒子之后操纵粒子的参数。
系统中的粒子可以使用 ParticleGroup 元素共享定时转换。默认情况下,每个粒子都在空('')组中。
- ParticleSystem —— 管理发射器之间的共享时间线
- Emitter —— 将逻辑粒子发射到系统中
- ParticlePainter —— 粒子由粒子绘制器可视化
- Direction —— 发射粒子的矢量空间
- ParticleGroup —— 每个粒子都是一个组的成员
- Affector —— 在发射粒子后,操纵粒子
8.2 简单模拟
让我们从一个非常简单的模拟来开始。Qt Quick 使得开始使用粒子渲染非常简单。 为此,我们需要:
- 将所有元素绑定到模拟的粒子系统(ParticleSystem)
- 将颗粒发射到系统中的发射器(Emitter)
- 一个粒子绘制器(ParticlePainter)派生的元素,用于可视化粒子
import QtQuick 2.5
import QtQuick.Particles 2.0
Rectangle {
id: root
width: 480; height: 160
color: "#1f1f1f"
ParticleSystem {
id: particleSystem
}
Emitter {
id: emitter
anchors.centerIn: parent
width: 160; height: 80
system: particleSystem
emitRate: 10
lifeSpan: 1000
lifeSpanVariation: 500
size: 16
endSize: 32
Tracer { color: 'green' }
}
ImageParticle {
source: "assets/particle.png"
system: particleSystem
}
}
示例的结果将如下所示:
我们从一个 80x80 像素的黑色矩形开始,作为我们的根元素和背景。其中我们声明一个 ParticleSystem。 这是系统将所有其他元素绑定在一起的第一步。通常,下一个元素是 Emitter,其基于其发射粒子的边界框和基本参数来定义发射区域。发射器使用 system 属性绑定到系统。
该示例中的发射器在发射器的面积上每秒发射10颗粒子(emitRate: 10),寿命为1000毫秒(lifeSpan: 1000),发射粒子之间的寿命变化为500毫秒(lifeSpanVariation: 500)。 一个粒子应该以16px(size: 16)的大小开始生命周期,并且在生命周期结束时是 32px(endSize: 32) 的大小。
绿色的边框矩形是显示发射器几何形状的示踪元件。这可以看出,当粒子在发射器边界框内发射时,渲染不限于发射器边界框。渲染位置取决于粒子的寿命和方向。当我们研究如何改变粒子的方向时,这将变得更加清晰。
发射器发射逻辑粒子。在本示例中,使用 ParticlePainter 可视化逻辑粒子,我们使用 ImageParticle,它将图像 URL 作为源属性。图像颗粒还具有几种其他性质,用于控制平均颗粒的外观。
- emitRate:每秒发射的粒子数(默认为每秒10个)
- lifeSpan:粒子应该持续的毫秒数(默认为1000毫秒)
- size, endSize:粒子在其生命周期的开始和结束时的大小(默认为16像素)
改变这些属性可以大大影响显示的结果
Emitter {
id: emitter
anchors.centerIn: parent
width: 20; height: 20
system: particleSystem
emitRate: 40
lifeSpan: 2000
lifeSpanVariation: 500
size: 64
sizeVariation: 32
Tracer { color: 'green' }
}
除了将发射率提高到 40,寿命延长到 2 秒,尺寸现在以 64 像素开始,在粒子寿命结束时减小到 32 像素。
增加 endSize 甚至会导致或多或少的白色背景。请注意,当粒子仅在由发射器定义的区域中发射时,渲染不受约束。
8.3 粒子参数
我们已经看到如何改变发射器的行为来改变我们的模拟。使用的粒子绘制器允许我们如何为每个粒子显示粒子图像。
回到我们的例子,我们更新我们的 ImageParticle。首先,我们将粒子图像更改为一个小火花星图像:
ImageParticle {
...
source: 'assets/star.png'
}
颗粒应以金色着色,颜色从颗粒到颗粒之间变化 +/- 20%:
color: '#FFD700'
colorVariation: 0.2
为了使场景更加活跃,我们想旋转粒子。每个颗粒应顺时针选择 15 度开始,粒子之间的旋转偏差为 +/- 5 度。另外粒子应以每秒 45 度的速度连续旋转。颗粒之间的旋转速度也应随每秒 +/-15 度而变化:
rotation: 15
rotationVariation: 5
rotationVelocity: 45
rotationVelocityVariation: 15
最后,也很重要的是,我们改变粒子的输入效应。这是粒子生命使用的效果。在这种情况下,我们要使用缩放效果:
entryEffect: ImageParticle.Scale
所以现在我们有旋转的金色星星出现。
这是我们在一个块中为图像粒子更改的代码。
ImageParticle {
source: "assets/star.png"
system: particleSystem
color: '#FFD700'
colorVariation: 0.2
rotation: 0
rotationVariation: 45
rotationVelocity: 15
rotationVelocityVariation: 15
entryEffect: ImageParticle.Scale
}
8.4 定向粒子
我们已经看到粒子可以旋转了。 但是粒子也可以有轨迹。轨迹被指定为由随机方向(也称为向量空间)定义的粒子的速度或加速度。
有不同的矢量空间可用于定义粒子的速度或加速度:
- AngleDirection —— 角度变化的方向
- PointDirection —— x 和 y 分量变化的方向
- TargetDirection —— 朝向目标点的方向
让我们尝试通过使用速度方向将粒子从左到右移动到我们的场景的右侧。
我们首先尝试 AngleDirection。为此,我们需要将 AngleDirection 指定为我们发射器的 velocity 属性的一个元素:
velocity: AngleDirection { }
使用角度特性来规定发射粒子的角度。该角度以 0.36 度和 0 点之间的值提供给右侧。对于我们的例子,我们希望粒子向右移动,所以 0 已经是正确的方向了。粒子应在 +/-15 度之间变化:
velocity: AngleDirection {
angle: 0
angleVariation: 15
}
现在我们已经设置了方向,接下来是指定粒子的速度。它是由一个叫做梯度值(magnitude)的属性来定义的,这个梯度值定义了每秒像素的变化。正如我们设置大约 640px,梯度值为 100 似乎是一个很好的数字。这意味着平均一个 6.4 秒生命周期的粒子可以穿越我们看到的区域。为了让粒子的穿越看起来更加有趣,我们使用magnitudeVariation 来设置梯度值的变化,这个值是我们当前梯度值的一半:
velocity: AngleDirection {
...
magnitude: 100
magnitudeVariation: 50
}
这是完整的源代码,平均使用时间设置为 6.4 秒。 我们将发射器的宽度和高度设置为 1px。这意味着所有的粒子都是在相同的位置和基于我们给定的轨迹的地方发射的。
Emitter {
id: emitter
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1; height: 1
system: particleSystem
lifeSpan: 6400
lifeSpanVariation: 400
size: 32
velocity: AngleDirection {
angle: 0
angleVariation: 15
magnitude: 100
magnitudeVariation: 50
}
}
那加速做什么呢?加速度向每个粒子添加一个加速度矢量,随时间改变速度矢量。例如,让我们做一个像星星弧的轨迹。为此,我们将速度方向改为 -45 度,并删除变化,以更好地看清这个弧形:
velocity: AngleDirection {
angle: -45
magnitude: 100
}
加速度方向应为90度(向下),我们选择四分之一的速度梯度:
acceleration: AngleDirection {
angle: 90
magnitude: 25
}
结果是我们会看到从左侧中心点到右下角的弧形。
这些值是通过不断的尝试和修改错误,最终选定的。
这是我们的发射器的完整代码。
Emitter {
id: emitter
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: 1; height: 1
system: particleSystem
emitRate: 10
lifeSpan: 6400
lifeSpanVariation: 400
size: 32
velocity: AngleDirection {
angle: -45
angleVariation: 0
magnitude: 100
}
acceleration: AngleDirection {
angle: 90
magnitude: 25
}
}
在下一个例子中,我们希望粒子再次从左到右行进,但这次我们使用 PointDirection 向量空间。
PointDirection 从 x 和 y 组合导出它的向量空间。例如,如果您希望粒子以 45 度向量行进,则需要为x和y指定相同的值。
在我们的例子中,我们希望颗粒是从左到右行进的 15 度大小的锥体。为此,我们将 PointDirection 指定为我们的速度向量空间:
velocity: PointDirection { }
为了实现每秒 100 像素的行进速度,我们将 x 分量设置为 100。对于 15 度(为 90 度的 1/6),我们指定 y 变化为 100/6:
velocity: PointDirection {
x: 100
y: 0
xVariation: 0
yVariation: 100/6
}
结果应该是从右到左以 15 度锥体行进的颗粒。
现在来到我们的最后一个元素,TargetDirection。目标方向允许我们将目标点指定为相对于发射器或物体的 x 和 y 坐标。当指定项目时,项目的中心将成为目标点。我们可以通过指定 x 目标的 1/6 的目标变化来实现 15 度锥体:
velocity: TargetDirection {
targetX: 100
targetY: 0
targetVariation: 100/6
magnitude: 100
}
** 注意: **
当我们有特定的 x / y 坐标作为我们想要发射的粒子流的终点时,使用 TargetDirection 是最推荐的做法。
现在的图像看起来和之前的是一样一样的,这不是我们的目标。
在以下图像中,红色和绿色圆圈指定每个目标项的目标方向速度的加速属性。每个目标方向具有相同的参数。这里的问题是:谁负责速度和谁负责加速?