[MetalKit]41-Working-with-Particles-in-Metal-part3粒子系統3

本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.c++

MetalKit系統文章目錄git


上次咱們關注的是如何操縱GPU上的Model I/O對象的頂點.本文咱們用另外一種方式來經過計算線程來建立粒子.咱們能夠重用上次的playground,從修改metal視圖代理類的Particle結構體開始,只須要包含兩個GPU更新用的成員就好了-positionvelocity:github

struct Particle {
    var position: float2
    var velocity: float2
}
複製代碼

咱們還能夠刪除timer變量, 及translate(by:)update() 方法了.改得最多的是initializeBuffers() 方法:swift

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: [])
}
複製代碼

注意:咱們在整個窗口範圍內生成隨機位置,並生成[-5,5]範圍內的速度值.將其除以10以讓速度慢下來.數組

最重要的部分則是在配置指令編碼器時.設置threads per group數量爲2D網格,一邊爲thread execution width,另外一邊爲maximum total threads per threadgroup,這兩個值是GPU的硬件特徵值,且在執行期間不會改變.設置threads per grid爲一維數組,size由粒子數量決定:app

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(:) 能夠不指定線程組數而直接工做.與使用舊的dispatchThreadgroups(:) 方法相比,新方法計算組數,並當網格尺寸不是組尺寸的倍數時提供nonuniform thread groups,並確保沒有未使用的線程.dom

回到內核着色器中,首先匹配CPU中的粒子結構體,而後在內核中更新位置和速度:ide

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));
複製代碼

注意:咱們作了邊界檢測,當遇到邊界時將速度取反,使粒子不會離開屏幕.還有一個小技巧,經過渲染出相鄰的四個粒子來讓整個粒子看起來更大點.post

你能夠設置particleCount1000000,但這樣會花費好幾秒來渲染所有粒子.我只用了10000個粒子,這樣它們在屏幕上不會顯得太擠.運行一下app,你會看到粒子隨機來回移動: 學習

particles3.gif

至此,粒子渲染系統結束,感謝FlexMonkey分享對計算概念的看法,源代碼source code已發佈在Github上.

下次見!

相關文章
相關標籤/搜索