游戏引擎设计 - 粒子系统

本文介绍了3D游戏引擎中一套基于3dsmax标准的粒子系统,支持公告板粒子、模型粒子、Metaball流体粒子等多种粒子效果。系统允许用户自定义粒子的发射方向、速度、大小、旋转方式和生命周期,以及空间扭曲如重力、风力等。此外,还提供了刀光插件,用于创建特殊视觉效果。粒子参数丰富,可实现复杂的粒子动画效果,且粒子系统与3dsmax高度兼容,方便导入导出。

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

粒子系统是一个游戏引擎必不可少的部分,粒子系统的实现并没有统一的标准,各类3D动画软件和游戏引擎的实现也不尽相同。

本套粒子系统基于3ds max的标准,实现了max9绝大部分的粒子参数调节功能。暂不支持3dsmax的粒子流源。粒子动画在3dsmax中做好后可以直接导出,所见即所得,连粒子编辑器都省了。实现了公告板粒子、模型粒子、Metaball流体粒子、粒子繁殖、粒子附着模型,空间扭曲等功能。

导出的动画依托于MC_Particle来驱动粒子系统的参数随关键帧而改变(MC_Particle是引擎中关键帧动画的派生类)。关键帧动画可以通过附着曲线控制器灵活设置粒子路径、旋转朝向等。在关键帧上还可以调节其它animatable参数,比如粒子发射密度、速度、大小、空间扭曲参数等。 

另外仿照max中的刀光插件,做了一套专门绘制刀光拖尾的小组件。

粒子可调参数类型:

//粒子绘制类型
    enum ParticleDrawType
    {
        PDT_PLANE       = 0,//3d面  3d自旋
        PDT_CROSS2    = 1,//双十字交叉面
        PDT_CROSS3    = 2,//三十字交叉面
        PDT_BILLBORD  = 3,//公告板 z自旋
        PDT_MESH          = 4,//模型
        PDT_METABALL  = 5,//水滴
        PDT_NUM
    };

    //粒子发射方向类型
    enum ParticleDirType
    {
        PMT_ObjUp        , //初始[0, 1,0],跟随物体变换
        PMT_ObjDown    , //初始[0,-1,0],跟随物体变换
        PMT_Rand         , //随机
        PMT_Fix             , //固定轴 对于cloud不一定等同于物体的dir
        PMT_FaceNormal , //沿附着面法线
    };
 

 //粒子旋转类型
    enum ParticleRotType
    {
        PRT_RotRand      , //随机轴自旋
        PRT_RotFixed      , //固定轴自旋
        PRT_RotTumble   , //翻滚
        PRT_RotMotion    , //粒子自动朝向运动方向,并且绕head旋转,对于公告板类型无效
    };
  

 //粒子发射方向异性类型
    enum ParticleDirVariationType
    {
        PDVT_Add  = 0, //直接相加
        PDVT_Spot,     //锥角平均
        PDVT_Spot2,    //锥角正太分布
    };

//发射器形状类型
    enum EmitterShapeType
    {
        EST_POINT       = 0,//点
        EST_PLANE      = 1,//面
        EST_BOX           = 2,//正方体
        EST_SPHERE    = 3,//球
        EST_AIRSPHERE  = 4,//空心球
        EST_CYLINDER     = 5,//圆柱
        EST_MESH         = 6,//附着模型
        EST_SPAWN      = 9,//只能通过别的粒子拖尾或繁殖
        EST_NUM
    };
    

//贴图 uv类型
    enum TextureUVType
    {
        TU_Face  ,   //面uv
        TU_Life  ,   //根据life平滑移动v坐标,每个粒子再随机u坐标,
        TU_Life2 ,   //根据life幻灯片移动v坐标
        TU_Dist  ,   //根据距离
        TU_Dist2 ,
    };
    
    //粒子繁殖类型
    enum ParticleSpawnType
    {
        PST_None                 ,//无繁殖
        PST_DieOnCollision  ,//碰撞导向板时死亡
        PST_SpawnOnCollision,//碰撞导向板时繁殖
        PST_SpawnOnDeath    ,//死亡时繁殖
        PST_SpawnTrails          ,//拖尾繁殖
    };
    

//空间扭曲类型
    enum SpaceWrapType
    {
        SWT_Gravity      , //重力
        SWT_Wind         , //风力,类似重力,多了湍流
        SWT_Drag         , //风阻 水阻 墙阻
        SWT_Vortex       , //旋涡
        SWT_Deflector    , //导向板
    };
    

效果:

添加了地面导向板、粒子拖尾、粒子附模

 metaball 粒子:

 更多参数见3dsmax粒子修改器或下面的头文件:

//========================================================
//  @Date:     2016.05
//  @File:     Include/Render/Particle.h
//  @Brief:     Particle
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================

#pragma once

#ifndef  __Particle__H__
#define  __Particle__H__

#include "General/String.h"
#include "Math/Mathlib.h"
#include "Math/MathLibIncDbg.h"
#include "Render/Texture.h"

