開篇
網上有很多篇粒子系統(tǒng)源碼解析,但是只是簡單的接口羅列,沒有從最原理出發(fā)去講清楚粒子系統(tǒng)的來龍去脈,我將從粒子系統(tǒng)的本質去講清楚它的設計理念,當理解了它的理念以后,很多粒子遇到的問題就會迎刃解決了,這篇文章主講粒子的實現和一些框架級的優(yōu)化方式,其實有很多優(yōu)化細節(jié)就不贅述
粒子系統(tǒng)的設計思想
在早期游戲發(fā)展的時候,有一些粒子效果是實現一些鼠標特效的,比如《刀劍封魔錄》中滑動鼠標后,鼠標本身就會作為一個粒子發(fā)射器,在鼠標拖動后,會產生很多粒子并隨著時間消亡,這就是最早的粒子系統(tǒng)模型
?在早期的桌面系統(tǒng)中實現的粒子全是用cpu在屏幕上渲染的,如果需要世界中的3D粒子,則會將世界坐標轉換為屏幕坐標,在屏幕指定位置渲染個粒子,而且這個粒子是每一幀去更新的
我給一個imgui類型渲染系統(tǒng)的粒子偽代碼方便大家去理解,每一幀都去更新所有粒子的信息,當然現代游戲引擎粒子實現也類似于此
//定義粒子結構
struct particle
{
float x;
float y;
image img;
}
//定義粒子集合
vector<particle> particles
//設置粒子位置
void setparticlepositon(particle &p)
{
p.x++;
p.y++;
}
tick::update() //每一幀更行所有需要渲染的信息
{
for(auto i : particles)
{
setparticlepositon(i);//循環(huán)更新粒子位置
draw(i);//循環(huán)更新粒子繪制
}
}
更復雜的話就是定義每個粒子的周期,并定義粒子發(fā)射器,另外可以在setparticlepositon中設置更復雜的更新條件
class ParticleEmitter{
protected:
void Tick() {
Spawn(); //生成階段:創(chuàng)建新粒子
UpdateAndRecycle(); //更新階段:更新每個粒子的狀態(tài)數據并回收已死亡的粒子
Render(); //渲染階段:使用粒子渲染器將粒子效果渲染出來
}
private:
vector<Particle> Particles;
};
萬變不離其宗,都是源于這個思想
既然有大量相同的粒子,那么結合現代游戲引擎的特性,可以用到實例化(Instancing),將相同的mesh和材質用一套,大大減少了數據量,另外也可以用GPU粒子優(yōu)化
使用幾何著色器優(yōu)化
Nvidia在2006年的GeForce 8系列中出了DirectX 10.0,并推出了幾何著色器
簡單說,就是幾何著色器可以把單個頂點擴充成多個頂點,如果粒子的mesh都一樣的話,只需要從頂點著色器中傳入的單個頂點和擴展規(guī)則,就可以生成想渲染的粒子mesh,而并不需要每回傳入大量的頂點了
使用之前:
可見每個粒子需要傳入五個位置然后生成五角星?
使用之后:
?想渲染一個五角星只要傳入一個頂點,就可以用幾何著色器擴充為五角星,大大減少了draw所占的帶寬
使用SMID優(yōu)化
關于smid的介紹我前面有篇文章可以看看,這里就不贅述它的原理了
使用SIMD指令加速計算_星空_MAX的博客-CSDN博客
這種情況試用語cpu對大量粒子計算和更新的情況
unreal引擎的nigara和chaos就是用這種緩存友好的內存布局,和unity的ECS架構原理一樣
?寫個大概的數據結構參考一下:
//定義粒子的組件集合
vector<particle> particlesx;
vector<particle> particlesy;
//設置粒子x位置
void setparticlepositonx(particlex &px)
{
px++;
}
//設置粒子y位置
void setparticlepositonx(particley &py)
{
py++;
}
//定義粒子和組件集合
class particle
{
vector<particle> *particlesx = particlesx;
vector<particle> *particlesx = particlesy;
}
//定義粒子集合
vector<particle> particles;
tick::update() //每一幀更行所有需要渲染的信息
{
for(auto i : particlesx)
{
setparticlepositonx(i);//循環(huán)更新粒子位置
}
for(auto i : particlesy)
{
setparticlepositony(i);//循環(huán)更新粒子位置
}
for(auto i : particles)
{
draw(i);//循環(huán)更新粒子繪制
}
}
使用GPU粒子優(yōu)化
CPU粒子設計上和我上一節(jié)提到的東西大同小異,不過原來的draw是CPU去繪制,現代會把這個內容送到GPU,會調用一次drawcall,但是粒子的消耗消耗會非常大,粒子渲染的數量非常有限
這時候能不能把把粒子更新的邏輯放在gpu里面呢,當然可以了
同樣是DirectX 10.0引用了Stream Output的新特性,在OpenGL3.0中也加入了這種特性,不過在OpenGL稱為Transform Feedback,在頂點著色器或者幾何著色器之后,可以把頂點數據緩存到這個結構中,而這個結構在gpu中,這樣每回就可以根據這個信息去更新頂點信息,重新傳入頂點著色器中去做渲染
整體流程如下:
?這時候就會發(fā)現GPU和cpu可以不進行數據傳輸了,粒子完成自我更新,大大減少了傳輸量和cpu的計算負擔
帶來的問題
如果用gpu粒子的話無法實時計算出剔除范圍了,因為剔除步驟在傳入頂點著色器之前,所以一開始就需要傳入效果的bounds,并預估出粒子的最大范圍,否則可能出現特效消失的問題
文章來源:http://www.zghlxwxcb.cn/news/detail-532498.html
結尾
粒子系統(tǒng)還有其他很多部分,可做優(yōu)化的部分非常多,我這里只介紹了一些粒子系統(tǒng)設計的基礎思想,其他更全面的內容網上很多,就不贅述了文章來源地址http://www.zghlxwxcb.cn/news/detail-532498.html
到了這里,關于游戲引擎的cpu/gpu粒子系統(tǒng)設計思想的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!