ComputeShder小例子之粒子喷泉

这是一个 ComputeShader 结合 Shader 实现粒子喷泉的例子。
通过 ComputeShader 计算喷泉粒子的位置,并通过 Shader 将其绘制出来。
效果
原理
生成指定数量具有随机速度的点数据,ComputeShader 中将其初速度与重力加速度叠加,并根据存活时间粗略计算其位置,之后使用 Shader 将其绘制出来。
代码
1.ComputeShader
#pragma kernel PenQuan
struct Particle
{
float3 position;
float3 velocity;//梓喵出没博客(azimiao.com)
float3 startingVelocity;
float lifetime;
};
RWStructuredBuffer<Particle> particleBuffer;
float deltaTime;
[numthreads(256, 1, 1)]
void PenQuan(uint3 id : SV_DispatchThreadID)
{
//粗略计算其位移
particleBuffer[id.x].position += particleBuffer[id.x].velocity * deltaTime;
particleBuffer[id.x].velocity += float3(0,-10,0) * deltaTime;
particleBuffer[id.x].lifetime += deltaTime;//梓喵出没博客(azimiao.com)
if (particleBuffer[id.x].lifetime >= 5) {
particleBuffer[id.x].lifetime = 0;
particleBuffer[id.x].position.x = 0;
particleBuffer[id.x].position.y = 0;
particleBuffer[id.x].position.z = 0;
particleBuffer[id.x].velocity = particleBuffer[id.x].startingVelocity;
}
}
2.Shader
Shader "PenQuan"
{
Properties
{
_Tint ("Tint", Color) = (0, 0, 0.5, 0.3)
_Randomness ("Random", Range(0.0, 1.0)) = 0.5
}
SubShader
{
Pass
{
Blend SrcAlpha one
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct Particle
{
float3 position;
float3 velocity;
float3 startingVelocity;
float lifetime;
};
// Pixel shader input
struct PS_INPUT
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
//共享 buffer
StructuredBuffer<Particle> particleBuffer;
// Properties variables
uniform float4 _Tint;
uniform float _Randomness;
// Vertex shader
PS_INPUT vert(uint vertex_id : SV_VertexID, uint instance_id : SV_InstanceID)
{
PS_INPUT o = (PS_INPUT)0;
// Color
float speed = length(particleBuffer[instance_id].velocity);
//根据当前速度修改颜色
float4 c = _Tint;
c.r *= 1.0 - _Randomness;
c.g *= 1.0 - _Randomness;
c.b *= 1.0 - _Randomness;
c.r += fmod(abs(particleBuffer[instance_id].velocity.x), _Randomness);
c.g += fmod(abs(particleBuffer[instance_id].velocity.y), _Randomness);
c.b += fmod(abs(particleBuffer[instance_id].velocity.z), _Randomness);
c.a = 1;
o.color = c;
// Position
o.position = UnityObjectToClipPos(float4(particleBuffer[instance_id].position, 1.0f));
return o;
}
// Pixel shader
float4 frag(PS_INPUT i) : COLOR
{
return i.color;
}
ENDCG
}
}
Fallback Off
}
3.CSharp
public class PenQuanTest : MonoBehaviour {
private struct Particle
{
public Vector3 position;
public Vector3 velocity;
public Vector3 startingVelocity;
public float lifetime;
}
public int particleCount = 10000;
public Material material;
public ComputeShader computeShader;
//结构体大小,用于初始化 buffer 时使用
private const int SIZE_PARTICLE = 40;
private int mComputeShaderKernelID;
//保持引用
ComputeBuffer particleBuffer;
private const int WARP_SIZE = 256;
private int mWarpCount;
private Particle[] particleArray;
void Start()
{
if (particleCount <= 0)
particleCount = 1;
mWarpCount = Mathf.CeilToInt((float)particleCount / WARP_SIZE);
particleArray = new Particle[particleCount];
//初始化粒子位置与随机速度信息
for (int i = 0; i < particleCount; ++i)
{
particleArray[i].position.x = 0;
particleArray[i].position.y = 0;
particleArray[i].position.z =0;
Vector2 v = Random.insideUnitCircle * 1.5f;
particleArray[i].velocity.x = v.x;
particleArray[i].velocity.y = 10f;
particleArray[i].velocity.z = v.y;
particleArray[i].startingVelocity = particleArray[i].velocity;
particleArray[i].lifetime = Random.Range(-5f, 5f);
}
particleBuffer = new ComputeBuffer(particleCount, SIZE_PARTICLE);
particleBuffer.SetData(particleArray);
mComputeShaderKernelID = computeShader.FindKernel("PenQuan");
computeShader.SetBuffer(mComputeShaderKernelID, "particleBuffer", particleBuffer);
material.SetBuffer("particleBuffer", particleBuffer);
}
void OnDestroy()
{
if (particleBuffer != null)
particleBuffer.Release();
}
// Update is called once per frame
void Update()
{
computeShader.SetFloat("deltaTime", Time.deltaTime);
computeShader.Dispatch(mComputeShaderKernelID, mWarpCount, 1, 1);
}
void OnRenderObject()
{
material.SetPass(0);
Graphics.DrawProceduralNow(MeshTopology.Points, 1, particleCount);
}
}
引用资料
本文在如下代码上进行了修改,以实现正常运行与效果优化。
- [代码资料] 【Github】 lukakostic ComputeShaderExamples