local copperplasma={}
local okShader=false
local shader=nil
local tLocal=0.0
local lastTimelineT=nil
local W,H=1920,1080
local phase1={0.0,0.0}
local phase2={0.0,0.0}
local function fract(x)return x-math.floor(x)end
local function lerp(a,b,t)return a+(b-a)*t end
local function smooth01(x)if x<=0 then return 0 elseif x>=1 then return 1 else return x*x*(3-2*x)end end
local function hash01(n)return fract(math.sin(n*12.9898+78.233)*43758.5453)end
local function sceneParams(idx)
local h1=lerp(0.00,1.00,hash01(idx*19+1))
local s1=lerp(0.35,0.90,hash01(idx*19+2))
local v1=lerp(0.55,0.95,hash01(idx*19+3))
local f1=lerp(1.20,2.10,hash01(idx*19+4))
local a1=hash01(idx*19+5)*2*math.pi
local m1=lerp(0.05,0.30,hash01(idx*19+6))
local s1x,s1y=math.cos(a1)*m1,math.sin(a1)*m1
local off=lerp(0.25,0.75,hash01(idx*19+7))
local h2=fract(h1+off)
local s2=lerp(0.30,0.90,hash01(idx*19+8))
local v2=lerp(0.50,0.95,hash01(idx*19+9))
local f2=lerp(1.60,3.10,hash01(idx*19+10))
local a2=hash01(idx*19+11)*2*math.pi
local m2=lerp(0.05,0.35,hash01(idx*19+12))
local s2x,s2y=math.cos(a2)*m2,math.sin(a2)*m2
local warpBias=hash01(idx*19+13)
return{l1HSV={h1,s1,v1},l2HSV={h2,s2,v2},l1Freq=f1,l2Freq=f2,l1Speed={s1x,s1y},l2Speed={s2x,s2y},warpBias=warpBias}end
local function buildShader()
local s
local ok,err=pcall(function()
s=love.graphics.newShader([[
extern number u_time;
extern vec2   u_resolution;
extern number u_l1_h;   extern number u_l1_s;   extern number u_l1_v;
extern number u_l1_freq; extern number u_l1_mix;
extern number u_l1_hueSpan;
extern vec2   u_phase1;
extern number u_l2_h;   extern number u_l2_s;   extern number u_l2_v;
extern number u_l2_freq; extern number u_l2_mix;
extern number u_l2_hueSpan;
extern vec2   u_phase2;
extern number u_rot;
extern number u_warpAmp;
extern number u_warpFreqX;
extern number u_warpFreqY;
extern number u_pxEnabled;
extern number u_pxSizePix;
extern number u_colorSteps;
extern number u_vignetteStrength;
extern number u_gamma;
vec2 norm(vec2 p){ return p / u_resolution; }
vec2 rotateUV(vec2 uv, float a){
vec2 c = vec2(0.5, 0.5);
float s = sin(a), co = cos(a);
uv -= c;
uv = mat2(co,-s,s,co) * uv;
uv += c;
return uv; }
vec2 snapToBlockCenter(vec2 screenCoord, float pxSize){
vec2 cell = floor(screenCoord / pxSize);
vec2 centerInPix = (cell + vec2(0.5, 0.5)) * pxSize;
return centerInPix; }
vec2 warp(vec2 uv, float t, float amp, float fx, float fy){
float wx = sin(6.28318*(uv.y*fy + t*0.15));
float wy = sin(6.28318*(uv.x*fx - t*0.12));
return uv + amp * vec2(wx, wy); }
float field(vec2 uv, float k){
float a = sin((uv.x + uv.y) * 6.28318 * k);
float b = sin((uv.x - uv.y) * 6.28318 * k);
return 0.5 + 0.5 * (a + b) * 0.5; }
vec3 hsv2rgb(float h, float s, float v){
float i = floor(h * 6.0);
float f = h * 6.0 - i;
float p = v * (1.0 - s);
float q = v * (1.0 - f*s);
float t = v * (1.0 - (1.0 - f)*s);
int m = int(mod(i, 6.0));
if      (m==0) return vec3(v, t, p);
else if (m==1) return vec3(q, v, p);
else if (m==2) return vec3(p, v, t);
else if (m==3) return vec3(p, q, v);
else if (m==4) return vec3(t, p, v);
else           return vec3(v, p, q); }
vec3 vignette(vec2 uv, vec3 col, float strength){
float dx = uv.x - 0.5, dy = uv.y - 0.5;
float r2 = dx*dx + dy*dy;
float vig = 1.0 - strength * smoothstep(0.08, 0.45, r2);
return col * vig; }
vec3 posterize(vec3 c, float steps){
if (steps <= 0.0) return c;
return floor(c * steps + 0.5) / steps; }
vec4 effect(vec4 color, Image tex, vec2 tc, vec2 sc){
if (u_pxEnabled > 0.5){
sc = snapToBlockCenter(sc, max(u_pxSizePix, 1.0)); }
vec2 uv = norm(sc);
uv = rotateUV(uv, u_rot);
uv = warp(uv, u_time, u_warpAmp, u_warpFreqX, u_warpFreqY);
vec2 uv1 = uv + u_phase1;
float v1 = field(uv1, max(u_l1_freq, 0.01));
float h1 = fract(u_l1_h + (v1 - 0.5) * u_l1_hueSpan);
vec3  c1 = hsv2rgb(h1, u_l1_s, u_l1_v);
vec2 uv2 = uv + u_phase2;
float v2 = field(uv2, max(u_l2_freq, 0.01));
float h2 = fract(u_l2_h + (v2 - 0.5) * u_l2_hueSpan);
vec3  c2 = hsv2rgb(h2, u_l2_s, u_l2_v);
vec3 outCol = c1 * clamp(u_l1_mix, 0.0, 1.0)
+ c2 * clamp(u_l2_mix, 0.0, 1.0);
outCol = posterize(outCol, u_colorSteps);
outCol = vignette(uv, outCol, u_vignetteStrength);
outCol = pow(outCol, vec3(1.0 / max(u_gamma, 0.0001)));
return vec4(outCol, 1.0); } ]]) end)
if not ok or not s then return nil,(err or "shader compile error")end
return s,nil end
function copperplasma.load()
W,H=love.graphics.getWidth(),love.graphics.getHeight()
shader=select(1,buildShader())
okShader=shader~=nil
tLocal,lastTimelineT=0.0,nil
phase1[1],phase1[2]=0.0,0.0
phase2[1],phase2[2]=0.0,0.0 end
function copperplasma.start(now)end
function copperplasma.stop(now)end
function copperplasma.update(dt,timelineT)
if timelineT~=nil then
if(lastTimelineT==nil)or(timelineT>lastTimelineT)then tLocal=timelineT else tLocal=tLocal+dt end
lastTimelineT=timelineT
else
tLocal=tLocal+dt end
W,H=love.graphics.getWidth(),love.graphics.getHeight()
local t=tLocal
local sDur=3.75
local sceneA=math.floor(t/sDur)
local sceneB=sceneA+1
local frac=(t%sDur)/sDur
local s=smooth01(frac)
local A=sceneParams(sceneA)
local B=sceneParams(sceneB)
local l1SpeedX=lerp(A.l1Speed[1],B.l1Speed[1],s)
local l1SpeedY=lerp(A.l1Speed[2],B.l1Speed[2],s)
local l2SpeedX=lerp(A.l2Speed[1],B.l2Speed[1],s)
local l2SpeedY=lerp(A.l2Speed[2],B.l2Speed[2],s)
phase1[1]=phase1[1]+l1SpeedX*dt
phase1[2]=phase1[2]+l1SpeedY*dt
phase2[1]=phase2[1]+l2SpeedX*dt
phase2[2]=phase2[2]+l2SpeedY*dt end
function copperplasma.draw()
if not okShader then
love.graphics.setColor(1,0.25,0.25,1)
love.graphics.print("plasma shader failed to compile",20,60)
love.graphics.setColor(1,1,1,1)
return
end
local t=tLocal
local sDur=3.75
local sceneA=math.floor(t/sDur)
local sceneB=sceneA+1
local frac=(t%sDur)/sDur
local s=smooth01(frac)
local A=sceneParams(sceneA)
local B=sceneParams(sceneB)
local l1H=lerp(A.l1HSV[1],B.l1HSV[1],s)
local l1S=lerp(A.l1HSV[2],B.l1HSV[2],s)
local l1V=lerp(A.l1HSV[3],B.l1HSV[3],s)
local l2H=lerp(A.l2HSV[1],B.l2HSV[1],s)
local l2S=lerp(A.l2HSV[2],B.l2HSV[2],s)
local l2V=lerp(A.l2HSV[3],B.l2HSV[3],s)
local l1Freq=lerp(A.l1Freq,B.l1Freq,s)
local l2Freq=lerp(A.l2Freq,B.l2Freq,s)
local rot=math.sin(t*0.03)*0.04
local warpBias=lerp(A.warpBias,B.warpBias,s)
local warpAmp=0.005+0.010*(0.35+0.65*math.sin(t*0.27+warpBias*6.28318))
local pxEnabled=1
local px=8
love.graphics.setShader(shader)
shader:send("u_time",t)
shader:send("u_resolution",{W,H})
shader:send("u_l1_h",l1H)
shader:send("u_l1_s",l1S)
shader:send("u_l1_v",l1V)
shader:send("u_l1_freq",l1Freq)
shader:send("u_l1_mix",1.00)
shader:send("u_l1_hueSpan",0.25)
shader:send("u_phase1",phase1)
shader:send("u_l2_h",l2H)
shader:send("u_l2_s",l2S)
shader:send("u_l2_v",l2V)
shader:send("u_l2_freq",l2Freq)
shader:send("u_l2_mix",0.45)
shader:send("u_l2_hueSpan",0.35)
shader:send("u_phase2",phase2)
shader:send("u_rot",rot)
shader:send("u_warpAmp",warpAmp)
shader:send("u_warpFreqX",1.10)
shader:send("u_warpFreqY",1.00)
shader:send("u_pxEnabled",pxEnabled)
shader:send("u_pxSizePix",px)
shader:send("u_colorSteps",16)
shader:send("u_vignetteStrength",0.00)
shader:send("u_gamma",1.00)
love.graphics.setColor(1,1,1,1)
love.graphics.rectangle("fill",0,0,W,H)
love.graphics.setShader()
love.graphics.setColor(1,1,1,1) end
return copperplasma
