39、图形动画中的粒子系统与摆动效果实现

图形动画中的粒子系统与摆动效果实现

在图形动画领域,粒子系统和摆动效果是两种常见且有趣的技术。下面将详细介绍它们的实现原理和代码示例。

粒子系统

粒子系统是一种强大的渲染技术,具有诸多优点。首先,它能以较少的人力创建复杂的系统;其次,其复杂度易于调整。正如相关研究指出,粒子系统的动态效果是使其看起来真实的关键。

应用设置

以“五彩纸屑炮”效果为例,我们希望实现一个能持续喷出大量小而鲜艳纸屑的效果。这些纸屑不会一次性全部喷出,而是稳定地持续喷出,直到全部喷完。纸屑的初始速度有一定随机性,但总体方向是向上并远离原点,同时受重力影响最终落回地面。

以下是创建粒子系统初始值的 C 子例程:

static GLint arrayWidth, arrayHeight;
static GLfloat *verts = NULL;
static GLfloat *colors = NULL;
static GLfloat *velocities = NULL;
static GLfloat *startTimes = NULL;
void createPoints(GLint w, GLint h)
{
    GLfloat *vptr, *cptr, *velptr, *stptr;
    GLfloat i, j;
    if (verts != NULL) 
        free(verts);
    verts  = malloc(w * h * 3 * sizeof(float));
    colors = malloc(w * h * 3 * sizeof(float));
    velocities = malloc(w * h * 3 * sizeof(float));
    startTimes = malloc(w * h * sizeof(float));
    vptr = verts;
    cptr = colors;
    velptr = velocities;
    stptr  = startTimes;
    for (i = 0.5 / w - 0.5; i < 0.5; i = i + 1.0/w)
        for (j = 0.5 / h - 0.5; j < 0.5; j = j + 1.0/h)
        {
            *vptr       = i;
            *(vptr + 1) = 0.0;
            *(vptr + 2) = j;
            vptr += 3;
            *cptr       = ((float) rand() / RAND_MAX) * 0.5 + 0.5;
            *(cptr + 1) = ((float) rand() / RAND_MAX) * 0.5 + 0.5;
            *(cptr + 2) = ((float) rand() / RAND_MAX) * 0.5 + 0.5;
            cptr += 3;
            *velptr       = (((float) rand() / RAND_MAX)) + 3.0;
            *(velptr + 1) =  ((float) rand() / RAND_MAX) * 10.0;
            *(velptr + 2) = (((float) rand() / RAND_MAX)) + 3.0;
            velptr += 3;
            *stptr = ((float) rand() / RAND_MAX) * 10.0;
            stptr++;
        }
    arrayWidth  = w;
    arrayHeight = h;
}

该子例程创建了一个二维网格的粒子系统,每个粒子有初始位置、随机颜色、随机初始速度和随机开始时间。

接着,我们需要使用通用顶点属性来指定粒子的位置、颜色、初始速度和开始时间,并定义必要的常量:

#define VERTEX_ARRAY 1
#define COLOR_ARRAY 2
#define VELOCITY_ARRAY 3
#define START_TIME_ARRAY 4

绑定通用顶点属性索引到顶点着色器属性变量名:

glBindAttribLocation(ProgramObject, VERTEX_ARRAY, "MCVertex");
glBindAttribLocation(ProgramObject, COLOR_ARRAY, "MColor");
glBindAttribLocation(ProgramObject, VELOCITY_ARRAY, "Velocity");
glBindAttribLocation(ProgramObject, START_TIME_ARRAY, "StartTime");

绘制粒子系统的 C 子例程如下:

void drawPoints()
{
    glPointSize(2.0);
    glVertexAttribPointer(VERTEX_ARRAY,     3, GL_FLOAT, 
                                 GL_FALSE,  0, verts);
    glVertexAttribPointer(COLOR_ARRAY,      3, GL_FLOAT,
                                 GL_FALSE,  0, colors); 
    glVertexAttribPointer(VELOCITY_ARRAY,   3, GL_FLOAT, 
                                 GL_FALSE,  0, velocities);
    glVertexAttribPointer(START_TIME_ARRAY, 1, GL_FLOAT,
                                 GL_FALSE,  0, startTimes);
    glEnableVertexAttribArray(VERTEX_ARRAY);
    glEnableVertexAttribArray(COLOR_ARRAY);
    glEnableVertexAttribArray(VELOCITY_ARRAY);
    glEnableVertexAttribArray(START_TIME_ARRAY);
    glDrawArrays(GL_POINTS, 0, arrayWidth * arrayHeight);
    glDisableVertexAttribArray(VERTEX_ARRAY);
    glDisableVertexAttribArray(COLOR_ARRAY);
    glDisableVertexAttribArray(VELOCITY_ARRAY);
    glDisableVertexAttribArray(START_TIME_ARRAY);
}

为了实现动画效果,应用程序需要将时间信息传递给顶点着色器:

