恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

C++与DirectX粒子系统实战:从零构建高性能烟花特效

  • 首页
  • 资讯中心
  • /
  • C++与DirectX粒子系统实战:从零构建高性能烟花特效

相关资讯

C#数据采集卡编程实战:从DllImport到连续采集与实时波形 2026/10/19 10:48:52
基于 SQLite FTS5 与向量检索的轻量级知识库笔记检索服务实战 2026/10/19 10:48:52
BadgerDB与sfsDb事务性能对比:LSM与段式存储的选型启示 2026/10/19 10:43:52

最新资讯

CNN并行计算全解析:数据并行、模型并行与张量并行的实战策略
AI数字员工实战:用Moltbot搭建自动化工作流的完整指南
RNN、LSTM、BiLSTM三部曲:从原理到实战的序列建模指南
GitHub周榜风向:AI代码审查、向量检索与本地优先工具解析
深度学习中的CNN并行计算:四大并行策略、工程实现与避坑指南
软件I2C模拟16位寄存器地址的读写实现与调试指南

今日推荐

OpenCowork 实测:支持本地文件、飞书机器人的 Windows AI 助手(只需配置 Token)
GPT-6 Sol 与 Luna 怎么选?Chat、Work、Codex 版本差异与 TaoToken 接入实测
openclaw.net 集成 ElBruno.MempalaceNet 记忆系统:把 MCP 配置改到 TaoToken 的完整实践

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

C++与DirectX粒子系统实战:从零构建高性能烟花特效