/**
1,一般勾选面贴图否则贴图颜色随生命变化
2,发射器的形状可以靠附着模型表面来指定, 粒子速度指向面法线方向
5,可以驱动metallball,但不承担SPH模拟。
todo: 将导向板风力等导出到额外物体  棍扫落叶:风力SpaceWind绑定到骨骼
*/
class File;
class Curve;
class MetaballSys3D;
class VertexBuffer;
class IndexBuffer;
class ListItemUpdater;

namespace RendSys
{
	class Mesh;
	class MovieClip;
	class MC_Frame;
	class ParticleEmitter;

	//粒子 
	class Particle					
	{
	public:
		Particle();
		inline void Update(float timeFrame);
		void ForcePos(vec3& pos);
		void ForceSpeed(vec3& speed);
		void Wander(vec3& pos1,vec3& pos2);
	
		Particle* prev;
		Particle* next;	
		vec3   pos;                 //位置
		vec3   posSpeed;            //速度

		float  size;                //大小

		vec3   rotAxis;             //粒子产生时 生成旋转轴
		float  rotSpeed;            //旋转速度
		float  rot;                 //粒子产生时 =rotPhase;

		float  life;	            //生命 
		float  lifeReverse;	        //剩余生命

		bool   doFlected;           //是否执行导向板反弹
		bool   deflected;           //反弹标记

		//气泡运动
		vec3   bubbleDis;          //方向+长度
		float  lastSinBubble;
		float  bubble;             //气泡当前相位 0~twopi 
		float  bubbleSpeed;

		float  d;  //d[8] 距离导向板的有向距 目前一个发射器最多只支持一个导向板

		int    generation;         //粒子代数
		int    spawnNum;           //繁殖或拖尾数量

		float  u;                  //u坐标

		Color  color;		       //颜色
		Color  colorSpeed;	
	};							
	
	//粒子发射方向类型
	enum ParticleDirType
	{
		PMT_ObjUp      , //初始[0, 1,0],跟随物体变换
		PMT_ObjDown    , //初始[0,-1,0],跟随物体变换
		PMT_Rand       , //随机
		PMT_Fix        , //固定轴 对于cloud不一定同物体的dir
		PMT_FaceNormal , //沿附着面法线
	};
	const char* ParticleDirTypeToString(int enumeration);
	bool StringToParticleDirType(const char* str,int& type);

	//粒子旋转类型
	enum ParticleRotType
	{
		PRT_RotRand      , //随机轴自旋
		PRT_RotFixed     , //固定轴自旋
		PRT_RotTumble    , //翻滚
		PRT_RotMotion    , //粒子自动朝向运动方向,并且绕head旋转,对于公告板类型无效
	};
	const char* ParticleRotTypeToString(int enumeration);
	bool StringToParticleRotType(const char* str,int& type);

	//粒子发射方向异性类型
	enum ParticleDirVariationType
	{
		PDVT_Add  = 0, //直接相加
		PDVT_Spot,     //锥角平均
		PDVT_Spot2,    //锥角正太
	};

	//粒子绘制类型
	enum ParticleDrawType
	{
		PDT_PLANE     = 0,//3d面  3d自旋
		PDT_CROSS2    = 1,//双十字交叉面
		PDT_CROSS3    = 2,//三十字交叉面
		PDT_BILLBORD  = 3,//公告板 z自旋
		PDT_MESH      = 4,//模型
		PDT_METABALL  = 5,//水滴
		PDT_NUM
	};
	const char* ParticleDrawTypeToString(int enumeration);
	bool StringToParticleDrawType(const char* str,int& type);

	//发射器形状类型
	enum EmitterShapeType
	{
		EST_POINT      = 0,//点
		EST_PLANE      = 1,//面
		EST_BOX        = 2,//正方体
		EST_SPHERE     = 3,//球
		EST_AIRSPHERE  = 4,//空心球
		EST_CYLINDER   = 5,//圆柱
		EST_MESH       = 6,//附着模型
		EST_SPAWN      = 9,//只能通过别的粒子拖尾或繁殖
		EST_NUM
	};
	const char* EmitterShapeTypeToString(int enumeration);
	bool StringToEmitterShapeType(const char* str,int& type);

	//贴图 uv类型
	enum TextureUVType
	{
		TU_Face  ,   //面uv
		TU_Life  ,   //根据life平滑移动v坐标,每个粒子再随机u坐标,可以做出烟花效果?? 会出现正方形方框
		TU_Life2 ,   //根据life幻灯片移动v坐标
		TU_Dist  ,   //根据距离
		TU_Dist2 ,
	};
	const char* TextureUVTypeToString(int enumeration);
	bool StringToTextureUVType(const char* str,int& type);


	//粒子繁殖类型
	enum ParticleSpawnType
	{
		PST_None            ,//无繁殖
		PST_DieOnCollision  ,//碰撞导向板时死亡
		PST_SpawnOnCollision,//碰撞导向板时繁殖
		PST_SpawnOnDeath    ,//死亡时繁殖
		PST_SpawnTrails     ,//拖尾繁殖
	};
	const char* ParticleSpawnTypeToString(int enumeration);
	bool StringToParticleSpawnType(const char* str,int& type);


