147 lines
No EOL
4.3 KiB
Text
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;
|
|
} |