后处理之粒子实例一:烟花效果

文章介绍了如何使用粒子系统创建烟花效果,包括粒子的发射、分散和消亡三个阶段的算法分析,以及代码实现细节,如粒子的生命周期、运动速率和重力加速度等参数。同时,提到了该方法的优缺点,如结构清晰但参数调整有限,且大量粒子可能影响性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、效果介绍

话不多说,先上图:

 烟花效果在shadertoy上比较常见,也是粒子系统里一个比较典型的实例。通过分析,该效果的粒子可分为三个阶段:粒子的发射,粒子的分散,粒子的消亡。

粒子并不难理解,只是通过粒子做出来的东西有很多,为了让粒子做出来的东西更有价值,更多心思可能要花在想象和创意上。

二、算法分析

1.粒子的发射

为了方便控制,这里采用的方法是定时发射,也就是隔一段时间发射一定数量的粒子,所以这里涉及到的参数有:粒子发射位置,每秒粒子发射数量或者间隔时间发射粒子数(两者描述的其实都是频率),粒子大小。发射比较简单,按照时间到点发射即可。

2.粒子的分散(运动)

粒子的分散又叫运动。运动轨迹与粒子单元决定了粒子形成的效果,在烟花效果中,粒子是以圆中心向外分散的。这里涉及到的参数有:粒子的运动速率,运动过程中的受力,比如重力加速度和阻力等等。通过发射时长来计算粒子的位移情况,让粒子做偏移,即可完成粒子的运动。

3.粒子的消亡

由于一直在发射粒子,粒子越来越多,性能跟不上,因此要考虑粒子的消亡,让着色器处理的粒子数维持一个稳定的最大数。这里将涉及的参数有:粒子的生命周期(存在时间)。通过粒子的生命周期和其发射时间过滤掉已经过期的粒子,然而粒子突然消失显得会有些突兀,一般会通过生命周期来做一个渐渐变暗的处理。

三、利弊分析

1.利

结构比较稳定,各个部件分得很清晰,参数明了,可以改变粒子类型,运动轨迹。不管是圆点还是条状,再或者是图片纹理,都可以很简单的替换,可以当作一个模板。

2.弊

参数可调,但需要达到比较好的效果,参数其实比较局限,只能是某几个值。粒子数量过大,性能还是不好,如果不设置生命周期,或者生命周期过长,性能问题还是没有解决。

四、代码实现

//计算点到线段的距离,返回x为距离,y为向量比例
vec2 line (vec2 a, vec2 b, vec2 p)
{
    vec2 pa = p-a, ba = b-a;
    float h = min(1., max(0., dot(pa, ba)/dot(ba, ba)));
    
    return vec2(length(pa - ba * h), h);
}

void main()
{
    vec2 textSize = textureSize(uTexture1,0);
    vec4 nColor = texture(uTexture1, vTexPosition1);
    nColor.rgb = vec3(0.0);
    int scount = 100; //烟花粒子数
    float s_peed = 200; //分散速度
    float sdtime = 0.5; //发射间隔时间
    float sdelay = 1.5; //生命周期
    float particle_Size = 3.0; //粒子大小
    float force = 100.0;  //重力加速度
    float decayRate = 0.7; //衰减比例-阻力加速度(这里模拟的是定向的,不太真实)
    float Nt = floor(vtime/sdtime); //计算发射的批次
    int nt = int(max(floor((vtime - sdelay)/sdtime),0.0)); //忽略过期的粒子
    
    for(int i = nt;i < Nt+1;i++) //按发射批次循环
    {
        if(sdtime*(vtime/sdtime - i) > sdelay) //判断当前批次粒子是否超过生命周期(二次判断,实际可以不要)
           continue;
        float seed = rand((i+1.0)*3.3459); //每批粒子的随机值
        vec2 randPos = 0.6*vec2(rand(seed*3.92351),rand(seed*1.18723)) + 0.2;
        vec2 stpos = vTexPosition1 - randPos;
        stpos *= textSize;
        float nowtime = max(vtime - i*sdtime,0.0); //计算当前时间相对发射时的时间
        vec3 colorR = vec3(rand(seed*2.13454),rand(seed*1.78345),rand(seed*3.26187))*vec3(2.0*nowtime/sdelay,1.5*nowtime/sdelay,1.0);
        for(int j = 1;j<scount+1;j++) //按每个烟花中粒子数循环
        {
            float decay = (1.0 - nowtime/sdelay); //衰减
            float rad = (s_peed - decayRate*s_peed*decay)*(rand(seed*j * 1.8372 + 3.3459)*0.7 + 0.3);
            float a = 2.0*PI*rand(seed*j*2.1312 + 2.8923); //随机发射角度
    
            //rad = 0.02*s_peed; //轨迹-心形
            vec2 pdir = vec2(rad * cos(a), rad * sin(a)); //轨迹-圆形
            //pdir.x = rad * 16* pow(sin(a),3.0); //心形
            //pdir.y = rad * (13.0*cos(a) - 5.0*cos(2*a) - 2*cos(3*a) - cos(4*a));//心形
    
            vec2 sparkPos = -max(nowtime,0.0)*pdir + Force(0.0,nowtime,force,vec2(0.0,1.0)); //加入重力影响
    
            //采用到点的距离实现绘制圆状粒子 -- Start
            float psize = length(sparkPos + stpos);
                
            nColor.rgb += smoothstep(0.0,1.0,0.5*(1.0 - fract(nowtime/sdelay))*particle_Size/psize) * colorR;
            //圆状粒子 -- End
    
            //采用到线的距离绘制条状粒子 -- Start
            float linelen = 0.5*s_peed;
            float t1 = max(nowtime - 0.3,0.0);
            //SDF计算到线的距离
            vec2 sdline = line(-t1*smoothstep(-0.2,1.0,decay)*linelen*normalize(pdir),vec2(0.0,0.0),sparkPos + stpos);
            sdline.x = smoothstep(1.5, 0.0, sdline.x);
            nColor.rgb += smoothstep(-0.1,1.0,decay)*smoothstep(0.0, 1.0, sdline.y)*colorR*5.0*sdline.x;
            //条状粒子 -- End
        }
    }
    FragColor = nColor;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值