if (DoingParticles)
{
    location = glGetUniformLocation(ProgramObject, "Time");
    ParticleTime += 0.001f;
    glUniform1f(location, ParticleTime);
    CheckOglError();
}
五彩纸屑炮顶点着色器

顶点着色器是粒子系统渲染的关键。它不是简单地变换传入的顶点,而是以其为初始位置,结合统一变量 Time 计算新的位置。

#version 140
uniform float Time;            // updated each frame by the application
uniform vec4  Background;      // constant color equal to background
uniform mat4  MVPMatrix;
in  vec4  MCVertex;
in  vec4  MColor;
in  vec3  Velocity;      // initial velocity
in  float StartTime;     // time at which particle is activated
out vec4  Color;

void main()
{
    vec4  vert;
    float t = Time - StartTime;
    if (t >= 0.0)
    {
        vert    = MCVertex + vec4(Velocity * t, 0.0);
        vert.y -= 4.9 * t * t;
        Color   = MColor;
    }
    else
    {
        vert  = MCVertex;       // Initial position
        Color = Background;     // "pre-birth" color
    }
    gl_Position  = MVPMatrix * vert;
}

顶点着色器首先计算粒子的年龄。如果年龄小于 0,粒子尚未“出生”,则将其颜色设置为背景颜色。如果粒子的开始时间小于当前时间,则使用运动学方程计算其当前位置:
[P = P_i + vt + \frac{1}{2}at^2]
其中,(P_i) 是粒子的初始位置,(v) 是初始速度,(t) 是经过的时间,(a) 是加速度(这里取地球重力加速度 -9.8 m/s²,在代码中体现为 -4.9 作用于 (y) 坐标),(P) 是最终计算的位置。

进一步增强

粒子系统还有很多可以改进的地方。例如,将 t 值从顶点着色器传递到片段着色器,使粒子颜色随时间变化;减少粒子的 alpha 值使其逐渐消失;设置“死亡时间”,当粒子达到一定时间或距离原点一定距离时消失;将粒子绘制为短线以模糊其运动;随时间改变粒子的大小;使用点精灵使粒子看起来更好等。

粒子系统的计算完全在图形硬件中完成,而非主机 CPU。如果将粒子系统数据存储在顶点缓冲区对象中,还能避免使用 I/O 总线带宽。使用 OpenGL 着色语言,更新每个粒子的方程可以非常复杂,并且可以像渲染其他 3D 对象一样旋转和从任意角度查看粒子系统。

摆动效果

前面的例子主要通过顶点处理器实现对象几何形状的动画,而片段处理器也能创建动画效果。摆动效果的着色器通过随时间变化扰动纹理坐标来实现振荡或摆动效果,结合合适的纹理,能轻松模拟凝胶状表面或“舞动”的标志。

摆动效果的实现

摆动效果着色器的核心是在纹理查找操作前,使用正弦函数扰动纹理坐标。扰动的幅度和频率可以通过应用程序传递的统一变量控制。

由于在编写该着色器时正弦函数未实现,作者使用泰勒级数的前两项来近似正弦值。虽然使用内置的 sin 函数会使片段着色器更简单,但这种方法展示了在着色器中可以使用数值方法。

应用程序需要提供摆动的频率、幅度和光的位置,并在每一帧递增统一变量 StartRad ,以实现摆动效果的动画。顶点着色器负责基于表面法线和光的位置进行简单的光照计算,并传递纹理坐标。

以下是实现摆动效果的片段着色器:

#version 140
// Constants
const float C_PI    = 3.1415;
const float C_2PI   = 2.0 * C_PI;
const float C_2PI_I = 1.0 / (2.0 * C_PI);
const float C_PI_2  = C_PI / 2.0;
uniform float StartRad;
uniform vec2  Freq;
uniform vec2  Amplitude;
uniform sampler2D WobbleTex;
in  float LightIntensity;
in  vec2  TexCoord;
out vec4  FragColor;
void main()
{
    vec2  perturb;
    float rad;
    vec3  color;
    // 计算 x 方向的扰动因子
    rad = Freq.x * (TexCoord.s + TexCoord.t) + StartRad;
    while (rad < -C_PI_2) rad += C_2PI;
    while (rad > C_PI_2) rad -= C_2PI;
    float sin_approx = rad - (rad * rad * rad) / 6.0;
    perturb.x = sin_approx * Amplitude.x * 0.05;

    // 计算 y 方向的扰动因子
    rad = Freq.y * (TexCoord.s - TexCoord.t) + StartRad;
    while (rad < -C_PI_2) rad += C_2PI;
    while (rad > C_PI_2) rad -= C_2PI;
    sin_approx = rad - (rad * rad * rad) / 6.0;
    perturb.y = sin_approx * Amplitude.y * 0.05;

    // 扰动后的纹理坐标
    vec2 perturbed_tex_coord = TexCoord + perturb;

    // 纹理采样
    color = texture(WobbleTex, perturbed_tex_coord).rgb;

    // 应用光照强度
    FragColor = vec4(color * LightIntensity, 1.0);
}