	//空间扭曲类型
	enum SpaceWrapType
	{
		//SWT_Motor      , // 
		//SWT_Push       , // 
		//SWT_PBomb      , // 
		//SWT_PathFollow , //  
		//SWT_Displace   , // 
		SWT_Gravity      , //重力
		SWT_Wind         , //风力,类似重力,多了湍流
		SWT_Drag         , //风阻 水阻 墙阻
		SWT_Vortex       , //旋涡
		SWT_Deflector    , //导向板
	};
	const char* SpaceWrapTypeToString(int enumeration);
	bool StringToSpaceWrapType(const char* str,int& type);

	//空间扭曲
	class SpaceWrap
	{
	public:
		SpaceWrap();
		virtual ~SpaceWrap();
		virtual SpaceWrap* Clone();
		virtual void DebugRender();
		virtual void Advance(float time){};
		/*virtual*/ inline void AffectParticle(Particle* pariticle,float time){};

		String name;
		int    spaceWrapType;
		bool   active; 
		vec3   pos,dir;
		vec3   posL;
		mat4   transform;
	};

	//重力
	class SpaceGravity:public SpaceWrap
	{
	public:
		SpaceGravity();;
		virtual SpaceGravity* Clone();
		/*virtual*/inline void AffectParticle(Particle* pariticle,float time);
		int   type;            //0 Planar Spherical
		float strength;        //强度
		float decay;           //衰减

		float strength_tempori;
	};

	//目前只有风力提供衰减,方便玩家通过风力和粒子交互
	class SpaceWind:public SpaceWrap
	{
	public:
		SpaceWind();;
		virtual SpaceWind* Clone();
		virtual void Advance(float time);
		/*virtual*/inline void AffectParticle(Particle* pariticle,float time);
		int   type;            //0 Planar Spherical
		float strength;        //强度
		float decay;           //衰减

		float turbulence;      //湍流强度default: 1.0 -- animatable
		float frequency;       //湍流频率
		float scale;

		float turbulenceTime;
		float stengthA;
		float stengthB;
		vec3  dirA;
		vec3  dirB;

		float strength_tempori;
	};

	//风阻 水阻 墙阻
	class SpaceDrag:public SpaceWrap
	{
	public:
		SpaceDrag();;
		virtual SpaceDrag* Clone();
		/*virtual*/inline void AffectParticle(Particle* pariticle,float time);
		int   type;            //0 Planar Spherical
		float strength;        //强度
		float decay;           //衰减

		float turbulence;      //湍流强度default: 1.0 -- animatable
		float frequency;       //湍流频率
		float scale;

		float turbulenceTime;
		float stengthA;
		float stengthB;
		vec3  dirA;
		vec3  dirB;
	};

	//旋涡
	class SpaceVortex:public SpaceWrap
	{
	public:
		SpaceVortex();;
		virtual SpaceVortex* Clone();

		/*virtual*/inline void AffectParticle(Particle* pariticle,float time);

		float taperstrength; //变尖强度 default: 100.0 ; Taper_Length  
		float tapershape;    //松紧 default: 1.0 ;   Taper_Curve  Range=1.0 to 4.0. 

		bool  rangeless;     //是否限制范围Unlimited_Range

		float axialstrength; //default: 0.1 轴向运动速度  Axial_Drop_Strength
		float axialrange;    //轴向阻尼开始衰减的位置(距离icon中心 而不是轴线) default: 100.0  Axial_Range
		float axialfalloff;  //轴向阻尼衰减到零的位置 default: 1000.0  Axial_Falloff
		float axialdamping;  //轴向阻尼 每帧减少百本比 default: 5.0    Axial_Damping  
		//particle motion parallel to the drop axis is restrained per frame. Default=5.0. Range=0 to 100. 

		float rotationstrength; //旋转速度 default: 0.5  Orbital_Speed_Strength
		float rotationrange;    //旋转阻尼开始衰减的位置 default: 100.0   Orbital_Range
		float rotationfalloff;  //旋转阻尼衰减到零的位置 default: 1000.0  Orbital_Falloff
		float rotationdamping;  //旋转阻尼 每帧减少百本比default: 5.0     Orbital_Damping

		float radialstrength; //径向速度 default: 0.5  Radial_Pull_Strength
		float radialrange;    //径向阻尼开始衰减的位置 default: 100.0  Radial_Range
		float radialfalloff;  //径向阻尼衰减到零的位置 default: 1000.0 Radial_Falloff
		float radialdamping;  //径向阻尼 每帧减少百本比default: 5.0    Radial_Damping

		bool  direction;      //是否逆时针
	};
	

	////!导向板碰撞后增殖(子弹扫射冒烟 箭群碰墙落下插地) 
	////!蘑菇云 时间碰撞器触发后爆开
	class SpaceDeflector:public SpaceWrap
	{
	public:
		enum ReactorAction
		{
			RT_REFLECT = 0,//反射
			RT_TARGETPOS,  //定点反弹
			RT_TARGETDIR,  //定向反弹
			RT_REDUCELIFE  //减life  
		};

		//竖直反弹板加速

		SpaceDeflector();;
		virtual SpaceDeflector* Clone();
		/*virtual*/inline bool AffectParticle(Particle* particle,float time);

		ReactorAction m_action;
		//!定点反弹
		vec3    m_targetPos;
		//!定向反弹
		vec3    m_targetDir;

		//!法线反弹
		int   type;            //0 	

