高级Niagara示例,如Nearest Neighbor queries、simulation stages、iterative constraints、 mesh reproduction、exporting particle data to blueprint 等等;
Simulation Stage Fill Render Target
在GPU发射器添加Simulation Stages,当前不支持CPU;可将Simulation Stages视为类似”Patricle Update“的额外堆栈(可每帧计算),不同之处在于它可迭代,可在一帧上对其中的所有元素运行多次,可想象成堆栈中的“For循环”;
"Iteration source"不仅仅是粒子,可迭代Grid 2d collection内的每个单元以创建解算器,迭代render target每个像素以写入,或迭代每个粒子;

此案例中没有创建粒子,渲染器的source mode设置为emitter:
- Emitter Spawn初始化Data Interface类型的变量Texture Sample、Render Target 2D;
- 在simulation stage,将iteration source设置为Render Target 2D,即迭代其中的每个像素;
- Render Target 2D索引(即每个纹理像素)除以render tatget size以创建UV,使用该UV采样纹理,在直接填充Render Target;
- “Exec Index”指的是迭代源元素执行时的序号,即render target cell, grid 2d cell等;
- Sprite Render的Source Mode为Emitter以创建单个Sprite,添加material parameter/attribute bindings;



Advect Grid 2D Collection
Grids的性能完全受限于其分辨率和底层数据的精度;几乎所有Grids都可以采用半浮点精度,几乎不需要使用全浮点精度;
- Grid2d Collection是一个可保存浮点属性的2d缓冲区数组,可理解为不会移动的矩阵排列粒子(一个粒子对应一个单元格);需设置单元格数量;

首先,在模拟阶段用texture填充Grid 2D Collection,其iteration source是Grid 2D Collection,即希望遍历每个网格单元并执行某些操作;
- 写入StackContext.RGBA,会自动在 Iteration Source(Grid 2D Collection)上创建Vector4属性,并可在之后读写;
- 命名空间StackContext会自动转换为目标空间;
![]()

其后,在对Grid 2D Collection的属性RGBA进行扰动,以使纹理显现动画;


最后,采样Grid 2D Collection,并写入render target;




Communicate with External Render Targets
![]()
使用用户参数"TextureRenderTargetObject",来读取外部的Render Target;此时会使用用户提供的Render Target,而不再是创建内部的Render Target;Simulation Stage计算的结果也会写回外部的Render Target;此参数可通过以下方式提供:
- 一个磁盘Render Target 2D;
- 使用"Set Niagara Variable - Texture Render Target"蓝图;
采样纹理并灰度化处理,最后存于Grid2D Collection属性RGBA;
- 灰度化即RGB数值相等,且数值不超过1;
![]()

模糊Grid2D Collection属性RGBA;


注,白色边框是在材质里添加的,不是Simulation Stage内计算的;
Sample GBuffer Attributes
GBuffer可理解为当前相机内不透明物体的纹理集合;
在Skeletal Mesh表面采样位置生成粒子,然后采样GBuffer属性(如Base Color、Depth、Roughness、Metallic等)应用于粒子;
- 将粒子在世界上的位置,转化为在屏幕上的UV;



Skeletal Mesh Reproduction
首先在系统节点的生成阶段,创建Particle Attribute Reader类型的属性;
- Particle Attribute Reader可读取自身或其他发射器中的粒子属性;

创建引导粒子,用于对Mesh Reproduction粒子施加影响;

创建Mesh Reproduction粒子;
- 通过Mesh Reproduction粒子与引导粒子的距离,来施加影响;



注,如隐藏skeletal Mesh仍需保留动画,设置Always Tick Pose and Refresh Bones;

Distance Field Traversal
让昆虫附着在物体表面:
- Move to Nearest Distance Field Surface GPU,将空间中的昆虫移动的物体表面;
- Initial Mesh Orientation,旋转对齐方向(使用Vector to Nearest Surface Normal);
让昆虫沿着物体表面移动:
- 让移动的昆虫始终附着在表面,且更正速度方向;
- 更正昆虫旋转方向;


让昆虫躲避光线
- Ray Trace Distance Field GPU,通过矢量(粒子与光源)与物体是否交叉来判断是否在光源内,需关闭Libary Only;
- Avoid Cone,用于使粒子偏离聚光灯或其他锥体;


Particle Attribute Reader
Data Interface类型Particle Attribute Reader,识别要读取的粒子时,有两种选择:Index和ID;
- Index是指定发射器粒子中的索引(范围从0到NumParticles-1);但粒子消亡时,Index可能会发生变化,因此是不稳定的;
- ID是保持不变,且在系统中的所有发射器中都是唯一的,但在不同的发射器之间可能很难识别;leader粒子需开启ID,在follow粒子使用Get ID at Spawn Index将ID缓存起来;



Follow The Leader 1.0
Leaders发射器粒子围绕原点随机运动,使用Pendulum Constraint保持离原点距离不变;
Followers发射器粒子跟随Leaders粒子运动且相对位置不变(使用速度);
- 在粒子生成时,采样并保存Leaders粒子的ID,以在粒子更新阶段仍采样该粒子;使用Random Vector动态输入,生成并保存该随机单位矢量;



