977 lines
24 KiB
HLSL
977 lines
24 KiB
HLSL
#define ParticleType_Dead 0
|
|
#define ParticleType_Emitter 1
|
|
#define ParticleType_Point 2
|
|
#define ParticleType_Star 3
|
|
|
|
#define SDF_SIMILARITY 2.0 // try 0.2 for fractal flames, you can also go up to 64
|
|
#define TANGENT_SPEED 8.0 // increase to add more chaos
|
|
#define GRADIENT_SPEED 8.0
|
|
|
|
RWTexture2D<float4> out0:register(u0);
|
|
SamplerState sampler0 : register(s0);
|
|
Texture2D tex0: register(t0);
|
|
Texture2D tex1: register(t1);
|
|
Texture2D tex2: register(t2);
|
|
Texture2D tex3: register(t3);
|
|
Texture2D tex4: register(t4);
|
|
Texture2D tex5: register(t5);
|
|
Texture2D tex6: register(t6);
|
|
Texture2D tex7: register(t7);
|
|
|
|
struct ParticleState
|
|
{
|
|
int type;
|
|
float3 oldPosition;
|
|
float3 currentPosition;
|
|
float3 direction;
|
|
float3 tangent;
|
|
float creationTime;
|
|
float mass;
|
|
float lifeTime;
|
|
};//64 bytes
|
|
|
|
cbuffer _0 : register(b0)
|
|
{
|
|
float4 smoothNoiseColor; // USED // additive to particle state
|
|
float4 colorBlend; // USED
|
|
float3 smoothNoiseDirection; // USED
|
|
float demoTime; // USED
|
|
float3 emitterDirection; // USED
|
|
float deltaTime; // USED
|
|
float3 emitterPosition; // USED
|
|
float emitterRadius; // USED
|
|
float3 cameraPosition; // USED
|
|
float emitRate; // USED// = 0.00001f; //delay in seconds between each particle spawn
|
|
float3 cameraLookat; // USED
|
|
float lifeTime; // USED// = 3.0f; //particle lifetime in seconds
|
|
float3 cameraUp; // USED
|
|
float gravity; // USED
|
|
float3 emitterVelocity; // USED
|
|
float emitChaos; // USED
|
|
float emitterMass; // USED
|
|
float sdfBlendFactor; // USED
|
|
float smoothNoiseSize; // additive to particle state
|
|
float particleMass; // USED // additive to particle state
|
|
float envelope1; // USED
|
|
float envelope2;
|
|
float distort;
|
|
float note; // USED
|
|
}; // 44 floats = 176 bytes
|
|
|
|
struct __1
|
|
{
|
|
float4 _d : _dS;
|
|
float4 _c : _cS;
|
|
float4x4 _v : _vS;
|
|
float4x4 _p : _pS;
|
|
};
|
|
|
|
cbuffer _1 : register(b1)
|
|
{
|
|
__1 _o;
|
|
}; // 40 floats = 160 bytes
|
|
|
|
cbuffer _2 : register(b2)
|
|
{
|
|
__1 _c;
|
|
}; // 40 floats = 160 bytes
|
|
|
|
float4x4 lookTo(float3 Eye, float3 zaxis, float3 Up)
|
|
{
|
|
float3 xaxis = normalize(cross(Up, zaxis));
|
|
float3 yaxis = cross(zaxis, xaxis);
|
|
|
|
float4x4 result = {
|
|
xaxis, -dot(xaxis, Eye),
|
|
yaxis, -dot(yaxis, Eye),
|
|
zaxis, -dot(zaxis, Eye),
|
|
0, 0, 0, 1
|
|
};
|
|
|
|
return transpose(result);
|
|
}
|
|
|
|
// Constant buffer vertex shader
|
|
__1 VSCB()
|
|
{
|
|
__1 o;
|
|
|
|
float3 direction = normalize(cameraLookat - cameraPosition);
|
|
o._v = lookTo(cameraPosition, direction, normalize(cameraUp));
|
|
o._d = float4(direction, 0);
|
|
o._c = float4(cameraPosition, 0);
|
|
o._p = float4x4( // perspective matrix
|
|
1, 0, 0, 0,
|
|
0, 1, 0, 0,
|
|
0, 0, 1, 1,
|
|
0, 0, -1, 0
|
|
);
|
|
|
|
return o;
|
|
}
|
|
|
|
#define PI 3.14159265
|
|
|
|
static uint rndSeed = 0;
|
|
static const float starThreshold = 20.0f;
|
|
|
|
uint hash(uint x)
|
|
{
|
|
x += x << 10, x ^= x >> 6, x += x << 3, x ^= x >> 11, x += x << 15; return x;
|
|
}
|
|
|
|
void setRndSeed(uint seed)
|
|
{
|
|
rndSeed = seed;
|
|
}
|
|
|
|
float rnd()
|
|
{
|
|
return asfloat((rndSeed = hash(rndSeed) & 0x007fffff) | 0x3f800000) - 1;
|
|
}
|
|
|
|
float srnd()
|
|
{
|
|
return asfloat((rndSeed = hash(rndSeed) & 0x007fffff) | 0x40000000) - 3;
|
|
}
|
|
|
|
float rnd(uint s)
|
|
{
|
|
return asfloat((s & 0x007fffff) | 0x3f800000) - 1;
|
|
}
|
|
|
|
float srnd(uint s)
|
|
{
|
|
return asfloat((s & 0x007fffff) | 0x40000000) - 3;
|
|
}
|
|
|
|
static const float2x2 mtx = float2x2(0.80, 0.60, -0.60, 0.80);
|
|
|
|
float noise(float2 p)
|
|
{
|
|
uint idx = (uint)(floor(p.y) * 1024 + floor(p.x));
|
|
return rnd(hash(idx));
|
|
}
|
|
|
|
float fbm4(float2 p)
|
|
{
|
|
float f = 0.0;
|
|
|
|
f += 0.5000*(noise(p)); p = mul(p*2.02, mtx);
|
|
f += 0.2500*(noise(p)); p = mul(p*2.03, mtx);
|
|
f += 0.1250*(noise(p)); p = mul(p*2.01, mtx);
|
|
f += 0.0625*(noise(p));
|
|
|
|
return f / 0.9375;
|
|
}
|
|
|
|
[numthreads(16, 16, 1)]
|
|
void csNoise(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 v = float2(id.x, id.y);
|
|
out0[id.xy] = float4(noise(v), 0, 0, 1);
|
|
}
|
|
|
|
[numthreads(16, 16, 1)]
|
|
void csFbm(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 v = float2(id.x, id.y) / 1024.0;
|
|
float f = 0.0;
|
|
|
|
float scale = 0.5;
|
|
float sum = 0.0;
|
|
float mag = 0.25;
|
|
for (int i = 6; i >= 0; --i)
|
|
{
|
|
f += scale * tex0.SampleLevel(sampler0, v, i).r;
|
|
sum += scale;
|
|
v = mul(v * (1.0 + 0.01 * i), mtx);
|
|
scale *= 0.5;
|
|
mag *= 2.0;
|
|
}
|
|
|
|
out0[id.xy] = float4(mag * (f / sum - 0.5) + 0.5, 0, 0, 1);
|
|
}
|
|
|
|
ConsumeStructuredBuffer<ParticleState> particles:register(u0);
|
|
AppendStructuredBuffer<ParticleState> writeParticles:register(u1);
|
|
StructuredBuffer<ParticleState> readParticles:register(t0);
|
|
|
|
void emit(int type, float3 oldPosition, float3 position, float3 direction, float mass, float time)
|
|
{
|
|
ParticleState emittedParticle;
|
|
emittedParticle.type = type;
|
|
emittedParticle.oldPosition = oldPosition;
|
|
emittedParticle.currentPosition = position;
|
|
emittedParticle.creationTime = time;
|
|
emittedParticle.direction = direction;
|
|
emittedParticle.mass = mass;
|
|
emittedParticle.lifeTime = lifeTime;
|
|
emittedParticle.tangent = normalize(float3(srnd(), srnd(), srnd()));
|
|
writeParticles.Append(emittedParticle); // emit new particle
|
|
}
|
|
|
|
void recycle(ParticleState particle)
|
|
{
|
|
ParticleState newParticle;
|
|
newParticle.type = particle.type;
|
|
newParticle.oldPosition = particle.currentPosition;
|
|
newParticle.currentPosition = particle.currentPosition + particle.direction * abs(deltaTime);
|
|
newParticle.creationTime = particle.creationTime;
|
|
newParticle.direction = particle.direction + float3(0, gravity * particle.mass, 0) * abs(deltaTime);
|
|
newParticle.mass = particle.mass;
|
|
newParticle.lifeTime = particle.lifeTime;
|
|
newParticle.tangent = particle.tangent;
|
|
writeParticles.Append(newParticle);
|
|
}
|
|
|
|
void emitStar()
|
|
{
|
|
float3 pos = cameraPosition + sqrt(rnd()) * starThreshold * normalize(float3(srnd(), srnd(), srnd()));
|
|
emit(ParticleType_Star, pos, pos, float3(0.1, 0.0, 0.1), 0.0f, 0.0f);
|
|
}
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void starInit(int3 id:SV_DispatchThreadID)
|
|
{
|
|
rndSeed = id.y * 256 + id.x;
|
|
emitStar();
|
|
}
|
|
|
|
float sdSphere(float3 p)
|
|
{
|
|
p /= 5;
|
|
return length(p) - 1.0;
|
|
}
|
|
|
|
float sdIso1(float3 p)
|
|
{
|
|
p /= 5;
|
|
float A = (p.x * p.x + p.y * p.y + p.z * p.z - 1);
|
|
float B = (p.z - 1);
|
|
float C = (p.z + 1);
|
|
return A * A - (B * B - 2 * p.x * p.x)*(C * C - 2 * p.y * p.y);
|
|
}
|
|
|
|
float sdIso2(float3 p)
|
|
{
|
|
return cos(p.x) + cos(p.y) + cos(p.z);
|
|
}
|
|
|
|
float sdIso3(float3 p)
|
|
{
|
|
return
|
|
sin(p.x) * sin(p.y) * sin(p.z) +
|
|
sin(p.x) * cos(p.y) * cos(p.z) +
|
|
cos(p.x) * sin(p.y) * cos(p.z) +
|
|
cos(p.x) * cos(p.y) * sin(p.z);
|
|
}
|
|
|
|
float sdBox(float3 p, float3 b)
|
|
{
|
|
float3 d = abs(p) - b;
|
|
return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));
|
|
}
|
|
|
|
float mengerSponge(float3 p)
|
|
{
|
|
p /= 3;
|
|
float d = sdBox(p, float3(1.0, 1.0, 1.0));
|
|
|
|
float s = 1.0;
|
|
for (int m = 0; m<3; m++)
|
|
{
|
|
float3 a = sign(p) * fmod(p*s, 2.0) - 1.0;
|
|
s *= 3.0;
|
|
float3 r = abs(1.0 - 3.0*abs(a));
|
|
|
|
float da = max(r.x, r.y);
|
|
float db = max(r.y, r.z);
|
|
float dc = max(r.z, r.x);
|
|
float c = (min(da, min(db, dc)) - 1.0) / s;
|
|
|
|
d = max(d, c);
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
float f(float3 p)
|
|
{
|
|
float sdfType = floor(sdfBlendFactor);
|
|
float sphere = step(0, sdfType) * (1 - step(1, sdfType));
|
|
float iso1 = step(1, sdfType) * (1 - step(2, sdfType));
|
|
float iso2 = step(2, sdfType) * (1 - step(3, sdfType));
|
|
float iso3 = step(3, sdfType) * (1 - step(4, sdfType));
|
|
float menger = step(4, sdfType) * (1 - step(5, sdfType));
|
|
return sdSphere(p) * sphere + sdIso1(p) * iso1 + sdIso2(p) * iso2 + sdIso3(p) * iso3 + mengerSponge(p) * menger;
|
|
}
|
|
|
|
void doEmitter(float3 particlePosition, float3 particleDirection, float t)
|
|
{
|
|
if (demoTime - t >= emitRate)
|
|
{
|
|
float count = ceil((demoTime - t) / emitRate);
|
|
float index = count - 1;
|
|
while (t <= demoTime)
|
|
{
|
|
float3 tangent = normalize(float3(srnd(), srnd(), srnd()));
|
|
if (length(emitterDirection) > 0.0f)
|
|
tangent = cross(normalize(emitterDirection), tangent);
|
|
float3 emitDirection = emitterDirection + emitChaos * normalize(float3(srnd(), srnd(), srnd()));
|
|
float3 newPosition = particlePosition + tangent * sqrt(rnd()) * emitterRadius;
|
|
emit(ParticleType_Point, newPosition, newPosition + emitDirection * deltaTime * (index-- / count), emitDirection, particleMass, t);
|
|
particlePosition += particleDirection * emitRate;
|
|
t += emitRate;
|
|
}
|
|
}
|
|
}
|
|
|
|
void doUpdate(int3 id:SV_DispatchThreadID, bool bPreserveEmitters)
|
|
{
|
|
rndSeed = (id.y * 1024 + id.x) * demoTime;
|
|
ParticleState particle = particles.Consume();
|
|
if (particle.type == ParticleType_Emitter)
|
|
{
|
|
float t = particle.creationTime;
|
|
doEmitter(particle.currentPosition, particle.direction, t);
|
|
|
|
if (bPreserveEmitters)
|
|
{
|
|
emit(ParticleType_Emitter, particle.currentPosition, particle.currentPosition + particle.direction * abs(deltaTime), particle.direction, particle.mass, demoTime);
|
|
}
|
|
}
|
|
else if (particle.type == ParticleType_Point)
|
|
{
|
|
float age = (demoTime - particle.creationTime) / particle.lifeTime;
|
|
uint index;
|
|
|
|
float2 e = float2(0.001, 0);
|
|
float3 p = particle.currentPosition;
|
|
float d = f(p);
|
|
float3 v = -normalize(float3(f(p + e.xyy) - f(p - e.xyy), f(p + e.yxy) - f(p - e.yxy), f(p + e.yyx) - f(p - e.yyx)));
|
|
float3 t = cross(particle.tangent, v);
|
|
v = TANGENT_SPEED * t / (1 + pow(abs(d), 0.5)) + GRADIENT_SPEED * v * d * (0.5 + 0.5 * step(0, -d));
|
|
v = lerp(particle.direction, v, SDF_SIMILARITY * GRADIENT_SPEED * deltaTime * frac(sdfBlendFactor));
|
|
particle.direction = v;
|
|
if (age <= 1)
|
|
{
|
|
recycle(particle);
|
|
}
|
|
}
|
|
else if (particle.type == ParticleType_Star)
|
|
{
|
|
if (distance(particle.currentPosition, cameraPosition) > starThreshold)
|
|
{
|
|
emitStar();
|
|
}
|
|
else
|
|
{
|
|
recycle(particle);
|
|
}
|
|
}
|
|
|
|
if ((id.x + id.y == 0) && note > 0.1)
|
|
{
|
|
rndSeed = 1337 * demoTime;
|
|
float3 emitPosition = cameraPosition;
|
|
float3 cameraDirection = normalize(cameraLookat - cameraPosition);
|
|
float3 cameraRight = cross(normalize(cameraUp), cameraDirection);
|
|
emitPosition += 10 * cameraDirection;
|
|
emitPosition -= 7 * normalize(cameraUp);
|
|
emitPosition += (50 * note - 25) * cameraRight;
|
|
float t = demoTime - deltaTime;
|
|
doEmitter(emitPosition, float3(0, 0.0, 0), t);
|
|
}
|
|
}
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void update(int3 id:SV_DispatchThreadID)
|
|
{
|
|
doUpdate(id, true);
|
|
}
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void event1(int3 id:SV_DispatchThreadID)
|
|
{
|
|
doUpdate(id, false);
|
|
}
|
|
|
|
[numthreads(1, 1, 1)]
|
|
void event2(int3 id:SV_DispatchThreadID)
|
|
{
|
|
doUpdate(id, true);
|
|
if (id.x + id.y == 0)
|
|
{
|
|
emit(ParticleType_Emitter, emitterPosition, emitterPosition, emitterVelocity, emitterMass, demoTime);
|
|
}
|
|
}
|
|
|
|
float3 project(float3 v, bool bOld)
|
|
{
|
|
float4 r;
|
|
if (bOld)
|
|
r = mul(mul(float4(v, 1.0), _o._v), _o._p);
|
|
else
|
|
r = mul(mul(float4(v, 1.0), _c._v), _c._p);
|
|
|
|
r.x /= 1.6;
|
|
return r.xyz / r.w;
|
|
}
|
|
|
|
float3 h2r(float h, float s, float v){ return lerp(saturate((abs(frac(h + float3(1, 2, 3) / 3) * 6 - 3) - 1)), 1, s)*v; }
|
|
|
|
|
|
struct _2
|
|
{
|
|
float3 pos1 : TEXCOORD0;
|
|
float3 pos2 : TEXCOORD1;
|
|
float3 dir : TEXCOORD2;
|
|
float age : AGE;
|
|
int type : TYPE;
|
|
};
|
|
|
|
struct _3
|
|
{
|
|
float4 pos : SV_POSITION;
|
|
float length : LENGTH;
|
|
float age : AGE;
|
|
float3 wpos : WORLDPOS;
|
|
int type : TYPE;
|
|
};
|
|
|
|
void pvs(uint id : SV_VertexID, out _2 o)
|
|
{
|
|
ParticleState particle = readParticles[id];
|
|
o.pos1 = particle.oldPosition;
|
|
o.pos2 = particle.currentPosition;
|
|
o.dir = particle.direction;
|
|
o.age = (demoTime - particle.creationTime) / lifeTime;
|
|
o.type = particle.type;
|
|
}
|
|
|
|
float particleSize(float z)
|
|
{
|
|
return (1 + 2 * envelope1) * 0.002f / z;
|
|
}
|
|
|
|
[maxvertexcount(18)]
|
|
void pgs(point _2 input[1], inout TriangleStream<_3> o)
|
|
{
|
|
_2 p = input[0];
|
|
|
|
float3 p1 = project(p.pos1, false);
|
|
float3 p2 = project(p.pos2, false);
|
|
if (p2.z <= 0 || p1.z <= 0 )
|
|
return;
|
|
|
|
float s1 = min(0.01, particleSize(p1.z));
|
|
float s2 = min(0.01, particleSize(p2.z));
|
|
float2 d = normalize(p2.xy - p1.xy);
|
|
d += (1.0 - step(0.5, length(float3(d, 0)))) * normalize(project(p.dir, false).xy);
|
|
d += (1.0 - step(0.5, length(float3(d, 0)))) * float2(1, 0);
|
|
|
|
float2 pr = float2(-d.y, d.x);
|
|
p1.xy += d * s1 * 0.5;
|
|
p2.xy -= d * s2 * 0.5;
|
|
|
|
_3 r;
|
|
r.length = distance(p1.xy, p2.xy) * 128;
|
|
r.age = p.age;
|
|
r.type = p.type;
|
|
r.wpos = p.pos2;
|
|
|
|
r.pos = float4(p1.xy - d * s1, p1.z, 1.0); o.Append(r);
|
|
r.pos = float4(p1.xy + pr * s1, p1.z, 1.0); o.Append(r);
|
|
r.pos = float4(p1.xy - pr * s1, p1.z, 1.0); o.Append(r);
|
|
o.RestartStrip();
|
|
|
|
r.pos = float4(p1.xy - pr * s1, p1.z, 1.0); o.Append(r);
|
|
r.pos = float4(p1, 1.0); o.Append(r);
|
|
r.pos = float4(p2.xy - pr * s2, p2.z, 1.0); o.Append(r);
|
|
o.RestartStrip();
|
|
|
|
r.pos = float4(p1, 1.0); o.Append(r);
|
|
r.pos = float4(p2, 1.0); o.Append(r);
|
|
r.pos = float4(p2.xy - pr * s2, p2.z, 1.0); o.Append(r);
|
|
o.RestartStrip();
|
|
|
|
r.pos = float4(p1, 1.0); o.Append(r);
|
|
r.pos = float4(p2.xy + pr * s2, p2.z, 1.0); o.Append(r);
|
|
r.pos = float4(p2, 1.0); o.Append(r);
|
|
o.RestartStrip();
|
|
|
|
r.pos = float4(p1, 1.0); o.Append(r);
|
|
r.pos = float4(p1.xy + pr * s1, p1.z, 1.0); o.Append(r);
|
|
r.pos = float4(p2.xy + pr * s2, p2.z, 1.0); o.Append(r);
|
|
o.RestartStrip();
|
|
|
|
r.pos = float4(p2.xy + d * s2, p2.z, 1.0); o.Append(r);
|
|
r.pos = float4(p2.xy - pr * s2, p2.z, 1.0); o.Append(r);
|
|
r.pos = float4(p2.xy + pr * s2, p2.z, 1.0); o.Append(r);
|
|
o.RestartStrip();
|
|
}
|
|
|
|
float3 fusion(float x)
|
|
{
|
|
float t = saturate(x);
|
|
return saturate(float3(sqrt(t), t*t*t, max(sin(3.1415*1.75*t), pow(t, 12.0))));
|
|
}
|
|
|
|
float noise3d(float3 p)
|
|
{
|
|
p += 133 + smoothNoiseDirection * demoTime;
|
|
p /= 1000;
|
|
float n = (
|
|
tex0.SampleLevel(sampler0, p.yz, 0).r +
|
|
tex0.SampleLevel(sampler0, p.xz, 0).r +
|
|
tex0.SampleLevel(sampler0, p.xy, 0).r) / 3.0;
|
|
return smoothstep(0.5 , 1.0, n);
|
|
}
|
|
|
|
float4 pps(_3 i) : SV_Target
|
|
{
|
|
float intensity = 1.0 - i.age;
|
|
float3 color = fusion(1.0 - i.age * 3);
|
|
if (i.type == ParticleType_Star)
|
|
{
|
|
float x = (distance(i.wpos, cameraPosition)-1) / starThreshold;
|
|
intensity = 5 * smoothstep(0, 0.2, x)*exp(-x * 10);
|
|
intensity /= (i.length + 8);
|
|
}
|
|
else
|
|
{
|
|
float n = noise3d(i.wpos);
|
|
intensity /= (i.length + 128);
|
|
color = lerp(color, smoothNoiseColor.rgb, smoothNoiseColor.a * n);
|
|
}
|
|
|
|
if (i.type == ParticleType_Star)
|
|
color = float3(intensity, intensity, intensity);
|
|
|
|
return float4(color, intensity);
|
|
}
|
|
|
|
//---
|
|
// Postprocessing computer shaders
|
|
//---
|
|
|
|
|
|
float mod(float x, float y)
|
|
{
|
|
return x - y * floor(x / y);
|
|
}
|
|
|
|
float2 mod(float2 x, float2 y)
|
|
{
|
|
return x - y * floor(x / y);
|
|
}
|
|
|
|
float3 mod(float3 x, float3 y)
|
|
{
|
|
return x - y * floor(x / y);
|
|
}
|
|
|
|
float2 mod(float2 x, float y)
|
|
{
|
|
return x - y * floor(x / y);
|
|
}
|
|
|
|
float3 mod(float3 x, float y)
|
|
{
|
|
return x - y * floor(x / y);
|
|
}
|
|
|
|
float pulse(float center, float width, float x)
|
|
{
|
|
float t = abs(x - center);
|
|
if (t > width) return 0;
|
|
t /= width;
|
|
return 1 - t*t*(3 - 2 * t);
|
|
}
|
|
|
|
//---
|
|
// Radial and circumferential blur
|
|
//---
|
|
static const float rcfMipmapFactor = 1.5;
|
|
static const float rcfStepFactor = 5;
|
|
static const float rcfStrengthRadial = 5;
|
|
static const float rcfStrengthCircumferential = 5;
|
|
static const float rcfRadius = 15;
|
|
static const float2 rcfCenter = { 0.5, 0.5 };
|
|
|
|
float rcfRadialFalloff(float radius, float amount)
|
|
{
|
|
return radius * (1 + amount*radius*0.01);
|
|
}
|
|
|
|
float4 rcfBlur(float2 tc, float2 resolution, float strength, float circumFerentialStrength)
|
|
{
|
|
// Direction from rcfCenter to the current tc, aspect ratio corrected
|
|
float2 d = (tc - rcfCenter) * float2(resolution.x / resolution.y, 1);
|
|
|
|
// Compute convolution step size
|
|
float delta = length(d);
|
|
float distortedDelta = rcfRadialFalloff(delta, strength);
|
|
float stepSize = abs(delta - distortedDelta) * rcfStepFactor;
|
|
|
|
float radius = rcfRadius;
|
|
// Circumferential blur
|
|
if (circumFerentialStrength > 0) {
|
|
d = float2(d.y, -d.x);
|
|
stepSize *= circumFerentialStrength;
|
|
radius = floor(rcfRadius * circumFerentialStrength);
|
|
}
|
|
|
|
// Early exit
|
|
if (radius <= 0.5) {// || stepSize <= 1/resolution.x) {
|
|
return tex0.SampleLevel(sampler0, tc, 0);
|
|
}
|
|
|
|
// Adjust direction
|
|
d = normalize(d) * stepSize / resolution;
|
|
|
|
// Convolution
|
|
float mipLevel = sqrt(stepSize) * rcfMipmapFactor;
|
|
float sigma = radius / 2;
|
|
float twoSigmaSquared = 2 * sigma * sigma;
|
|
float4 a = 0;
|
|
for (float i = -radius; i <= radius; ++i) {
|
|
float weight = exp(-i*i / twoSigmaSquared);
|
|
a += tex0.SampleLevel(sampler0, i * d + tc, mipLevel) * weight;
|
|
}
|
|
|
|
return a / sqrt(twoSigmaSquared * PI);
|
|
}
|
|
|
|
[numthreads(16, 16, 1)] void csRadialBlur(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 resolution;
|
|
out0.GetDimensions(resolution.x, resolution.y);
|
|
|
|
float2 tc = (id.xy + 0.5) / resolution;
|
|
|
|
out0[id.xy] = rcfBlur(tc, resolution, rcfStrengthRadial, 0);
|
|
}
|
|
|
|
[numthreads(16, 16, 1)] void csCircumferentialBlur(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 resolution;
|
|
out0.GetDimensions(resolution.x, resolution.y);
|
|
|
|
float2 tc = (id.xy + 0.5) / resolution;
|
|
|
|
out0[id.xy] = rcfBlur(tc, resolution, rcfStrengthCircumferential, 1);
|
|
}
|
|
|
|
//---
|
|
// Sensor dirt
|
|
//---
|
|
|
|
[numthreads(16, 16, 1)] void csSensorDirt(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 tc = id.xy + .5;
|
|
}
|
|
|
|
//---
|
|
// Lens dirt
|
|
//---
|
|
static const float ldMinRadius = 0.025;
|
|
static const float ldMaxRadius = 0.075;
|
|
static const float ldCellWidth = 3 * ldMaxRadius;
|
|
static const float ldBorderWidthHalf = 0.00125;
|
|
static const float ldSeed = 3;
|
|
static const float ldNumLayers = 4;
|
|
|
|
[numthreads(16, 16, 1)] void csLensDirt(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 resolution;
|
|
out0.GetDimensions(resolution.x, resolution.y);
|
|
|
|
float2 tc = (id.xy + 0.5) / resolution;
|
|
tc *= float2(resolution.x / resolution.y, 1);
|
|
//float2 tc = id.xy + .5;
|
|
|
|
rndSeed = ldSeed;
|
|
|
|
float4 a = 0;
|
|
for (int i = 0; i < ldNumLayers; ++i) {
|
|
// Modulo repeat, find cell
|
|
float2 p = mod(tc, ldCellWidth) - ldCellWidth / 2;
|
|
float2 c = floor(tc / ldCellWidth);
|
|
|
|
// Backup seed
|
|
uint seedBackup = rndSeed;
|
|
|
|
// Modifiy seed with cell coordinates
|
|
rndSeed += (c.x + c.y*ldCellWidth + i*ldCellWidth*ldCellWidth) * resolution.x;
|
|
|
|
// Determine radius and jitter
|
|
float r = lerp(ldMinRadius, ldMaxRadius, rnd());
|
|
p += float2(srnd(), srnd()) * (ldCellWidth / 2 - r);
|
|
|
|
// Shapes
|
|
// Hexagon
|
|
//p = abs(p);
|
|
//float d = max(p.y + p.x*0.57735, p.x*1.1547);
|
|
|
|
// Circle
|
|
//float d = length(p);
|
|
|
|
// Pentagon
|
|
//float phi = 0.145;
|
|
//float2 n[] = {
|
|
// { sin(0*PI/5 + phi), cos(0*PI/5 + phi) },
|
|
// { sin(2*PI/5 + phi), cos(2*PI/5 + phi) },
|
|
// { sin(4*PI/5 + phi), cos(4*PI/5 + phi) },
|
|
// { sin(6*PI/5 + phi), cos(6*PI/5 + phi) },
|
|
// { sin(8*PI/5 + phi), cos(8*PI/5 + phi) }
|
|
//};
|
|
//// Difference between circumcircle and incircle radii
|
|
//float delta = r - (1 + sqrt(5)) / 4 * r;
|
|
//float d = max(max(max(max(dot(p, n[0]), dot(p, n[1])), dot(p, n[2])), dot(p, n[3])), dot(p, n[4]));
|
|
//// Mix between sphere and pentagon
|
|
//d = lerp(length(p), d+ delta, 0.5);
|
|
float d = length(p);
|
|
|
|
// Random color
|
|
a += (float4(rnd(), rnd(), rnd(), 1) + 0.5) * (smoothstep(r, r - ldBorderWidthHalf * 2, d) + 0.25 * smoothstep(ldBorderWidthHalf, 0, abs(d - r + ldBorderWidthHalf * 2)));
|
|
//a += float4(c, 0, 1)*0.1;
|
|
|
|
// Restore seed
|
|
rndSeed = seedBackup;
|
|
|
|
// Displace grid
|
|
tc += ldCellWidth / ldNumLayers;
|
|
}
|
|
|
|
//out0[id.xy] = float4(a.xyz / ldNumLayers, 1);
|
|
out0[id.xy] = float4(a.xyz / ldNumLayers, 1);
|
|
}
|
|
|
|
//---
|
|
// Blur
|
|
//---
|
|
static const float bRadius = 160;
|
|
static const float bSigma = bRadius / 3.5;
|
|
static const float bTwoSigmaSquared = 2 * bSigma * bSigma;
|
|
|
|
void bBlur(int3 id, float2 direction)
|
|
{
|
|
float2 resolution;
|
|
out0.GetDimensions(resolution.x, resolution.y);
|
|
|
|
float2 sourceResolution;
|
|
tex0.GetDimensions(sourceResolution.x, sourceResolution.y);
|
|
|
|
float mipLevel = log2(sourceResolution.x / resolution.x);
|
|
|
|
float2 tc = (id.xy + 0.5) / resolution;
|
|
float2 d = direction / resolution;
|
|
|
|
float4 a = 0;
|
|
for (float i = -bRadius; i <= bRadius; ++i) {
|
|
float weight = exp(-i*i / bTwoSigmaSquared);
|
|
a += tex0.SampleLevel(sampler0, i * d + tc, mipLevel) * weight;
|
|
}
|
|
|
|
out0[id.xy] = a / sqrt(bTwoSigmaSquared * PI);
|
|
}
|
|
|
|
[numthreads(16, 16, 1)] void csBlurH(int3 id:SV_DispatchThreadID) { bBlur(id, float2(1, 0)); }
|
|
[numthreads(16, 16, 1)] void csBlurV(int3 id:SV_DispatchThreadID) { bBlur(id, float2(0, 1)); }
|
|
|
|
//---
|
|
// Streaks
|
|
//---
|
|
static const float stRadius = 160;
|
|
|
|
[numthreads(16, 16, 1)] void csStreaks(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 resolution;
|
|
out0.GetDimensions(resolution.x, resolution.y);
|
|
|
|
float2 sourceResolution;
|
|
tex0.GetDimensions(sourceResolution.x, sourceResolution.y);
|
|
|
|
float mipLevel = log2(sourceResolution.x / resolution.x);
|
|
|
|
float2 tc = (id.xy + 0.5) / resolution;
|
|
float2 d = float2(1, 0) / resolution;
|
|
|
|
// Convolution with parabolic tent filter
|
|
float4 a = 0;
|
|
for (float i = -stRadius + 1; i < stRadius; ++i) {
|
|
float weight = (stRadius - abs(i)) * (stRadius - abs(i));
|
|
a += tex0.SampleLevel(sampler0, i * d + tc, mipLevel) * weight;
|
|
}
|
|
|
|
out0[id.xy] = a * 1.5 / (stRadius * stRadius * stRadius);
|
|
}
|
|
|
|
//---
|
|
// Distort Chroma
|
|
//---
|
|
static const float dcStrength = 10.0;
|
|
|
|
static const float2 dcCenter = { 0.5, 0.5 };
|
|
static const int dcSamples = 12;
|
|
|
|
float dcRadialFalloff(float radius, float amount)
|
|
{
|
|
return radius * (1 + amount*radius*0.01);
|
|
}
|
|
|
|
float4 dcSample(float2 tc, float amount, float2 resolution)
|
|
{
|
|
// Direction from cdCenter to the current tc, aspect ratio corrected
|
|
float2 d = (tc - dcCenter) * float2(resolution.x / resolution.y, 1);
|
|
float radius = length(d);
|
|
d /= radius == 0 ? d : radius;
|
|
return tex0.SampleLevel(sampler0, dcCenter + d * dcRadialFalloff(radius, amount) / float2(resolution.x / resolution.y, 1), 0);
|
|
}
|
|
|
|
[numthreads(16, 16, 1)] void csDistortChroma(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 resolution;
|
|
out0.GetDimensions(resolution.x, resolution.y);
|
|
|
|
float2 tc = (id.xy + 0.5) / resolution;
|
|
|
|
float4 original = tex0.SampleLevel(sampler0, tc, 0);
|
|
if (dcStrength == 0) {
|
|
out0[id.xy] = original;
|
|
return;
|
|
}
|
|
|
|
// Accumulate along spectrum
|
|
float4 a = 0;
|
|
for (int i = 1; i <= dcSamples; ++i) {
|
|
float lambda = 0.4f * (i / float(dcSamples + 1)) + 0.35f;
|
|
float4 w = float4(pulse(0.65, 0.1, lambda), pulse(0.55, 0.1, lambda), pulse(0.45, 0.1, lambda), 0.25) * 4.0;
|
|
a += w * dcSample(tc, -(i - 1) / float(dcSamples) * dcStrength, resolution);
|
|
}
|
|
|
|
a /= dcSamples;
|
|
|
|
out0[id.xy] = lerp(original, a, saturate(dcStrength));
|
|
}
|
|
|
|
//---
|
|
// Ghosts
|
|
//---
|
|
float4 ghRainbow(float angle)
|
|
{
|
|
return float4(sin(angle), sin(angle + 3.1415*2.0 / 3.0), sin(angle + 3.1415*4.0 / 3.0), 1) * 0.5 + 0.5;
|
|
}
|
|
|
|
[numthreads(16, 16, 1)] void csGhosts(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 resolution;
|
|
out0.GetDimensions(resolution.x, resolution.y);
|
|
|
|
float2 tc = (id.xy + 0.5) / resolution;
|
|
tc = 1 - tc;
|
|
float2 texelSize = 1 / resolution;
|
|
float2 aspectCorrection = float2(resolution.x / resolution.y, 1);
|
|
|
|
float2 direction = (0.5 - tc) * 2;
|
|
float4 a = 0;
|
|
for (int i = 0; i < 5; ++i) {
|
|
float2 sc0 = 1 - frac(tc + direction * i * 0.2);
|
|
float2 sc1 = 1 - frac(tc + direction * i * 0.1999);
|
|
float r = length((sc0 - 0.5) * aspectCorrection);
|
|
a += max(0, tex0.SampleLevel(sampler0, sc0, 0) - 0.125) * lerp(1, ghRainbow(r * 20), 1.5*r);
|
|
a += max(0, tex1.SampleLevel(sampler0, sc0, 0) - 0.125) * lerp(1, ghRainbow(r * 15 + i), 1.5*r);
|
|
}
|
|
|
|
out0[id.xy] = float4(a.xyz / 5, 1);
|
|
}
|
|
|
|
|
|
//---
|
|
// Merge
|
|
//---
|
|
float3 saturation(float3 color, float saturation)
|
|
{
|
|
return lerp(dot(color, float3(0.2126, 0.7152, 0.0722)), color, saturation);
|
|
}
|
|
|
|
float3 liftGammaGain(float3 color, float3 lift, float3 gamma, float3 gain)
|
|
{
|
|
return pow(saturate(gain*color + lift*(-gain*color + 1)), 1 / gamma);
|
|
}
|
|
|
|
float3 uncharted2ToneMapping(float3 c)
|
|
{
|
|
float A = 0.15;
|
|
float B = 0.50;
|
|
float C = 0.10;
|
|
float D = 0.20;
|
|
float E = 0.02;
|
|
float F = 0.30;
|
|
float W = 11.2;
|
|
|
|
c = ((c * (A * c + C * B) + D * E) / (c * (A * c + B) + D * F)) - E / F;
|
|
float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F;
|
|
return c / white;
|
|
}
|
|
|
|
[numthreads(16, 16, 1)] void csMerge(int3 id:SV_DispatchThreadID)
|
|
{
|
|
float2 resolution;
|
|
out0.GetDimensions(resolution.x, resolution.y);
|
|
|
|
float2 tc = (id.xy + 0.5) / resolution;
|
|
tc.x += sin((tc.y + demoTime) * 44) * 0.05 * distort;
|
|
|
|
rndSeed = (id.y * 720 + id.x);// * int(time * 1000);
|
|
float3 rndNoiseXYT = float3(rnd(), rnd(), rnd());
|
|
|
|
rndSeed = id.y;
|
|
float3 rndNoiseY = float3(rnd(), rnd(), rnd());
|
|
|
|
|
|
float3 base = tex0.SampleLevel(sampler0, tc, 0).xyz;
|
|
float3 blur1 = tex1.SampleLevel(sampler0, tc, 0).xyz;
|
|
float3 blur8 = tex2.SampleLevel(sampler0, tc, 0).xyz;
|
|
float3 blur32 = tex3.SampleLevel(sampler0, tc, 0).xyz;
|
|
float3 streaks = tex4.SampleLevel(sampler0, tc, 0).xyz;
|
|
float3 lensDirt = tex5.SampleLevel(sampler0, tc, 0).xyz;
|
|
float3 ghosts = tex6.SampleLevel(sampler0, tc, 0).xyz; // t6
|
|
|
|
float3 color = base.xyz;
|
|
|
|
color += blur1*0.125;
|
|
color += blur8*0.125;
|
|
color += blur32*0.125;
|
|
|
|
// Streaks
|
|
color += streaks * (lerp(1, rndNoiseY.x, 0.25)) * 0.25;// * rnd();
|
|
|
|
// Lens dirt
|
|
color += lensDirt * 0.25 * max(0, dot(tex2.SampleLevel(sampler0, frac(1-tc), 0).xyz, float3(0.299, 0.587, 0.114)) - 1.0);
|
|
|
|
// Ghosts
|
|
color += ghosts * 0.125;// * ghosts * 0.25;
|
|
|
|
// Grain
|
|
color *= lerp(1, rndNoiseXYT, 0.35);
|
|
|
|
//color = base;
|
|
|
|
|
|
// Tonemapping
|
|
color = max(0, uncharted2ToneMapping(max(0, color*0.5)));
|
|
//color *= color;
|
|
|
|
color = liftGammaGain(color, float3(0, 0, 0), float3(1, 1, 1), float3(1, 1, 1));
|
|
color = pow(color, 1 / 2.2);
|
|
color = saturation(color, 1.2);
|
|
//color = pow(color, 1 / 2.2);
|
|
color = lerp(color, colorBlend.rgb, colorBlend.a);
|
|
float4 result = float4(color, dot(color, float3(0.299, 0.587, 0.114)));
|
|
|
|
out0[id.xy] = result;
|
|
}
|