[MetalKit]Working with Particles in Metal part3粒子系統(tǒng)3

本系列文章是對 http://metalkit.org 上面MetalKit內(nèi)容的全面翻譯和學(xué)習(xí).

MetalKit系統(tǒng)文章目錄


上次我們關(guān)注的是如何操縱GPU上的Model I/O對象的頂點(diǎn).本文我們用另一種方式來通過計(jì)算線程來創(chuàng)建粒子.我們可以重用上次的playground,從修改metal視圖代理類的Particle結(jié)構(gòu)體開始,只需要包含兩個(gè)GPU更新用的成員就行了-positionvelocity:

struct Particle {
    var position: float2
    var velocity: float2
}

我們還可以刪除timer變量, 及translate(by:)update()方法了.改得最多的是initializeBuffers()方法:

func initializeBuffers() {
    for _ in 0 ..< particleCount {
        let particle = Particle(
                position: float2(Float(arc4random() %  UInt32(side)), 
                        Float(arc4random() % UInt32(side))), 
                velocity: float2((Float(arc4random() %  10) - 5) / 10, 
                        (Float(arc4random() %  10) - 5) / 10))
        particles.append(particle)
    }
    let size = particles.count * MemoryLayout<Particle>.size
    particleBuffer = device.makeBuffer(bytes: &particles, length: size, options: [])
}

注意:我們在整個(gè)窗口范圍內(nèi)生成隨機(jī)位置,并生成[-5,5]范圍內(nèi)的速度值.將其除以10以讓速度慢下來.

最重要的部分則是在配置指令編碼器時(shí).設(shè)置threads per group數(shù)量為2D網(wǎng)格,一邊為thread execution width,另一邊為maximum total threads per threadgroup,這兩個(gè)值是GPU的硬件特征值,且在執(zhí)行期間不會(huì)改變.設(shè)置threads per grid為一維數(shù)組,size由粒子數(shù)量決定:

let w = pipelineState.threadExecutionWidth
let h = pipelineState.maxTotalThreadsPerThreadgroup / w
let threadsPerGroup = MTLSizeMake(w, h, 1)
let threadsPerGrid = MTLSizeMake(particleCount, 1, 1)
commandEncoder.dispatchThreads(threadsPerGrid, threadsPerThreadgroup: threadsPerGroup)

注意:在新的Metal 2中,dispatchThreads(:)可以不指定線程組數(shù)而直接工作.與使用舊的dispatchThreadgroups(:)方法相比,新方法計(jì)算組數(shù),并當(dāng)網(wǎng)格尺寸不是組尺寸的倍數(shù)時(shí)提供nonuniform thread groups,并確保沒有未使用的線程.

回到內(nèi)核著色器中,首先匹配CPU中的粒子結(jié)構(gòu)體,然后在內(nèi)核中更新位置和速度:

Particle particle = particles[id];
float2 position = particle.position;
float2 velocity = particle.velocity;
int width = output.get_width();
int height = output.get_height();
if (position.x < 0 || position.x > width) { velocity.x *= -1; }
if (position.y < 0 || position.y > height) { velocity.y *= -1; }
position += velocity;
particle.position = position;
particle.velocity = velocity;
particles[id] = particle;
uint2 pos = uint2(position.x, position.y);
output.write(half4(1.), pos);
output.write(half4(1.), pos + uint2( 1, 0));
output.write(half4(1.), pos + uint2( 0, 1));
output.write(half4(1.), pos - uint2( 1, 0));
output.write(half4(1.), pos - uint2( 0, 1));

注意:我們做了邊界檢測,當(dāng)遇到邊界時(shí)將速度取反,使粒子不會(huì)離開屏幕.還有一個(gè)小技巧,通過渲染出相鄰的四個(gè)粒子來讓整個(gè)粒子看起來更大點(diǎn).

你可以設(shè)置particleCount1000000,但這樣會(huì)花費(fèi)好幾秒來渲染全部粒子.我只用了10000個(gè)粒子,這樣它們在屏幕上不會(huì)顯得太擠.運(yùn)行一下app,你會(huì)看到粒子隨機(jī)來回移動(dòng):

particles3.gif

至此,粒子渲染系統(tǒng)結(jié)束,感謝FlexMonkey分享對計(jì)算概念的見解,源代碼source code已發(fā)布在Github上.
下次見!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,045評論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,564評論 19 139
  • 本文圖片來自網(wǎng)絡(luò) 小時(shí)候,我是個(gè)淘氣丫頭。淘得多了,難免出糗。然而,那時(shí)候的我哪里懂得什么是“糗”,這才有幸在快...
    愛做美夢的懶貓閱讀 268評論 1 14
  • 5月2日消息,今日騰訊宣布任命語音識(shí)別技術(shù)頂級(jí)專家俞棟為AI Lab(人工智能實(shí)驗(yàn)室)副主任,并成立美國西雅...
    投資說閱讀 399評論 0 0
  • 我不想睡覺 害怕一睜眼又是新的一天毫無作為 昨天還沒待夠不想去明天 我不知道我要去哪里 我沒有方向 我不敢抬腳 我...
    OlinDar閱讀 314評論 0 0

友情鏈接更多精彩內(nèi)容