		float width;           //大小
		float length;
		float bounce;          //强度
		float variation;       //强度异性
		float chaos;           //反射角异性
		float friction;        //摩擦
		float inheritVelocity; //运动继承

		float d;               //原点到面有向距离


		float width_tempori ;
		float length_tempori;
	};
	

	/**粒子发射器
	* snow rain 跟随摄像机    瞬间产生线
	* 瀑布 火焰 流体 流体模型碰撞  吸魔
	*/
	class ParticleEmitter
	{
		friend class Particle;
		friend class ParticleEditorGui;
	public:
		struct Vertex
		{
			float x, y, z;
			float u, v, w;
			float r, g, b,a;
		};
		
		ParticleEmitter();
		ParticleEmitter(const ParticleEmitter& emitter);
		void operator= (const ParticleEmitter& emitter);
		virtual ~ParticleEmitter();

		//重置发射器
		void IdentityEmitter();
		void GotoAndPlay(float timeFrame);
		void Free();
		void LoadFromFile(const char* filename);
		void SaveToFile  (const char* filename);		
		void LoadingUpdate();
		void InitBuffer();
		void Render();
		void Update(float time);
		void EmitParticle(Particle* particle);
		void EmitParticle(Particle* parent,Particle* particle);

		//mat对发射器的作用分两种,1只改变新发射的粒子,2改变所有存活粒子(todo)
		void SetEmitterMat(const mat4& mat);
		//空间扭曲只改变位置大小?不改变旋转
		void OffsetSpaceWrap(const vec3& pos,float fitScale);
		//空间扭曲改变旋转,一般不旋转重力
		void OffsetSpaceWrap(const mat4& mat);

		//emitterdef
		void SetEmitterBound(vec3& bound);
		void SetEmitterShapeType(EmitterShapeType type);
		void SetParticleNum(int num);
		bool SetTexture(const char* fileName);
		RendSys::MovieClip* GetEmitterMovie() const;
		void                SetEmitterMovie(RendSys::MovieClip* movie);
		bool SetParticleMovie(const char* fileName);
		bool SetParticleMovie(RendSys::MovieClip* movie);

		//SpaceWrap
		bool AddSpaceWrap(SpaceWrap* spaceWrap);

		//
		void UpdateAllItems(ListItemUpdater& updater);

protected:
		//构建缓存区
		void BuildBuffer();
		//从活动列表删除,加入非活动列表头部
		void PushInactive(Particle* particle);
		Particle* PopInactive();

protected:
		Particle*      m_activeParticles;   //活动粒子链表
		Particle*      m_inactiveParticles; //非活动粒子链表
		int            m_activeParticleNum;
		float          m_toResetParticleNum;
		float          m_regulatorTime; 
		TexturePtr     m_texture;
		//!作为粒子的模型,顶点拷贝同一批次渲染
		RendSys::MovieClip* m_particleMovie;
		//!表面发射模型,可能被克隆多个实体
		RendSys::MovieClip* m_emitterShapeMovie;
		//!使得仅有旋转时也能产生运动继承 只有PCloud、 framemovie+PArray时有效
		//对于SuperSpray发射点无偏移所以无效,对于boneMovie需要lastMixMesh才有效
		//mat4     m_lastEmitterShapeFrame;

		//流体粒子渲染器
		MetaballSys3D* m_metaballSys;

		Particle*      m_particles;	  //所有粒子数组
		Vertex*        m_vertexs;
		VertexBuffer*  m_vertexBuffer;
		IndexBuffer*   m_indexBuffer;		

public://can not animatable def
		String     m_emitterName;
		//随机种子
		int        m_emitterSeed;

		//发射器形状类型
		EmitterShapeType    m_emitterShapeType;
		//!表面发射模型名字
		String              m_emitterShapeMovieName;
		int                 m_emitterMaxParticle;
		ParticleDrawType    m_particleDrawType;
		String              m_particleMovieName;

		//metaball 参数
		float               m_particleMetaTension;//张力 数值越大 水滴越难融合  和3dsmax有出入 要求0.1~0.5之间 
		float               m_particleMetaTensionVariation; 
		float               m_particleMetaRenderCoarsness;//渲染粗糙度

		float         m_particleLife;
		float         m_particleLifeVariation;

		//!考虑到效率,序列贴图必须在一张纹理中
		String        m_textureName;		
		int           m_opacityType;
		TextureUVType m_textureUVType;
		int           m_textureUTile;
		float         m_textureFrameTime;

public://animatable def
		mat4    m_emitterMatrix;             //发射器变换矩阵
		vec3    m_emitterSpeed;              //发射器运动速度,用作运动继承,不是粒子发射速度
								  
		//<<粒子生成
		vec3    m_emitterBound;	             //发射盒
		float   m_emitterDensity;            //!每帧发射个数:<=0时不发射

		Color	m_particleStartColor;		
		Color	m_particleEndColor;	

		float  m_particleSpeed;              //粒子发射速度
		float  m_particleSpeedVariation;	 //粒子速度异性
		ParticleDirType  m_particleDirType;  //粒子方向类型
		float  m_particleDirVariation;		 //粒子方向异性
		int    m_particleDirVariationType;	 //粒子方向异性类型
		vec3   m_particleFixDir;             //固定轴向时使用