发布时间:2026/10/19 10:48:52
C++与DirectX粒子系统实战:从零构建高性能烟花特效 1. 从零拆解一个烟花粒子系统的核心设计思路1.1 为什么选择粒子系统来做烟花在图形编程里烟花特效几乎是每个想入门特效的人都会碰的一个经典项目。它看起来简单——不就是一堆点炸开然后落下来吗但真正动手写过的人都知道里面涉及的东西一点都不少粒子发射、生命周期管理、物理模拟、颜色渐变、混合模式、性能优化每一个环节都有坑。我选择用 C 配合 DirectX 来实现这套烟花特效原因很直接。C 能让我对内存布局和渲染管线有完全的控制权而 DirectX 作为底层图形接口在 Windows 平台上做粒子系统有着天然的优势——不管是顶点缓冲的动态更新还是混合状态的灵活切换都比上层引擎来得更直接。当然如果你用的是 OpenGL 或者 Vulkan核心思路是完全一样的只是 API 调用不同而已。这套烟花特效最终能做什么简单说你给它一个发射位置它就能在屏幕上炸出一朵完整的烟花先是上升的拖尾然后是爆炸瞬间的球状扩散接着粒子受重力影响自然下落颜色从亮白过渡到暖色再逐渐暗淡消失。整个过程完全由代码驱动不依赖任何预制动画资源。适合谁来参考如果你已经写过一些 C 的基础项目对 DirectX 的初始化流程有基本了解但还没做过完整的粒子系统那这套方案就是为你准备的。我会把每个参数的计算过程、每个设计决策背后的理由都讲清楚让你不只是抄代码而是真正理解为什么这么写。1.2 整体架构CPU 模拟 GPU 渲染的分工粒子系统的架构设计核心就一个问题粒子的位置更新放在哪里算有两种主流方案。一种是把粒子数据全部放在 GPU 上用计算着色器或者 Transform Feedback 来更新位置CPU 完全不参与。这种方案性能上限高适合百万级粒子但调试困难逻辑改起来也麻烦。另一种是 CPU 负责模拟每帧计算好所有粒子的位置、颜色、大小然后上传到 GPU 渲染。这种方案性能上限低一些但逻辑清晰、调试方便对于烟花这种几千到几万粒子的场景完全够用。我选的是第二种方案。原因很简单烟花的粒子行为需要频繁调整参数——爆炸力度、重力系数、颜色曲线、生命周期——这些在 CPU 端改起来直观得多。而且烟花特效的粒子数量通常在可控范围内CPU 模拟完全扛得住。整个系统的数据流是这样的粒子池管理预分配一块连续内存存放所有粒子数据避免频繁的 new/delete发射器负责在特定时刻生成新粒子设置初始速度、颜色、生命周期模拟循环每帧遍历所有活跃粒子更新速度、位置、颜色、剩余生命渲染提交把活跃粒子的数据打包成顶点缓冲提交给 GPU 绘制这个流程听起来简单但每个环节都有值得展开的细节。接下来我会逐个拆解。1.3 粒子池的设计为什么不用 vector 动态增删很多新手写粒子系统的第一反应是用std::vectorParticle发射的时候 push_back粒子死亡的时候 erase。这个做法在小规模下没问题但粒子数量一上去就会出大问题。首先是内存碎片。频繁的 push_back 和 erase 会导致 vector 内部的元素不断搬移每次 erase 都要把后面的元素往前挪时间复杂度是 O(n)。几千个粒子每帧都在增删帧率直接崩掉。其次是缓存不友好。vector 扩容时会重新分配内存所有粒子数据被搬到新地址CPU 缓存全部失效。我的做法是预分配一个固定大小的粒子数组用一个activeCount来标记当前活跃粒子的数量。所有活跃粒子紧凑地排在数组前段死亡粒子的位置由最后一个活跃粒子填补。这样做的代价是粒子的顺序会变但对于烟花特效来说渲染顺序本来就不重要因为用的是加法混合顺序无关。struct Particle { float posX, posY, posZ; float velX, velY, velZ; float life; // 剩余生命 float maxLife; // 总生命用于计算归一化时间 float size; float r, g, b, a; }; const int MAX_PARTICLES 10000; Particle particlePool[MAX_PARTICLES]; int activeCount 0;这个设计的好处是内存完全连续CPU 遍历时缓存命中率极高。而且没有动态分配不会在运行时产生内存抖动。注意粒子池的大小要根据最坏情况来定。一朵烟花爆炸可能产生 500 到 2000 个粒子如果同时有多朵烟花峰值可能到上万。宁可多分配一些也不要运行时扩容。2. 烟花特效的核心细节与实操要点2.1 发射阶段从地面到空中的拖尾效果烟花的第一个阶段是升空。这个阶段看起来简单但要做出好看的拖尾效果有几个关键点。发射时粒子从底部某个位置出发给一个向上的初速度。但如果你只发射一个粒子它就是一个光点往上飞完全没有烟花的感觉。真正的烟花升空时尾部会拖出一条亮线这条线其实是由很多个小粒子组成的。我的做法是在升空阶段持续发射小粒子每个粒子有微小的随机偏移和不同的生命周期。这样它们会逐渐落后于主粒子形成拖尾。具体参数上主粒子的上升速度大约在 300 到 500 像素/秒假设屏幕高度 720拖尾粒子的发射频率是每帧 2 到 3 个生命周期 0.3 到 0.5 秒。这里有个细节拖尾粒子的颜色要和主粒子有区分。主粒子偏亮白拖尾粒子偏暖黄这样视觉上更有层次。而且拖尾粒子要随着时间逐渐变暗变小模拟燃烧殆尽的效果。// 升空阶段每帧发射拖尾粒子 void EmitTrailParticle(float baseX, float baseY) { Particle p particlePool[activeCount]; p.posX baseX RandomFloat(-3.0f, 3.0f); p.posY baseY RandomFloat(-3.0f, 3.0f); p.velX RandomFloat(-20.0f, 20.0f); p.velY RandomFloat(-50.0f, -20.0f); p.life RandomFloat(0.3f, 0.5f); p.maxLife p.life; p.size RandomFloat(2.0f, 4.0f); p.r 1.0f; p.g 0.8f; p.b 0.3f; p.a 1.0f; }拖尾粒子的速度要略微向下这样它们才会落在主粒子后面。如果速度也是向上的拖尾就会跑到主粒子前面去看起来就很奇怪。2.2 爆炸瞬间球面均匀分布的实现升空到最高点后主粒子消失同时在那个位置生成大量爆炸粒子。这是整个特效最核心的部分。爆炸粒子的速度方向必须是球面均匀分布的。如果你直接用rand()生成三个方向的分量然后归一化会发现粒子在球面上的分布不均匀——靠近坐标轴方向会偏多。正确的做法是用球坐标来生成float theta RandomFloat(0.0f, 2.0f * PI); float phi acos(1.0f - 2.0f * RandomFloat(0.0f, 1.0f)); float speed RandomFloat(minSpeed, maxSpeed); float vx speed * sin(phi) * cos(theta); float vy speed * sin(phi) * sin(theta); float vz speed * cos(phi);这里的phi用acos(1 - 2 * rand)而不是直接rand * PI是为了保证球面上的面积均匀。这个推导来自球面面积微元dA sin(phi) * dphi * dtheta要让面积均匀phi的分布必须满足cos(phi)均匀分布。爆炸速度的范围也有讲究。如果所有粒子速度一样爆炸就是一个完美的球壳看起来很假。实际烟花爆炸时粒子的速度有一个分布中间快边缘慢或者反过来。我一般用minSpeed 150maxSpeed 400让速度在一定范围内随机这样爆炸看起来更自然。爆炸粒子的数量取决于你想要的效果。一般来说500 到 1500 个粒子能做出比较饱满的球状爆炸。如果追求更细腻的效果可以加到 3000 个但要注意性能。2.3 颜色渐变从亮白到暖色的过渡烟花的颜色变化是让它看起来真实的关键。真实的烟花爆炸时中心温度极高呈现亮白色随着温度降低颜色逐渐过渡到黄色、橙色、红色最后暗淡消失。在代码里我用粒子的归一化生命值t life / maxLife来控制颜色。t从 1 降到 0对应粒子从刚生成到消亡。颜色曲线大致是这样的t 0.8接近纯白RGB 大约 (1.0, 1.0, 0.9)0.5 t 0.8过渡到暖黄(1.0, 0.8, 0.3)0.2 t 0.5橙色到红色(1.0, 0.4, 0.1)t 0.2暗红到消失(0.5, 0.1, 0.0) 且 alpha 逐渐降低这个曲线可以用分段线性插值来实现也可以用指数衰减。我试过几种方案最后觉得用pow(t, 0.5)来做亮度衰减比较自然——前期衰减慢后期衰减快符合燃烧的物理直觉。float t p.life / p.maxLife; float brightness powf(t, 0.5f); p.r brightness; p.g brightness * (0.3f 0.7f * t); p.b brightness * (0.1f 0.9f * t * t); p.a t 0.1f ? 1.0f : t * 10.0f;这里p.b用了t * t是为了让蓝色衰减得比绿色快这样颜色会从白偏黄再偏红过渡更自然。2.4 重力与阻力让粒子下落更真实爆炸之后粒子在重力的作用下开始下落。但如果你只加重力粒子会像石头一样直直掉下来看起来很生硬。真实的烟花粒子在下落时还会受到空气阻力的影响速度会逐渐衰减。我的做法是每帧对速度施加一个阻力系数p.velX * dragFactor; p.velY * dragFactor; p.velZ * dragFactor; p.velY gravity * deltaTime;dragFactor一般在 0.98 到 0.995 之间按 60 帧每秒计算。这个系数越小粒子减速越快看起来越轻。对于烟花来说我一般用 0.99 左右让粒子在下落时保持一定的速度感但不会太快。重力系数gravity用 200 到 400 像素/秒平方比较合适。太小了粒子飘着不落太大了掉得太快没有美感。实操心得阻力和重力要配合调整。如果阻力大重力也要相应加大否则粒子会悬浮在空中不动。我一般先把重力定在 300然后调阻力直到下落速度看起来舒服为止。3. 完整实操流程与关键环节实现3.1 初始化 DirectX 环境与顶点缓冲在写粒子逻辑之前得先把 DirectX 的环境搭起来。这部分是标准流程但有几个和粒子系统相关的点需要特别注意。首先是顶点格式。粒子渲染本质上就是画一堆带纹理的四边形billboard每个四边形需要四个顶点。但为了简化我直接用点精灵point sprite的方式——每个粒子就是一个顶点在几何着色器或者顶点着色器里扩展成四边形。不过点精灵在不同硬件上的支持不一致所以我更倾向于用实例化渲染或者直接构建四边形顶点。我最终用的是动态顶点缓冲。每帧根据活跃粒子数量把粒子数据转换成顶点数据写入缓冲。顶点结构包含位置、颜色、大小struct ParticleVertex { float x, y, z; float r, g, b, a; float size; }; D3D11_BUFFER_DESC bd {}; bd.Usage D3D11_USAGE_DYNAMIC; bd.ByteWidth sizeof(ParticleVertex) * MAX_PARTICLES * 4; bd.BindFlags D3D11_BIND_VERTEX_BUFFER; bd.CPUAccessFlags D3D11_CPU_ACCESS_WRITE;每个粒子对应四个顶点四边形的四个角所以缓冲大小要乘以 4。索引缓冲用固定的四边形索引模式每四个顶点对应两个三角形。混合状态用加法混合Additive Blending这样粒子重叠的地方会更亮符合发光效果D3D11_BLEND_DESC blendDesc {}; blendDesc.RenderTarget[0].BlendEnable TRUE; blendDesc.RenderTarget[0].SrcBlend D3D11_BLEND_SRC_ALPHA; blendDesc.RenderTarget[0].DestBlend D3D11_BLEND_ONE; blendDesc.RenderTarget[0].BlendOp D3D11_BLEND_OP_ADD;加法混合的公式是最终颜色 源颜色 * 源Alpha 目标颜色。这样多个粒子叠加时颜色会累加产生发光的效果。3.2 每帧更新循环的完整实现更新循环是整个粒子系统的心脏。我把它分成几个步骤按顺序执行第一步处理发射器。如果当前有烟花正在升空就发射拖尾粒子如果到达爆炸点就生成爆炸粒子。第二步遍历所有活跃粒子更新它们的生命值。如果生命值降到 0 以下就把它和最后一个活跃粒子交换然后activeCount--。第三步对存活的粒子更新速度和位置。第四步根据归一化生命值更新颜色和大小。void Update(float deltaTime) { // 发射逻辑 UpdateEmitters(deltaTime); // 更新粒子 for (int i 0; i activeCount; ) { Particle p particlePool[i]; p.life - deltaTime; if (p.life 0.0f) { // 和最后一个活跃粒子交换 particlePool[i] particlePool[activeCount - 1]; activeCount--; continue; // 不递增 i因为新换过来的粒子还没处理 } // 物理更新 p.velX * dragFactor; p.velY * dragFactor; p.velZ * dragFactor; p.velY gravity * deltaTime; p.posX p.velX * deltaTime; p.posY p.velY * deltaTime; p.posZ p.velZ * deltaTime; // 颜色更新 float t p.life / p.maxLife; float brightness powf(t, 0.5f); p.r brightness; p.g brightness * (0.3f 0.7f * t); p.b brightness * (0.1f 0.9f * t * t); p.a t 0.1f ? 1.0f : t * 10.0f; i; } }这里有个容易出错的地方当粒子死亡被交换后不能递增i因为换过来的粒子还没有被处理。用continue跳过递增下一轮循环会重新处理当前位置。3.3 渲染提交从粒子数据到屏幕像素渲染阶段要把粒子数据转换成 GPU 能理解的顶点数据。每个粒子需要生成四个顶点位置是粒子中心加上偏移量。偏移量取决于粒子的大小和相机的朝向——粒子始终面向相机这就是 billboard 效果。void Render() { D3D11_MAPPED_SUBRESOURCE mapped; context-Map(vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, mapped); ParticleVertex* verts (ParticleVertex*)mapped.pData; int vertIndex 0; for (int i 0; i activeCount; i) { Particle p particlePool[i]; float halfSize p.size * 0.5f; // 四个角 verts[vertIndex] { p.posX - halfSize, p.posY - halfSize, p.posZ, p.r, p.g, p.b, p.a, p.size }; verts[vertIndex] { p.posX halfSize, p.posY - halfSize, p.posZ, p.r, p.g, p.b, p.a, p.size }; verts[vertIndex] { p.posX halfSize, p.posY halfSize, p.posZ, p.r, p.g, p.b, p.a, p.size }; verts[vertIndex] { p.posX - halfSize, p.posY halfSize, p.posZ, p.r, p.g, p.b, p.a, p.size }; } context-Unmap(vertexBuffer, 0); // 绘制 UINT stride sizeof(ParticleVertex); UINT offset 0; context-IASetVertexBuffers(0, 1, vertexBuffer, stride, offset); context-Draw(activeCount * 6, 0); // 每个粒子两个三角形六个索引 }这里用的是D3D11_MAP_WRITE_DISCARD它会丢弃缓冲里的旧数据返回一块新的内存。这样做避免了 GPU 还在读取旧数据时 CPU 写入造成的同步等待。顶点着色器里粒子的位置直接输出颜色和大小传给像素着色器。像素着色器里可以根据到中心的距离做一个圆形的 alpha 衰减让粒子边缘柔和一些float2 center float2(0.5, 0.5); float dist length(input.uv - center) * 2.0; float alpha saturate(1.0 - dist); return float4(input.color.rgb, input.color.a * alpha);这样粒子就不是硬边的方块而是柔和的圆形光点。3.4 多朵烟花的调度与随机化单朵烟花看起来太单调实际演示时通常会同时或连续发射多朵。我的做法是维护一个发射器列表每个发射器有自己的状态等待、升空、爆炸、完成和参数发射位置、爆炸高度、颜色主题、粒子数量。struct FireworkEmitter { float startX, startY; float targetY; float currentY; float riseSpeed; int state; // 0等待, 1升空, 2爆炸, 3完成 float colorR, colorG, colorB; int particleCount; };每朵烟花的参数都随机化发射位置在屏幕底部一定范围内随机爆炸高度在屏幕上半部分随机颜色主题从预设的几组配色里选红金、蓝银、绿紫等粒子数量在 500 到 1500 之间随机。发射时机也要随机化。如果所有烟花同时发射看起来像齐射不够自然。我一般让每朵烟花在上一朵发射后 0.5 到 2 秒内随机延迟发射。实操心得多朵烟花同时存在时粒子池的峰值使用量会很高。我建议在粒子池快满的时候优先让老粒子死亡加速它们的生命衰减而不是拒绝新粒子的生成。否则会出现新烟花炸不出来或者炸出来缺一块的情况。4. 常见问题与排查技巧实录4.1 粒子不显示或者显示为方块这是最常见的问题通常有几个原因。第一个原因是混合状态没设置对。如果用了加法混合但 alpha 值全是 0粒子就完全不可见。检查一下粒子的 alpha 是否在 0 到 1 之间以及混合公式是否正确。第二个原因是顶点缓冲没有正确更新。用Map之后一定要Unmap否则 GPU 读不到数据。另外检查Draw的顶点数量是否正确——每个粒子 6 个索引如果只画了activeCount个索引就只能看到六分之一个粒子。第三个原因是着色器里的坐标变换有问题。如果粒子的世界坐标没有正确转换到裁剪空间它们可能被画到了屏幕外面。检查一下视图矩阵和投影矩阵是否设置正确。如果粒子显示为硬边方块而不是圆形光点那就是像素着色器里的圆形 alpha 计算没生效。检查 UV 坐标是否正确传递以及saturate函数是否用对了。4.2 帧率突然下降的排查思路粒子系统最常见的性能问题是帧率突然下降。可能的原因和排查方法如下现象可能原因排查方法粒子数量多时帧率线性下降CPU 模拟太慢用性能分析工具看 Update 函数的耗时粒子数量少时帧率也低渲染状态切换太频繁检查是否每帧都在创建/销毁缓冲帧率周期性波动内存分配抖动检查是否有动态分配改用粒子池爆炸瞬间卡顿单帧生成粒子太多把爆炸粒子的生成分散到多帧我踩过的一个坑是在Update里用了std::vector的erase导致每帧都有大量内存搬移。改成粒子池的交换删除后帧率直接翻倍。另一个坑是顶点缓冲的大小。如果每帧都重新创建缓冲GPU 驱动会不停地分配和释放显存性能极差。正确做法是创建一次之后用Map/Unmap更新内容。4.3 粒子颜色发灰或者过曝颜色问题通常出在混合模式和颜色空间上。如果粒子颜色发灰可能是用了 alpha 混合而不是加法混合。alpha 混合会让粒子颜色和背景颜色做插值导致亮度降低。烟花特效应该用加法混合让粒子颜色叠加到背景上。如果粒子过曝一片死白可能是粒子的 alpha 值太高或者同时重叠的粒子太多。可以降低单个粒子的 alpha或者减少爆炸粒子的数量。另外在像素着色器里对最终颜色做一个 tone mapping色调映射把超过 1.0 的亮度压回来也能改善过曝问题。还有一个容易被忽略的点如果渲染目标用的是 sRGB 格式但着色器输出的是线性颜色颜色会偏暗。需要在着色器里做 gamma 校正或者把渲染目标改成线性格式。4.4 粒子拖尾断裂或者不连续拖尾断裂通常是因为发射频率不够。如果每帧只发射一个拖尾粒子而粒子速度很快两个粒子之间的间距就会很大看起来就是断断续续的。解决方法有两个一是提高发射频率每帧发射多个粒子位置在上一帧和当前帧之间插值二是增大拖尾粒子的大小让它们互相重叠。我一般用第一种方法每帧发射 2 到 3 个拖尾粒子位置在主粒子的当前位置和上一帧位置之间做线性插值。这样即使主粒子移动很快拖尾也是连续的。for (int i 0; i trailCount; i) { float t (float)i / trailCount; float px lerp(prevX, currentX, t); float py lerp(prevY, currentY, t); EmitTrailParticle(px, py); }4.5 爆炸形状不对称或者偏向一侧如果爆炸粒子明显偏向某个方向通常是随机数生成的问题。检查一下球面坐标的生成公式特别是phi的计算。如果用rand() * PI而不是acos(1 - 2 * rand())粒子会在两极聚集。另一个可能的原因是速度的随机范围不对称。如果velX的范围是[-100, 200]而不是[-200, 200]爆炸就会偏向一侧。检查所有速度分量的随机范围是否关于零对称。还有一种情况是粒子的初始位置有偏移。如果所有爆炸粒子都从同一个点生成但那个点不在主粒子的爆炸位置上爆炸中心就会偏。确保爆炸粒子的生成位置和主粒子的消失位置一致。4.6 粒子池耗尽的处理策略当粒子池满了新的粒子无法生成时有几种处理策略第一种是直接丢弃新粒子。这会导致烟花爆炸不完整但不会崩溃。适合对视觉效果要求不高的场景。第二种是强制回收最老的粒子。遍历粒子池找到剩余生命最少的粒子把它替换掉。这样做能保证新烟花完整但老烟花会突然消失。第三种是动态扩容。在粒子池满时分配一块更大的内存把旧数据拷过去。这样做不会丢失任何粒子但会有一次内存分配的开销可能造成帧率波动。我一般用第二种策略但做一个小优化不是找剩余生命最少的而是找剩余生命比例最少的life / maxLife最小。这样能优先回收那些已经接近消亡的粒子视觉上的突兀感最小。注意不管用哪种策略都要确保粒子池的索引不会越界。在生成新粒子之前一定要检查activeCount MAX_PARTICLES。5. 进阶优化与效果提升的实操建议5.1 用噪声让爆炸更自然纯球面爆炸看起来太完美了真实的烟花爆炸会有一些不规则性。我试过在速度方向上叠加一层噪声效果提升很明显。具体做法是用一个简单的 3D 噪声函数比如 Perlin 噪声的简化版根据粒子的方向向量采样一个噪声值然后把速度乘以1 noise * 0.3。这样爆炸就不是完美的球壳而是有一些凸起和凹陷看起来更自然。噪声的频率也要调。频率太高爆炸会变得太碎频率太低又看不出效果。我一般用频率 2 到 4噪声强度 0.2 到 0.4。5.2 二次爆炸让烟花更有层次单次爆炸的烟花看多了会腻。我后来加了一个二次爆炸的效果一部分爆炸粒子在飞行一段时间后再次爆炸成更小的粒子。实现上给粒子加一个isSecondary标记和secondaryTimer。当计时器归零时把这个粒子替换成一组更小的粒子速度方向随机颜色偏暖。这个效果的关键是控制二次爆炸的比例。如果所有粒子都二次爆炸看起来太乱我一般让 10% 到 20% 的粒子有二次爆炸这样既有层次感又不会太杂乱。5.3 性能优化的几个实用技巧第一个技巧是减少每帧的顶点数据量。如果粒子很小比如小于 2 像素可以直接跳过渲染因为根本看不清。这样能省下不少顶点。第二个技巧是用 LOD细节层次。远处的烟花用更少的粒子近处的用更多。判断标准可以是粒子到相机的距离或者粒子在屏幕上的投影大小。第三个技巧是把粒子的更新分成奇偶帧。奇数帧更新一半粒子偶数帧更新另一半。这样每帧的 CPU 计算量减半但视觉上几乎看不出差别因为粒子的位置变化很小。第四个技巧是用 SIMD 指令加速粒子更新。把粒子数据按 SoA结构体数组转数组结构体的方式重新排列然后用 SSE 或 AVX 指令批量计算。这个优化比较底层但效果显著粒子数量多的时候能提升 2 到 3 倍性能。5.4 参数调优的实战记录最后分享一组我调了很久才满意的参数可以直接参考参数数值说明升空速度400 像素/秒太快看不清拖尾太慢显得无力爆炸粒子数800兼顾饱满度和性能爆炸速度范围150-400 像素/秒速度差产生层次感重力300 像素/秒平方下落速度适中阻力系数0.99每帧衰减 1%粒子生命周期1.5-2.5 秒太短来不及下落太长显得拖沓拖尾发射频率每帧 3 个保证拖尾连续拖尾生命周期0.3-0.5 秒刚好落在主粒子后面这些参数不是绝对的你可以根据自己的屏幕分辨率和审美偏好调整。我的建议是先把重力定下来然后调爆炸速度最后调生命周期和阻力。每次只改一个参数改完看效果这样能快速找到感觉。我在实际项目里发现烟花的颜色比形状更重要。同样的爆炸形状配色好看的话整体效果会提升一个档次。我常用的几组配色是红金红为主金色拖尾、蓝银蓝色爆炸银色闪烁、绿紫绿色和紫色交替、彩虹每个粒子随机色相。你可以多试几组找到自己喜欢的风格。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号