该片段着色器首先计算 x 方向的扰动因子,将 rad 值调整到 [-π/2, π/2] 范围内,然后使用泰勒级数近似计算正弦值,并乘以幅度因子得到扰动值。接着,以类似的方式计算 y 方向的扰动因子。最后,使用扰动后的纹理坐标进行纹理采样,并乘以光照强度得到最终的片段颜色。

摆动效果的频率和幅度可以通过 Freq Amplitude 统一变量调整。 Freq 变量类似于里氏震级,值为 0 时无摆动,值为 1.0 时为轻微晃动,值为 2.0 时为抖动,值为 4.0 时为摆动,值为 8.0 时为强烈震动效果。通过调整 Amplitude 变量可以改变摆动的大小。

综上所述,粒子系统和摆动效果为图形动画带来了丰富的视觉效果。通过合理运用这些技术和代码,开发者可以创造出更加生动和有趣的图形应用。

摆动效果(续)

摆动效果在图形动画中具有独特的魅力,它能为纹理增添动态感和趣味性。下面我们进一步深入探讨摆动效果的一些细节和应用场景。

摆动效果的参数调整

摆动效果的实现依赖于几个关键的统一变量,这些变量可以让我们灵活地控制摆动的表现。以下是对这些变量的详细分析:
- StartRad :这是一个由应用程序在每一帧递增的变量,它为扰动计算提供起始点,从而实现摆动效果的动画。通过调整递增的值,可以控制摆动的速度。例如,较大的增量会使摆动更快,而较小的增量则会使摆动更慢。通常,约 1° 的增量能带来视觉上令人满意的效果。
- Freq :这是一个 vec2 类型的变量,其 x y 分量可以独立调整。它类似于里氏震级,用于控制摆动的频率。不同的值会产生不同的摆动效果:
| Freq 值 | 摆动效果 |
| ---- | ---- |
| 0 | 无摆动 |
| 1.0 | 轻微晃动 |
| 2.0 | 抖动 |
| 4.0 | 摆动 |
| 8.0 | 强烈震动效果 |
- Amplitude :同样是 vec2 类型的变量,用于控制摆动的幅度。它决定了纹理坐标从原始值拉伸的程度。例如,值为 0.05 时,扰动对原始纹理坐标的改变不超过 ±0.05;值为 0.5 时,改变不超过 ±0.5。通过调整这个值,可以使摆动效果更大或更小。

摆动效果的应用场景

摆动效果结合合适的纹理可以模拟多种现实场景,以下是一些常见的应用:
- 模拟液体表面 :当纹理看起来像水、熔岩或黏液的表面时,摆动效果可以很好地模拟液体的流动和波动,使画面更加逼真。
- 动画标志 :对于一些需要突出动态感的标志,如活动的宣传标志或游戏中的动态图标,摆动效果可以让标志“舞动”起来,吸引用户的注意力。
- 生物皮肤效果 :在模拟动物或怪物的皮肤时,摆动效果可以为皮肤增添一种柔软和动态的感觉,使生物看起来更加生动。

摆动效果的性能考虑

在使用摆动效果时,性能也是一个需要考虑的因素。由于使用了泰勒级数近似来计算正弦值,其性能可能因不同的图形硬件供应商而异。一些硬件可能对内置的 sin 函数有更高效的实现,而另一些硬件可能在使用泰勒级数近似时表现更好。因此,在实际应用中,需要根据目标硬件进行性能测试,选择最合适的方法。

粒子系统与摆动效果的结合应用

粒子系统和摆动效果可以相互结合,创造出更加复杂和绚丽的图形效果。以下是一些结合应用的思路:
- 粒子的摆动效果 :可以为粒子系统中的粒子添加摆动效果,使粒子的运动更加复杂和有趣。例如,在五彩纸屑炮效果中,让纸屑在下落过程中产生摆动,模拟风吹动的效果。
- 背景的摆动与粒子的交互 :将摆动效果应用于背景纹理,同时在前景中使用粒子系统。背景的摆动可以与粒子的运动相互呼应,增强整个场景的动态感。

总结

图形动画中的粒子系统和摆动效果为开发者提供了丰富的工具来创造生动和有趣的图形应用。粒子系统通过模拟大量粒子的运动,可以实现如五彩纸屑炮等复杂的效果,并且其计算可以在图形硬件中高效完成。摆动效果则通过扰动纹理坐标,为纹理增添了动态感,可用于模拟多种现实场景。

通过合理调整粒子系统和摆动效果的参数,开发者可以根据不同的需求创造出独特的视觉效果。同时,将两者结合应用可以进一步拓展创意空间,为用户带来更加震撼的视觉体验。在实际开发中,还需要考虑性能因素,选择最合适的实现方法。

未来,随着图形技术的不断发展,粒子系统和摆动效果可能会有更多的应用场景和优化方法。开发者可以持续关注这些技术的发展,不断探索和创新,创造出更加精彩的图形动画作品。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值