		float  m_particleSize;               //粒子大小
		float  m_particleSizeVariation;      //粒子大小异性
		float  m_particleSizeGrowTime;       //粒子出生到最大的时间(0.01~m_size)
		float  m_particleSizeFadeTime;       //粒子最大到死亡的时间(m_size~m_size/10)

		//<<翻滚
		//3dsmax 中只产生粒子旋转效果无位移效果
		float  m_tumble;                     //翻滚 翻滚时自旋失效 todo:翻滚和粒子运动速度有相关性 生命周期内多次随机改变翻滚轴 翻滚速度为正弦波
		float  m_tumbleSpeed;

		//<<气泡运动 
		//可以代替上面的翻滚效果
		//3dsmax 中只产生粒子位移效果
		float  m_particleBubble               ;      //气泡运动幅度 运动速度无关 沿轴叠加正弦位移
		float  m_particleBubbleVariation      ;
		float  m_particleBubblePeriod         ;
		float  m_particleBubblePeriodVariation;

		//<<旋转
		ParticleRotType  m_particleRotType;          //0:随机轴 1:固定轴 2:运动方向
		float  m_motionStretch;                      //for 2 rot_motion
		vec3   m_particleRotAxis;                    //自旋轴
		float  m_particleRotAxisVariation;
		float  m_particleRotSpeed;
		float  m_particleRotSpeedVariation;
		float  m_particleRotPhase;
		float  m_particleRotPhaseVariation;

		//<<运动继承 animatable 
		//3dsmax中旋转运动不会产生运动继承
		float  m_particleMotionInherit;           //运动继承的例子数量
		float  m_particleMotionInheritMultiplier; //继承强度
		float  m_particleMotionInheritVariation;  //强度异性

		//<<粒子繁殖 
		//粒子繁殖后可能模型 大小 速度 旋转 生命值都可能变
		//??todo 采用有别于3dsmax的改进方案:添加发射形状类型EST_SPAWN???
#define MaxSpawnGeneration 4
		enum  ChaosType
		{
			CT_Slow = 0, //根据spawn_Speed_Chaos减慢粒子速度
			CT_Fast = 1, //加快
			CT_Both = 2,

			CT_Down = 0, //变小
			CT_Up   = 1, //变大
			//CT_Both = 2,

			CT_Fixed = -1,//Chaos固定,而不在范围内随机
		};
		ParticleSpawnType m_spawnType;
		float     m_spawn_Affects;               //执行繁殖的例子百分比
		int       m_spawn_Generations;           //繁殖代数
		int       m_spawn_Multiply;              //繁殖个数		
		float     m_spawn_MultiplyVariation;
		int       m_lifeAfterCollision;          //碰撞后存活帧数
		float     m_lifeAfterCollisionVariation;

		float     m_spawn_DirChaos;              //0~1 子粒子继承父粒子速度后的改变.  0不变.  1完全随机  0.5随机到90度 

		float     m_spawn_SpeedChaos;            //0~1 子粒子速度改变  
		ChaosType m_spawn_SpeedType; 
		bool      m_spawnInheritVelocity;        //inherit the speed of their parents, in addition to the effect of the .spawn_Speed_Chaos value. 	 

		float     m_spawn_Scale_Chaos;
		ChaosType m_spawnScaleType;

		int       m_spawnLifeQueue[MaxSpawnGeneration];       // 每一代的粒子寿命
		float     m_objectMutationQueue[MaxSpawnGeneration];  // 每一代的粒子模型


		//<<空间扭曲 
		//todo animatable
#define MaxSpaceWrap 8
		SpaceWrap*  m_spaceWraps[MaxSpaceWrap];          //
		int         m_spaceWrapNum;

		//适用性缩放 影响的因素有 粒子大小、速度  发射器boundbox  空间扭曲力 
		//粒子的出生的位置、速度都被m_emitterMatrix自动缩放了
		//发射器boundbox、speed也自动计算了
		float       m_fitScale;

	};


	//!刀光拖尾,配合uv可以做出闪电尾光、水墨尾光、或火花甩出去的特效(拖尾隐形延长)。
	//!配合骨骼动画,使拖体不总是一条直线,可以做出更炫的效果
	//!在长直面片上,流动的视觉幻象会减弱
	//!水墨也可以用截屏来做
	class StripTail
	{
	public:
		struct Vertex
		{
			float x, y, z;
			float u, v, w;
		};

		enum TailType
		{
			Tail_U,
			Tail_V,
		};
		StripTail(int nodeNum,int stepNum,float stepLife,const char* texture);

		~StripTail();
		void AddStep(Vertex* nodes);
		void Update(float time);
		void Render(int option);

		Vertex* m_stepVertexs;
		float*  m_stepLifes;

		Vertex* m_bufVertexs;

		VertexBuffer*  m_vertexBuffer;
		IndexBuffer*   m_indexBuffer;

#ifdef _DEBUG
		Vertex(* m_debugStepVertexs)[100];
		float(*  m_debugStepLifes)[100];
#endif

