Aetheria/Assets/Shaders/Compute/Stardust/Stardust.compute
2026-08-10 14:57:50 +01:00

147 lines
No EOL
4.3 KiB
Text

// Global keywords defining environment settings shared between shaders
// Would rather just #include_with_pragmas
// but it's broken in compute due to a bug that has been fixed but not backported yet
#pragma multi_compile __ FLOW_GLOBAL
#pragma multi_compile __ FLOW_SLOPE
#pragma multi_compile __ NOISE_SLOPE
//Tell unity that we have defined a kernel
#pragma kernel UpdateParticles
#include "Assets/Shaders/Volumetric.cginc"
//Store Particle data in a struct
struct Particle
{
float3 position;
float3 color;
float size;
};
RWStructuredBuffer<Particle> particles;
// Use this texture to choose the color of the particle
Texture2D HueTexture;
SamplerState samplerHueTexture;
float period;
float time;
float spacing;
float ceilingHeight;
float floorHeight;
float maximumSize;
float minimumSize;
float minHeadroom;
float maxHeadroom;
float heightExponent;
uint span;
//================
//Bonus: Random number generator
//Using xorshift, and thread ID as a seed. If you only use xorshift,
//there is too much correlation, so we first hash the seed id in RandomFirst()
//See: http://www.reedbeta.com/blog/2013/01/12/quick-and-easy-gpu-random-numbers-in-d3d11/
uint rngState;
#define UINT_MAXF 4294967290.0f
float RandomFirst(uint a, uint b)
{
//wang hash the seed first to get rid of correlation.
a = (a ^ 61) ^ (a >> 16);
a *= 9;
a = a ^ (a >> 4);
a *= 0x27d4eb2d;
a = a ^ (a >> 15);
b -= (b<<6);
b ^= (a>>17);
b -= (b<<9);
b ^= (a<<4);
b -= (b<<3);
b ^= (b<<10);
b ^= (b>>15);
rngState = a ^ b;
return (float)rngState / UINT_MAXF;
}
float RandomValue()
{
// Xorshift algorithm from George Marsaglia's paper
//You could use RandomFirst() all the time - it'd just be more expensive.
rngState ^= (rngState << 13);
rngState ^= (rngState >> 17);
rngState ^= (rngState << 5);
return (float) rngState / UINT_MAXF;
}
#define HASHSCALE 0.1031
float hash(float p)
{
float3 p3 = frac(p.xxx * HASHSCALE);
p3 += dot(p3, p3.yzx + 19.19);
return frac((p3.x + p3.y) * p3.z);
}
float fade(float t) { return t*t*t*(t*(6.*t-15.)+10.); }
float grad(float hash, float p)
{
int i = int(1e4*hash);
return (i & 1) == 0 ? p : -p;
}
float perlinNoise1D(float p)
{
float pi = floor(p), pf = p - pi, w = fade(pf);
return lerp(grad(hash(pi), pf), grad(hash(pi + 1.0), pf - 1.0), w) * 2.0;
}
const float4 irrationals = float4(1.41421, 3.14159, 2.71828, 1.61803);
float noise(float p)
{
return (sin(irrationals.x * p) + sin(irrationals.y * p) + sin(irrationals.z * p) + sin(irrationals.w * p))/4;
}
const uint offset = 65535;
const float randomRange = 2048;
//Since we only have a long list of particles, our thread group is just a flat block of threads in the x direction
//128 threads in a group is a good amount for a couple of reasons
[numthreads(128, 1, 1)]
void UpdateParticles(uint3 id : SV_DispatchThreadID, int3 groupID : SV_GroupThreadID)
{
int x = id % span - span / 2;
int y = id / span - span / 2;
int xt = (int)(_GridTransform.x / spacing);
int yt = (int)(_GridTransform.y / spacing);
uint xp = x + xt + offset;
uint yp = y + yt + offset;
const float lifetime = frac(time / period + RandomFirst(xp, yp));
particles[id.x].position = float3(
x * spacing + xt * spacing + perlinNoise1D(RandomValue()*1024)*spacing, 0,
y * spacing + yt * spacing + perlinNoise1D(RandomValue()*1024)*spacing);
const float2 puv = getUV(particles[id.x].position.xz);
const float gravHeight = -tex2Dlod(_NebulaSurfaceHeight, float4(puv, 0, 0)).x + floorHeight;
const float headroom = clamp(ceilingHeight - gravHeight, minHeadroom, maxHeadroom);
particles[id.x].position.y = gravHeight + headroom *
pow(RandomValue(), heightExponent) +
perlinNoise1D(RandomValue()*1024)*spacing/2;
const float3 flowVec = flow(particles[id.x].position);
const float3 lighting = tex2Dlod(_NebulaTint, float4(puv.x, puv.y, 0, _DynamicLodHigh)).rgb +
tex2Dlod(_NebulaTint, float4(puv.x, puv.y, 0, _DynamicLodLow)).rgb;
particles[id.x].position -= flowVec * lifetime * period;
//const float fade = smoothstep(0,ceilingHeight, particles[id.x].position.y);
//const float d = pow(density(particles[id.x].position) + .01, 0.1);
particles[id.x].size = lerp(minimumSize, maximumSize, saturate(RandomValue())) * parabola(lifetime, 2);
particles[id.x].color = HueTexture.SampleLevel(samplerHueTexture, float2(RandomValue(), 0.0f), 0.0f).rgb * lighting;
}