QML Book 第八章 粒子模拟 1

8.粒子模拟(Particle Simulations)

本章的作者:jryannel

** 注意: **
最新的构建时间:2016/03/21
这章的源代码能够在assetts folder找到。

粒子是可视化的某些图形效果的计算机图形技术。典型的效果可能是:落叶,火灾,爆炸,流星,云等。

它与其他图形渲染不同,因为粒子渲染基于模糊方面。结果在像素基础上不是完全可预测的。粒子系统的参数描述了随机模拟的边界。使用粒子呈现的现象通常难以用传统的渲染技术进行可视化。 好消息是,我们可以让 QML 元素与粒子系统进行交互。另外作为参数表示为属性,它们可以使用传统的动画技术进行动画化。

8.1 基本概念

粒子模拟的核心是控制共享时间线的 ParticleSystem。一个场景可以有几个粒子系统,每个粒子系统都有独立的时间线。使用 Emitter 元素发射粒子,并使用 ParticlePainter 可视化,它可以是图像、QML 元素或着色器元素。Emitter 还提供使用向量空间的粒子的方向。发射的粒子不能再被 Emitter 操纵了。粒子模块提供了 Affector。它允许在发射粒子之后操纵粒子的参数。

系统中的粒子可以使用 ParticleGroup 元素共享定时转换。默认情况下,每个粒子都在空('')组中。

particlesystem
  • 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
    }
}

示例的结果将如下所示:

simpleparticles

我们从一个 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 像素。

simpleparticles2

增加 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

所以现在我们有旋转的金色星星出现。

particleparameters

这是我们在一个块中为图像粒子更改的代码。

    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 —— 朝向目标点的方向
particle_directions

让我们尝试通过使用速度方向将粒子从左到右移动到我们的场景的右侧。

我们首先尝试 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
}
angledirection

这是完整的源代码,平均使用时间设置为 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
}

结果是我们会看到从左侧中心点到右下角的弧形。

angledirection2

这些值是通过不断的尝试和修改错误,最终选定的。

这是我们的发射器的完整代码。

    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 度锥体行进的颗粒。

pointdirection

现在来到我们的最后一个元素,TargetDirection。目标方向允许我们将目标点指定为相对于发射器或物体的 x 和 y 坐标。当指定项目时,项目的中心将成为目标点。我们可以通过指定 x 目标的 1/6 的目标变化来实现 15 度锥体:

velocity: TargetDirection {
    targetX: 100
    targetY: 0
    targetVariation: 100/6
    magnitude: 100
}

** 注意: **
当我们有特定的 x / y 坐标作为我们想要发射的粒子流的终点时,使用 TargetDirection 是最推荐的做法。

现在的图像看起来和之前的是一样一样的,这不是我们的目标。

在以下图像中,红色和绿色圆圈指定每个目标项的目标方向速度的加速属性。每个目标方向具有相同的参数。这里的问题是:谁负责速度和谁负责加速?

directionquest
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容