		Vertex*  m_toAddVertex;
		//int*    m_indexs;
		int m_startStepIndex;
		int m_opacityType;
		//int m_endIndex;
		int m_stepNum;
		int m_nodeNum;
		float m_cdTime;
		float m_cdMaxTime;
		float m_maxStepLifes;
		TailType m_tailType;

		TexturePtr m_texture;

		int m_activeStepNum;
	};

}

#endif

粒子出生设置部分源码:看似有些复杂,速度并不是很慢。

void ParticleEmitter::EmitParticle(Particle* particle)
	{
		particle->generation = 0;

		vec3 faceNormal,faceTangent;
		//==================^_^pos
		{
			vec3 halfEmitterBound = m_emitterBound*0.5f;
			switch(m_emitterShapeType)
			{
			case EST_PLANE:
			case EST_BOX:
				{
					particle->pos.x = halfEmitterBound.x * RandRange(-1.0,1.0);
					particle->pos.y = halfEmitterBound.y * RandRange(-1.0,1.0);
					particle->pos.z = halfEmitterBound.z * RandRange(-1.0,1.0);
					break;
				}
			case EST_SPHERE:
				{
					////先绕z轴旋转theta1 再绕y轴旋转theta2 错误方法不均匀的分布
					//float theta1 = RandRange(0.0f,TWOPI);
					//float theta2 = RandRange(0.0f,TWOPI);
					//float len = RandRange(-1.0,1.0);
					//float radLevel = cosf(theta1)*len;      //水平半径
					//float ex = halfEmitterBound.x *radLevel * cosf(theta2);
					//float ey = halfEmitterBound.y *sinf(theta1)*len;
					//float ez = halfEmitterBound.z *radLevel * sinf(theta2);
					//particle->pos += vec3(ex,ey,ez);

					//正确方法1: 先绕z轴旋转theta1 再绕新的y'轴旋转theta2 得到球面上的随机点, 乘以(在半径上随机取长度开3次方)

					//正确方法2: 
					while(1)
					{
						particle->pos.x = RandRange(-1.0,1.0);
						particle->pos.y = RandRange(-1.0,1.0);
						particle->pos.z = RandRange(-1.0,1.0);
						if (particle->pos.LengthSq()<1)
						{
							particle->pos.x *= halfEmitterBound.x;
							particle->pos.y *= halfEmitterBound.y;
							particle->pos.z *= halfEmitterBound.z;
							break;
						}
					}
					break;
				}
			case EST_AIRSPHERE:
				{
					while(1)
					{
						particle->pos.x = RandRange(-1.0,1.0);
						particle->pos.y = RandRange(-1.0,1.0);
						particle->pos.z = RandRange(-1.0,1.0);
						if (particle->pos.LengthSq()<1)
						{
							particle->pos.Normalize();
							particle->pos.x *= halfEmitterBound.x;
							particle->pos.y *= halfEmitterBound.y;
							particle->pos.z *= halfEmitterBound.z;
							break;
						}
					}
					break;
				}
			case EST_CYLINDER:
				{
					while(1)
					{
						particle->pos.x = RandRange(-1.0,1.0);
						particle->pos.y = 0;
						particle->pos.z = RandRange(-1.0,1.0);
						if (particle->pos.LengthSq()<1)
						{
							particle->pos.x *= halfEmitterBound.z;//halfEmitterBound.x;
							particle->pos.y = RandRange(-1.0,1.0)*halfEmitterBound.y;
							particle->pos.z *= halfEmitterBound.z;
							break;
						}
					}
					break;
				}
				//case EST_AIRCYLINDER:
				//	{
				//		while(1)
				//		{
				//			particle->pos.x = RandRange(-1.0,1.0);
				//			particle->pos.y = 0;
				//			particle->pos.z = RandRange(-1.0,1.0);
				//			if (particle->pos.LengthSq()<1)
				//			{
				//				particle->pos.Normalize();
				//				particle->pos.x *= halfEmitterBound.x;
				//				particle->pos.y = RandRange(-1.0,1.0)*halfEmitterBound.y;
				//				particle->pos.z *= halfEmitterBound.z;
				//				break;
				//			}
				//		}
				//		break;
				//	}
			case EST_MESH:
				{
					if (m_emitterShapeMovie)
					{		
						//对于framemovie 旋转不产生运动继承,位移已经体现在m_emitterSpeed中
						//对于bonemovie  m_lastEmitterShapeFrame无效,应该保存last mixmesh
						//if (m_particleMotionInherit>0 && m_particleMotionInheritMultiplier>0)
						//{
						//	//带运动继承
						//	m_emitterShapeMovie->GetRandMeshPoint(m_lastEmitterShapeFrame,particle->pos,faceNormal,faceTangent,m_emitterSpeed);
						//}
						//else
						{
							//不带运动继承
							m_emitterShapeMovie->GetRandMeshPoint(particle->pos,faceNormal,faceTangent);
						}
					}
					break;
				}
			default:
				break;
			}

			if (m_emitterShapeType!=EST_MESH)
			{
				particle->pos = m_emitterMatrix * particle->pos;
			}
		}

		//==================^_^speed
		{

			float randSpeed = m_particleSpeed;
			if(m_particleSpeedVariation>_EPSILON)
				randSpeed *= RandRange(1-m_particleSpeedVariation,1+m_particleSpeedVariation);

			if (m_particleDirType==PMT_Rand)
			{
				//异向失效
				particle->posSpeed.x = RandRange(-1.0,1.0);
				particle->posSpeed.y = RandRange(-1.0,1.0);
				particle->posSpeed.z = RandRange(-1.0,1.0);
				particle->posSpeed.Normalize();
				particle->posSpeed *= randSpeed;
			}
			else if (m_particleDirType==PMT_Fix)  //设置m_emitterMatrix即可
			{
				particle->posSpeed = m_particleFixDir*randSpeed;
				if (m_particleDirVariation>_EPSILON)
				{
					if (m_particleDirVariationType==PDVT_Add)
					{
						//速度相加
						float randDir = m_particleDirVariation*randSpeed;
						//particle->posSpeed.x += RandRange(-m_particleDirVariation,m_particleDirVariation);
						//particle->posSpeed.y += RandRange(-m_particleDirVariation,m_particleDirVariation);
						//particle->posSpeed.z += RandRange(-m_particleDirVariation,m_particleDirVariation);
						particle->posSpeed.x += RandRange(-randDir,randDir);
						particle->posSpeed.y += RandRange(-randDir,randDir);
						particle->posSpeed.z += RandRange(-randDir,randDir);
					}
				}
			}		
			else if (m_particleDirType==PMT_FaceNormal)
			{
				particle->posSpeed.x = 0;
				particle->posSpeed.y = 0;
				particle->posSpeed.z = randSpeed;
				if (m_particleDirVariation>_EPSILON)
				{
					if (m_particleDirVariationType==PDVT_Add)
					{
						//速度相加
						float randDir = m_particleDirVariation*randSpeed;
						//particle->posSpeed.x += RandRange(-m_particleDirVariation,m_particleDirVariation);
						//particle->posSpeed.y += RandRange(-m_particleDirVariation,m_particleDirVariation);
						//particle->posSpeed.z += RandRange(-m_particleDirVariation,m_particleDirVariation);
						particle->posSpeed.x += RandRange(-randDir,randDir);
						particle->posSpeed.y += RandRange(-randDir,randDir);
						particle->posSpeed.z += RandRange(-randDir,randDir);
					}
					else if (m_particleDirVariationType==PDVT_Spot)
					{
						vec3 dir;
						GetRandDir(m_particleDirVariation,dir);
						particle->posSpeed.x = dir.x*randSpeed;
						particle->posSpeed.y = dir.z*randSpeed;//dir.y*randSpeed;
						particle->posSpeed.z = dir.y*randSpeed;//dir.z*randSpeed;
					}
				}

				//转到face坐标系
				//mat4 TBN;
				//TBN.LookAt(faceTangent,faceBitangent,faceNormal,vec3());
				//TBN.InverseLookAt();          //equal transpose
				//particle->posSpeed = TBN*particle->posSpeed;

				vec3  faceBitangent = faceNormal.Cross(faceTangent);
				mat3 TBN;//= mat3(faceTangent, faceBitangent, faceNormal);
				TBN.mat[0] = faceTangent.x; TBN.mat[3] = faceBitangent.x; TBN.mat[6] = faceNormal.x;
				TBN.mat[1] = faceTangent.y; TBN.mat[4] = faceBitangent.y; TBN.mat[7] = faceNormal.y;
				TBN.mat[2] = faceTangent.z; TBN.mat[5] = faceBitangent.z; TBN.mat[8] = faceNormal.z;
				particle->posSpeed  = TBN*particle->posSpeed;//vec3(0,0,randSpeed);
			}		
			else if (m_particleDirType==PMT_ObjUp 
				   ||m_particleDirType==PMT_ObjDown)
			{
				particle->posSpeed.x = 0;
				particle->posSpeed.y = randSpeed;
				particle->posSpeed.z = 0;

				if (m_particleDirVariation>_EPSILON)
				{
					if (m_particleDirVariationType==PDVT_Add)
					{
						//速度相加
						float randDir = m_particleDirVariation*randSpeed;
						//particle->posSpeed.x += RandRange(-m_particleDirVariation,m_particleDirVariation);
						//particle->posSpeed.y += RandRange(-m_particleDirVariation,m_particleDirVariation);
						//particle->posSpeed.z += RandRange(-m_particleDirVariation,m_particleDirVariation);
						particle->posSpeed.x += RandRange(-randDir,randDir);
						particle->posSpeed.y += RandRange(-randDir,randDir);
						particle->posSpeed.z += RandRange(-randDir,randDir);
					}
					else if (m_particleDirVariationType==PDVT_Spot)
					{
						vec3 dir;
						GetRandDir(m_particleDirVariation,dir);
						particle->posSpeed.x = dir.x*randSpeed;
						particle->posSpeed.y = dir.y*randSpeed;
						particle->posSpeed.z = dir.z*randSpeed;
					}
				}

				if (m_particleDirType==PMT_ObjDown)
					particle->posSpeed.y *= -1;

				//转到obj坐标系
				m_emitterMatrix.AffectNormal(particle->posSpeed);
			}

		}
		//运动继承
		if (m_particleMotionInherit>_EPSILON) //运动继承的粒子数量
		{
			if (RandRange(0.0f,1.0f)<m_particleMotionInherit)
			{
				//if ( m_emitterShapeType==EST_MESH && 
				//	  (m_emitterShapeMovie->m_type == MovieClip::MT_BONE || m_emitterShapeMovie->m_type == MovieClip::MT_VERTEXANIM)
				//	 )
				//{
				    //仅有旋转也会产生运动继承
				//	speed = per trigion speed;
				//}
				//else
				{
					//仅有旋转不产生运动继承
					particle->posSpeed += m_emitterSpeed*m_particleMotionInheritMultiplier*RandRange(1-m_particleMotionInheritVariation,1+m_particleMotionInheritVariation);
				}
			}
		}

		//if (particle->posSpeed.Length()>100)
		//{
		//	int a = 0;
		//}

		//超级喷射第一帧移动距离均匀分布, 避免出现空白
		particle->pos += particle->posSpeed*(RandRange(0.0f,1.0f));

		//==================^_^
		{
			particle->size = m_particleSize;
			if(m_particleSizeVariation>_EPSILON)
			{
				particle->size *= RandRange(1-m_particleSizeVariation,1+m_particleSizeVariation);
			}

			particle->rot = m_particleRotPhase;
			particle->rot *= RandRange(1-m_particleRotPhaseVariation,1.0f);
			particle->rotSpeed = m_particleRotSpeed;
			particle->rotSpeed *= RandRange(1-m_particleRotSpeedVariation,1+m_particleRotSpeedVariation);
			if (m_particleDrawType==PDT_BILLBORD)
			{
				particle->rotSpeed *= (Rand()%2)?1:-1;
			}
			if (m_particleRotType == PRT_RotRand)
			{
				//非均匀分布 todo
				particle->rotAxis = vec3(RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f));
				particle->rotAxis.Normalize();
			}
			else if (m_particleRotType == PRT_RotFixed)
			{
				particle->rotAxis = m_particleRotAxis;
				if (m_particleRotAxisVariation>_EPSILON)
				{
					//0~pi
					mat4 mat;
					mat.FromAxisAngle(vec3(RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f)),m_particleRotAxisVariation);
					mat.AffectNormal(particle->rotAxis);
					particle->rotAxis.Normalize();
				}
			}
			else if (m_particleRotType == PRT_RotTumble)
			{
				particle->rotAxis = vec3(RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f));
				particle->rotAxis.Normalize();
			}

			particle->life = 0;
			particle->lifeReverse = m_particleLife;
			if(m_particleLifeVariation>_EPSILON)
				particle->lifeReverse *= RandRange(1-m_particleLifeVariation,1.0f);		

		}

		//==================^_^
		particle->bubble = 0; 
		particle->lastSinBubble = 0;
		if (m_particleBubble>_EPSILON)
		{
			particle->bubbleDis  = vec3(RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f))*m_particleBubble*RandRange(1.0f-m_particleBubbleVariation,1.0f);
			particle->bubbleSpeed = TWOPI/(m_particleBubblePeriod*RandRange(1-m_particleBubblePeriodVariation,1+m_particleBubblePeriodVariation));
		}
		else
		{
			particle->bubbleSpeed = 0;
		}

		//==================^_^
		if (m_spawnType==PST_SpawnTrails
			||m_spawnType==PST_SpawnOnCollision
			||m_spawnType==PST_SpawnOnDeath    
			&&RandRange(0.0f,1.0f)<=m_spawn_Affects)
		{
			particle->spawnNum = m_spawn_Multiply*RandRange(1-m_spawn_MultiplyVariation,1+m_spawn_MultiplyVariation);
		}
		else
		{
			particle->spawnNum = 0;
		}
		
		//==================^_^
		SpaceWrap*  psw;
		SpaceWrap** ppsw    = m_spaceWraps;
		SpaceWrap** swEnd = m_spaceWraps+m_spaceWrapNum;
		for (ppsw=m_spaceWraps;ppsw!=swEnd;ppsw++)
		{
			psw = *ppsw;
			switch(psw->spaceWrapType)
			{
			case SWT_Deflector:
				particle->d = Dot(psw->dir, particle->pos) - ((SpaceDeflector*)psw)->d;
				break;
			case SWT_Vortex:
				particle->posSpeed -= psw->dir*((SpaceVortex*)psw)->axialstrength;//默认朝下 初始化时只叠加一次
				break;
			}
		}

		//
		if (m_textureUVType==TU_Life)
		{
			//particle->u = RandRange(0.0f,1.0f);

			//float rat = particle->lifeReverse/this->m_particleLife;
			//if (rat<0) rat = 0;
			//else if(rat>1) rat = 1;
			//particle->color = this->m_particleStartColor*rat + this->m_particleEndColor*(1-rat);
			particle->color = Color(RandRange(0.0f,1.0f),RandRange(0.0f,1.0f),RandRange(0.0f,1.0f),1);
			particle->colorSpeed =  Color(0,0,0,0.5f/particle->lifeReverse);
		}
		else
		{
			particle->color = m_particleStartColor;
			particle->colorSpeed = (m_particleEndColor-m_particleStartColor)*(1.0f/particle->lifeReverse);
		}

		particle->doFlected = true;

	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值