Vulkan_基於CPU的粒子系統(火焰、煙霧)

粒子系統

本部分主要是實現一個簡單的基於CPU的粒子系統。粒子數據存儲在主機內存中,每幀在CPU上更新,並在使用預乘alpha渲染之前與設備同步。

一個微粒,從vulkan的角度看就是一個總是面向攝像機方向且(通常)包含一個大部分區域是透明的紋理的小四邊形。一個微粒本身主要就是一個sprite,但是當你把成千上萬個這些微粒放在一起的時候,就可以創造出令人瘋狂的效果。

當處理這些微粒的時候,通常是由一個叫做粒子發射器或粒子生成器的東西完成的,從這個地方,持續不斷的產生新的微粒並且舊的微粒隨着時間逐漸消亡。如果這個粒子發射器產生一個帶着類似煙霧紋理的微粒的時候,它的顏色亮度同時又隨着與發射器距離的增加而變暗,那麼就會產生出灼熱的火焰的效果:
在這裏插入圖片描述
一個單一的微粒通常有一個生命值變量,並且從它產生開始就一直在緩慢的減少.一旦它的生命值少於某個極限值(通常是0)我們就會殺掉這個粒子,這樣下一個粒子產生時就可以讓它來替換那個被殺掉的粒子.一個粒子發射器控制它產生的所有粒子並且根據它們的屬性來改變它們的行爲.一個粒子通常有下面的屬性:

看上面那個火焰的例子,那個粒子發射器可能在靠近發射器的地方產生每一個粒子,並且有一個向上的速度,這樣每個粒子都是朝着正yy軸方向移動.那似乎有3個不同區域,只是可能相比其他的區域,給了某個區域內的粒子更快的速度.我們也可以看到,yy軸方向越高的粒子,它們的黃色或者說亮度就越低.一旦某個粒子到達某個高度的時候,它的生命值就會耗盡然後被殺掉;絕不可能直衝雲霄.

你可以想象到用這樣一個系統,我們就可以創造一些有趣的效果比如火焰,青煙,煙霧,魔法效果,炮火殘渣等等.我們將會創建一個簡單的粒子生成器來製作一些有趣的效果,結果看起來就像上面火焰或者下面煙霧這樣:在這裏插入圖片描述
上面那個粒子生成器在這個球的位置產生無數的粒子,根據球移動的速度給了粒子相應的速度,並且根據它們的生命值來改變他們的顏色亮度.

爲了渲染這些粒子,我們將會用到有不同實現的着色器:

頂點着色器:

#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout (location = 0) in vec4 inPos;
layout (location = 1) in vec4 inColor;
layout (location = 2) in float inAlpha;
layout (location = 3) in float inSize;
layout (location = 4) in float inRotation;
layout (location = 5) in int inType;

layout (location = 0) out vec4 outColor;
layout (location = 1) out float outAlpha;
layout (location = 2) out flat int outType;
layout (location = 3) out float outRotation;

layout (binding = 0) uniform UBO 
{
	mat4 projection;
	mat4 modelview;
	vec2 viewportDim;
	float pointSize;
} ubo;

out gl_PerVertex
{
	vec4 gl_Position;
	float gl_PointSize;
};

void main () 
{
	outColor = inColor;
	outAlpha = inAlpha;
	outType = inType;
	outRotation = inRotation;
	  
	gl_Position = ubo.projection * ubo.modelview * vec4(inPos.xyz, 1.0);	
	
	// 粒子的基本大小
	float spriteSize = 8.0 * inSize;

	// 尺寸顆粒大小取決於相機的投影
	vec4 eyePos = ubo.modelview * vec4(inPos.xyz, 1.0);
	vec4 projectedCorner = ubo.projection * vec4(0.5 * spriteSize, 0.5 * spriteSize, eyePos.z, eyePos.w);
	gl_PointSize = ubo.viewportDim.x * projectedCorner.x / projectedCorner.w;
	
}

以及像素着色器:

#version 450

#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable

layout (binding = 1) uniform sampler2D samplerSmoke;
layout (binding = 2) uniform sampler2D samplerFire;

layout (location = 0) in vec4 inColor;
layout (location = 1) in float inAlpha;
layout (location = 2) in flat int inType;
layout (location = 3) in float inRotation;


layout (location = 0) out vec4 outFragColor;

void main () 
{
	vec4 color;
	float alpha = (inAlpha <= 1.0) ? inAlpha : 2.0 - inAlpha;
	
	// 旋轉紋理座標
	// 旋轉UV	
	float rotCenter = 0.5;
	float rotCos = cos(inRotation);
	float rotSin = sin(inRotation);
	vec2 rotUV = vec2(
		rotCos * (gl_PointCoord.x - rotCenter) + rotSin * (gl_PointCoord.y - rotCenter) + rotCenter,
		rotCos * (gl_PointCoord.y - rotCenter) - rotSin * (gl_PointCoord.x - rotCenter) + rotCenter);

	if (inType == 0) 
	{
		// 火焰
		color = texture(samplerFire, rotUV);
		outFragColor.a = 0.0;
	}
	else
	{
		// 煙霧
		color = texture(samplerSmoke, rotUV);
		outFragColor.a = color.a * alpha;
	}
	
	outFragColor.rgb = color.rgb * inColor.rgb * alpha;	
}

以上是初步實現的效果,由於main代碼還未完全理解消化,故下一部分着重來描述下粒子數據的生成過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章