Spawn Particles From Another Emitter
Spawn Particles From Other Emitter用于查询其他发射器并设置发射率,Sample Particles From Other Emitter选择并应用其他发射器的属性,两模块是成对出现的;

IterativeConstraints
Particles Spawn
- 将生成的粒子位置依次排列为竖线,利用ExecutionCount、ExecutionIndex;
- 记录下每个粒子离原点的距离ChainGoalDistanceFromOrigin,前粒子ID号ChainLinkAhead,后粒子ID号ChainLinkAhead;
- 调整每个粒子的方向,以形成锁链形态;
Particles Supdate
- 对粒子施加各种力,如Gravity、Curl Noise Force、Wind Force、Drag等;
- 在Solve Forces and Velocity后,将每个粒子距离约束到开始时的距离;此时粒子还不在一条线上且方向也不正确;
Simulation Stage
- 通过多次迭代将粒子约束在一条线上且方向正确;


Color Copy by Cell
Neighbor Grid 3D本质上是一种基于位置的哈希查找结构,将空间划分为多个单元格Cell,每个单元格Cell负责管理其内部粒子;
- 空间划分 (Initialize),将空间划分为指定数量的单元格,每个单元格有唯一索引;
- Initialize Neighbor Grid 模块;
- 网格填充 (Fill),根据粒子位置,计算出其所属单元格的索引,并将该粒子的Execution Index存储单元格;
- 快速查询 (Query),当一个粒子需要查找其邻近粒子时(如检测碰撞),只需查询自己所在的网格单元及其周围相邻的 26 个单元,极大缩小了搜索范围;
利用数据接口Neighbor Grid 3D,首先存储粒子本身的索引号到所属的单元格,一个单元格可指定多个邻居数;
AddedToGrid = false;
#if GPU_SIMULATION
//根据粒子的世界位置,计算出所属单元格索引
float3 UnitPos;
NeighborGrid.SimulationToUnit(Position, SimulationToUnit, UnitPos);
int3 Index;
NeighborGrid.UnitToIndex(UnitPos, Index.x,Index.y,Index.z);
//验证单元格索引是否有效
int3 NumCells;
NeighborGrid.GetNumCells(NumCells.x, NumCells.y, NumCells.z);
if (Index.x >= 0 && Index.x < NumCells.x &&
Index.y >= 0 && Index.y < NumCells.y &&
Index.z >= 0 && Index.z < NumCells.z)
{
int LinearIndex;
NeighborGrid.IndexToLinear(Index.x, Index.y, Index.z, LinearIndex);
int PreviousNeighborCount;
NeighborGrid.SetParticleNeighborCount(LinearIndex, 1, PreviousNeighborCount);
int MaxNeighborsPerCell;
NeighborGrid.MaxNeighborsPerCell(MaxNeighborsPerCell);
if (PreviousNeighborCount < MaxNeighborsPerCell)
{
AddedToGrid = true;
int NeighborGridLinear;
NeighborGrid.NeighborGridIndexToLinear(Index.x, Index.y, Index.z, PreviousNeighborCount, NeighborGridLinear);
int IGNORE;
NeighborGrid.SetParticleNeighbor(NeighborGridLinear, ExecIndex, IGNORE);
}
}
#endif
在利用数据接口Neighbor Grid 3D,计算单元格内最近的存储粒子索引号,在通过该索引号获取所需数据如颜色;
NeighborIndex = -1;
#if GPU_SIMULATION
bool Valid;
float neighbordist = 3.4e+38;
//根据粒子的世界位置,计算出所属单元格索引
float3 UnitPos;
NeighborGrid.SimulationToUnit(Position, SimulationToUnit, UnitPos);
int3 Index;
NeighborGrid.UnitToIndex(UnitPos, Index.x,Index.y,Index.z);
//循环单元格内的所有邻居(刚才存储到单元格的粒子索引号),以找到最近的邻居粒子
int MaxNeighborsPerCell;
NeighborGrid.MaxNeighborsPerCell(MaxNeighborsPerCell);
for (int i = 0; i < MaxNeighborsPerCell; ++i)
{
int NeighborLinearIndex;
NeighborGrid.NeighborGridIndexToLinear(Index.x, Index.y, Index.z, i, NeighborLinearIndex);
int CurrNeighborIdx;
NeighborGrid.GetParticleNeighbor(NeighborLinearIndex, CurrNeighborIdx);
if (CurrNeighborIdx != -1)
{
float3 NeighborPos;
AttributeReader.GetPositionByIndex<Attribute="Position">(CurrNeighborIdx, Valid, NeighborPos);
const float3 delta = Position - NeighborPos;
const float dist = length(delta);
if( dist < neighbordist )
{
neighbordist = dist;
NeighborIndex = CurrNeighborIdx;
}
}
}
#endif

Dynamic Grid Transform
Initialize Neighbor Grid 创建一网格并可轻松定义其变换;
Visualize Grid Cells
- 首先利用Spawn Particles in Grid、Grid Location创建一个单位空间的粒子矩阵(中心点在原点),每个点可看成是单元格的中心,所以需偏移达到正确的单位空间位置;
- 在通过GridUnitToWorld将单位空间变换到世界空间;
- 最后还要设置方向UnitToWorldRotationAsQuat;

Max Neighbors Per Cell




2240

被折叠的 条评论
为什么被折叠?



