Working
This commit is contained in:
8
shaders/black-and-white/shader.json
Normal file
8
shaders/black-and-white/shader.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "black-and-white",
|
||||
"name": "Black and White",
|
||||
"description": "A minimal monochrome shader that converts the decoded video input to grayscale.",
|
||||
"category": "Built-in",
|
||||
"entryPoint": "shadeVideo",
|
||||
"parameters": []
|
||||
}
|
||||
5
shaders/black-and-white/shader.slang
Normal file
5
shaders/black-and-white/shader.slang
Normal file
@@ -0,0 +1,5 @@
|
||||
float4 shadeVideo(ShaderContext context)
|
||||
{
|
||||
float luma = dot(context.sourceColor.rgb, float3(0.2126, 0.7152, 0.0722));
|
||||
return float4(luma, luma, luma, context.sourceColor.a);
|
||||
}
|
||||
36
shaders/gaussian-blur/shader.json
Normal file
36
shaders/gaussian-blur/shader.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"id": "gaussian-blur",
|
||||
"name": "Gaussian Blur",
|
||||
"description": "Applies a simple Gaussian-style blur to the decoded video input.",
|
||||
"category": "Built-in",
|
||||
"entryPoint": "shadeVideo",
|
||||
"parameters": [
|
||||
{
|
||||
"id": "radius",
|
||||
"label": "Radius",
|
||||
"type": "float",
|
||||
"default": 2.0,
|
||||
"min": 0.0,
|
||||
"max": 8.0,
|
||||
"step": 0.1
|
||||
},
|
||||
{
|
||||
"id": "strength",
|
||||
"label": "Strength",
|
||||
"type": "float",
|
||||
"default": 1.0,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "samples",
|
||||
"label": "Samples",
|
||||
"type": "float",
|
||||
"default": 2.0,
|
||||
"min": 0.0,
|
||||
"max": 25.0,
|
||||
"step": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
35
shaders/gaussian-blur/shader.slang
Normal file
35
shaders/gaussian-blur/shader.slang
Normal file
@@ -0,0 +1,35 @@
|
||||
float4 shadeVideo(ShaderContext context)
|
||||
{
|
||||
float2 texel = 1.0 / max(context.inputResolution, float2(1.0, 1.0));
|
||||
float blurRadius = max(radius, 0.0);
|
||||
float2 sampleStep = texel * blurRadius;
|
||||
int sampleRadius = int(clamp(samples, 0.0, 8.0) + 0.5);
|
||||
|
||||
float4 center = sampleVideo(context.uv);
|
||||
float4 blur = float4(0.0, 0.0, 0.0, 0.0);
|
||||
float totalWeight = 0.0;
|
||||
|
||||
for (int y = -sampleRadius; y <= sampleRadius; ++y)
|
||||
{
|
||||
for (int x = -sampleRadius; x <= sampleRadius; ++x)
|
||||
{
|
||||
float distanceSquared = float(x * x + y * y);
|
||||
float sigma = max(float(sampleRadius) * 0.5, 0.5);
|
||||
float weight = exp(-distanceSquared / (2.0 * sigma * sigma));
|
||||
float2 offset = float2(float(x), float(y)) * sampleStep;
|
||||
blur += sampleVideo(context.uv + offset) * weight;
|
||||
totalWeight += weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (sampleRadius == 0)
|
||||
{
|
||||
blur = center;
|
||||
totalWeight = 1.0;
|
||||
}
|
||||
|
||||
blur /= max(totalWeight, 0.0001);
|
||||
|
||||
float mixValue = saturate(strength);
|
||||
return lerp(center, blur, mixValue);
|
||||
}
|
||||
72
shaders/vhs/shader.json
Normal file
72
shaders/vhs/shader.json
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"id": "vhs",
|
||||
"name": "VHS",
|
||||
"description": "VHS with wiggle, smear, and YIQ-style color separation inspired by the Godot shader reference.",
|
||||
"category": "Built-in",
|
||||
"entryPoint": "shadeVideo",
|
||||
"parameters": [
|
||||
{
|
||||
"id": "wiggle",
|
||||
"label": "Wiggle",
|
||||
"type": "float",
|
||||
"default": 0.03,
|
||||
"min": 0.0,
|
||||
"max": 1.5,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "wiggleSpeed",
|
||||
"label": "Wiggle Speed",
|
||||
"type": "float",
|
||||
"default": 25.0,
|
||||
"min": 0.0,
|
||||
"max": 100.0,
|
||||
"step": 1.0
|
||||
},
|
||||
{
|
||||
"id": "smear",
|
||||
"label": "Smear",
|
||||
"type": "float",
|
||||
"default": 1.0,
|
||||
"min": 0.0,
|
||||
"max": 2.0,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "blurSamples",
|
||||
"label": "Blur Samples",
|
||||
"type": "float",
|
||||
"default": 15.0,
|
||||
"min": 3.0,
|
||||
"max": 15.0,
|
||||
"step": 1.0
|
||||
},
|
||||
{
|
||||
"id": "vignetteAmount",
|
||||
"label": "Vignette",
|
||||
"type": "float",
|
||||
"default": 0.18,
|
||||
"min": 0.0,
|
||||
"max": 0.6,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "aberrationAmount",
|
||||
"label": "Aberration",
|
||||
"type": "float",
|
||||
"default": 0.75,
|
||||
"min": 0.0,
|
||||
"max": 3.0,
|
||||
"step": 0.05
|
||||
},
|
||||
{
|
||||
"id": "halationAmount",
|
||||
"label": "Halation",
|
||||
"type": "float",
|
||||
"default": 0.12,
|
||||
"min": 0.0,
|
||||
"max": 0.5,
|
||||
"step": 0.01
|
||||
}
|
||||
]
|
||||
}
|
||||
115
shaders/vhs/shader.slang
Normal file
115
shaders/vhs/shader.slang
Normal file
@@ -0,0 +1,115 @@
|
||||
float onOff(float a, float b, float c, float framecount)
|
||||
{
|
||||
return step(c, sin((framecount * 0.001) + a * cos((framecount * 0.001) * b)));
|
||||
}
|
||||
|
||||
float2 jumpy(float2 uv, float framecount)
|
||||
{
|
||||
float2 look = uv;
|
||||
float m = frac(framecount / 4.0);
|
||||
float dy = look.y - m;
|
||||
float window = 1.0 / (1.0 + 80.0 * dy * dy);
|
||||
look.x += 0.05 * sin(look.y * 10.0 + framecount) / 20.0 * onOff(4.0, 4.0, 0.3, framecount) * (0.5 + cos(framecount * 20.0)) * window;
|
||||
float vShift = (0.1 * wiggle) * 0.4 * onOff(2.0, 3.0, 0.9, framecount) * (sin(framecount) * sin(framecount * 20.0) + (0.5 + 0.1 * sin(framecount * 200.0) * cos(framecount)));
|
||||
look.y = frac(look.y - 0.01 * vShift);
|
||||
return look;
|
||||
}
|
||||
|
||||
float2 circle(float start, float points, float point)
|
||||
{
|
||||
float rad = 6.28318530718 * (1.0 / points) * (point + start);
|
||||
return float2(-(.3 + rad), cos(rad));
|
||||
}
|
||||
|
||||
float3 rgb2yiq(float3 c)
|
||||
{
|
||||
return float3(
|
||||
0.2989 * c.x + 0.5959 * c.y + 0.2115 * c.z,
|
||||
0.5870 * c.x - 0.2744 * c.y - 0.5229 * c.z,
|
||||
0.1140 * c.x - 0.3216 * c.y + 0.3114 * c.z
|
||||
);
|
||||
}
|
||||
|
||||
float3 yiq2rgb(float3 c)
|
||||
{
|
||||
return float3(
|
||||
1.0 * c.x + 1.0 * c.y + 1.0 * c.z,
|
||||
0.956 * c.x - 0.2720 * c.y - 1.1060 * c.z,
|
||||
0.6210 * c.x - 0.6474 * c.y + 1.7046 * c.z
|
||||
);
|
||||
}
|
||||
|
||||
float3 blurVhs(float2 uv, float d, int sampleCount)
|
||||
{
|
||||
float3 sum = float3(0.0, 0.0, 0.0);
|
||||
float weight = 1.0 / max(float(sampleCount), 1.0);
|
||||
float start = 2.0 / max(float(sampleCount), 1.0);
|
||||
float2 pixelOffset = float2(d, 0.0);
|
||||
float2 scale = 0.66 * 8.0 * pixelOffset;
|
||||
|
||||
for (int i = 0; i < 15; ++i)
|
||||
{
|
||||
if (i >= sampleCount)
|
||||
break;
|
||||
|
||||
float2 offset = circle(start, float(sampleCount), float(i)) * scale;
|
||||
sum += sampleVideo(frac(uv + offset)).rgb * weight;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
float4 shadeVideo(ShaderContext context)
|
||||
{
|
||||
float2 uv = context.uv;
|
||||
float framecount = frac(context.time * wiggleSpeed / 7.0) * 7.0;
|
||||
int sampleCount = int(clamp(blurSamples, 3.0, 15.0) + 0.5);
|
||||
|
||||
float d = 0.1 - round(frac(context.time / 3.0)) * 0.1;
|
||||
uv = jumpy(uv, framecount);
|
||||
float s = 0.0001 * -d + 0.0001 * wiggle * sin(context.time * wiggleSpeed);
|
||||
float e = min(0.30, pow(max(0.0, cos(uv.y * 4.0 + 0.3) - 0.75) * (s + 0.5), 3.0)) * 25.0;
|
||||
float r = 250.0 * (2.0 * s);
|
||||
uv.x += abs(r * pow(min(0.003, (-uv.y + (0.01 * frac(context.time / 5.0) * 5.0))) * 3.0, 2.0)) * wiggle;
|
||||
|
||||
d = 0.051 + abs(sin(s / 4.0));
|
||||
float c = max(0.0001, 0.002 * d) * smear;
|
||||
|
||||
float3 yBlur = blurVhs(uv, c + c * uv.x, sampleCount);
|
||||
float y = rgb2yiq(yBlur).r;
|
||||
|
||||
uv.x += 0.01 * d;
|
||||
c *= 6.0;
|
||||
float3 iBlur = blurVhs(uv, c, sampleCount);
|
||||
float i = rgb2yiq(iBlur).g;
|
||||
|
||||
uv.x += 0.005 * d;
|
||||
c *= 2.5;
|
||||
float3 qBlur = blurVhs(uv, c, sampleCount);
|
||||
float q = rgb2yiq(qBlur).b;
|
||||
|
||||
float3 color = yiq2rgb(float3(y, i, q)) - pow(s + e * 2.0, 3.0);
|
||||
|
||||
float2 centered = context.uv * 2.0 - 1.0;
|
||||
centered.x *= context.outputResolution.x / max(context.outputResolution.y, 1.0);
|
||||
float2 aberrationOffset = centered * (aberrationAmount * 0.0015);
|
||||
float redAberration = sampleVideo(frac(context.uv + aberrationOffset)).r;
|
||||
float blueAberration = sampleVideo(frac(context.uv - aberrationOffset)).b;
|
||||
color.r = lerp(color.r, redAberration, 0.35);
|
||||
color.b = lerp(color.b, blueAberration, 0.35);
|
||||
|
||||
float2 halationOffset = float2(0.0015, 0.0) * (1.0 + smear * 0.35);
|
||||
float3 halationSource =
|
||||
sampleVideo(frac(context.uv + halationOffset)).rgb * 0.4 +
|
||||
sampleVideo(frac(context.uv - halationOffset)).rgb * 0.4 +
|
||||
sampleVideo(frac(context.uv + halationOffset * 2.0)).rgb * 0.2;
|
||||
float halationLuma = dot(halationSource, float3(0.299, 0.587, 0.114));
|
||||
float halationMask = smoothstep(0.45, 1.0, halationLuma) * halationAmount;
|
||||
color += halationSource * float3(1.0, 0.38, 0.24) * halationMask * 0.35;
|
||||
|
||||
float vignetteBase = context.uv.x * (1.0 - context.uv.x) * context.uv.y * (1.0 - context.uv.y);
|
||||
float vignette = saturate(pow(vignetteBase * 16.0, 0.22));
|
||||
color *= lerp(1.0 - vignetteAmount, 1.0, vignette);
|
||||
|
||||
return float4(saturate(color), 1.0);
|
||||
}
|
||||
45
shaders/video-cube/shader.json
Normal file
45
shaders/video-cube/shader.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"id": "video-cube",
|
||||
"name": "Video Cube",
|
||||
"description": "Maps the live video onto the faces of a rotating cube in screen space.",
|
||||
"category": "Built-in",
|
||||
"entryPoint": "shadeVideo",
|
||||
"parameters": [
|
||||
{
|
||||
"id": "spinSpeed",
|
||||
"label": "Spin Speed",
|
||||
"type": "float",
|
||||
"default": 1.0,
|
||||
"min": 0.0,
|
||||
"max": 4.0,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "cubeScale",
|
||||
"label": "Cube Scale",
|
||||
"type": "float",
|
||||
"default": 0.85,
|
||||
"min": 0.3,
|
||||
"max": 1.4,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "faceZoom",
|
||||
"label": "Face Zoom",
|
||||
"type": "float",
|
||||
"default": 1.0,
|
||||
"min": 0.5,
|
||||
"max": 2.0,
|
||||
"step": 0.01
|
||||
},
|
||||
{
|
||||
"id": "backgroundMix",
|
||||
"label": "Background Mix",
|
||||
"type": "float",
|
||||
"default": 0.15,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"step": 0.01
|
||||
}
|
||||
]
|
||||
}
|
||||
116
shaders/video-cube/shader.slang
Normal file
116
shaders/video-cube/shader.slang
Normal file
@@ -0,0 +1,116 @@
|
||||
float3 rotateX(float3 p, float angle)
|
||||
{
|
||||
float s = sin(angle);
|
||||
float c = cos(angle);
|
||||
return float3(p.x, c * p.y - s * p.z, s * p.y + c * p.z);
|
||||
}
|
||||
|
||||
float3 rotateY(float3 p, float angle)
|
||||
{
|
||||
float s = sin(angle);
|
||||
float c = cos(angle);
|
||||
return float3(c * p.x + s * p.z, p.y, -s * p.x + c * p.z);
|
||||
}
|
||||
|
||||
bool intersectCube(float3 rayOrigin, float3 rayDirection, float halfExtent, out float hitDistance)
|
||||
{
|
||||
float3 boxMin = float3(-halfExtent, -halfExtent, -halfExtent);
|
||||
float3 boxMax = float3(halfExtent, halfExtent, halfExtent);
|
||||
|
||||
float3 invDir = 1.0 / rayDirection;
|
||||
float3 t0 = (boxMin - rayOrigin) * invDir;
|
||||
float3 t1 = (boxMax - rayOrigin) * invDir;
|
||||
|
||||
float3 tMin3 = min(t0, t1);
|
||||
float3 tMax3 = max(t0, t1);
|
||||
|
||||
float tMin = max(max(tMin3.x, tMin3.y), tMin3.z);
|
||||
float tMax = min(min(tMax3.x, tMax3.y), tMax3.z);
|
||||
|
||||
if (tMax < max(tMin, 0.0))
|
||||
{
|
||||
hitDistance = 0.0;
|
||||
return false;
|
||||
}
|
||||
|
||||
hitDistance = tMin > 0.0 ? tMin : tMax;
|
||||
return hitDistance > 0.0;
|
||||
}
|
||||
|
||||
float2 cubeFaceUv(float3 hitPoint, float halfExtent, float zoom)
|
||||
{
|
||||
float3 face = abs(hitPoint);
|
||||
float2 uv = float2(0.5, 0.5);
|
||||
float safeZoom = max(zoom, 0.001);
|
||||
|
||||
if (face.x >= face.y && face.x >= face.z)
|
||||
{
|
||||
uv = hitPoint.x > 0.0
|
||||
? float2(-hitPoint.z, hitPoint.y)
|
||||
: float2(hitPoint.z, hitPoint.y);
|
||||
}
|
||||
else if (face.y >= face.x && face.y >= face.z)
|
||||
{
|
||||
uv = hitPoint.y > 0.0
|
||||
? float2(hitPoint.x, -hitPoint.z)
|
||||
: float2(hitPoint.x, hitPoint.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
uv = hitPoint.z > 0.0
|
||||
? float2(hitPoint.x, hitPoint.y)
|
||||
: float2(-hitPoint.x, hitPoint.y);
|
||||
}
|
||||
|
||||
uv = uv / (halfExtent * 2.0 * safeZoom) + 0.5;
|
||||
return frac(uv);
|
||||
}
|
||||
|
||||
float4 shadeVideo(ShaderContext context)
|
||||
{
|
||||
float2 centeredUv = context.uv * 2.0 - 1.0;
|
||||
float aspect = context.outputResolution.x / max(context.outputResolution.y, 1.0);
|
||||
centeredUv.x *= aspect;
|
||||
|
||||
float3 rayOrigin = float3(0.0, 0.0, 2.7);
|
||||
float3 rayDirection = normalize(float3(centeredUv, -2.1));
|
||||
|
||||
float spin = context.time * spinSpeed;
|
||||
float yaw = spin;
|
||||
float pitch = spin * 0.61 + 0.35;
|
||||
|
||||
float3 localOrigin = rotateY(rotateX(rayOrigin, -pitch), -yaw);
|
||||
float3 localDirection = rotateY(rotateX(rayDirection, -pitch), -yaw);
|
||||
|
||||
float halfExtent = max(cubeScale, 0.05);
|
||||
float hitDistance = 0.0;
|
||||
|
||||
float3 background = lerp(float3(0.02, 0.02, 0.03), context.sourceColor.rgb, saturate(backgroundMix));
|
||||
|
||||
if (!intersectCube(localOrigin, localDirection, halfExtent, hitDistance))
|
||||
return float4(background, 1.0);
|
||||
|
||||
float3 localHit = localOrigin + localDirection * hitDistance;
|
||||
float2 faceUv = cubeFaceUv(localHit, halfExtent, faceZoom);
|
||||
float4 faceColor = sampleVideo(faceUv);
|
||||
|
||||
float3 normal;
|
||||
float3 face = abs(localHit);
|
||||
if (face.x >= face.y && face.x >= face.z)
|
||||
normal = float3(sign(localHit.x), 0.0, 0.0);
|
||||
else if (face.y >= face.x && face.y >= face.z)
|
||||
normal = float3(0.0, sign(localHit.y), 0.0);
|
||||
else
|
||||
normal = float3(0.0, 0.0, sign(localHit.z));
|
||||
|
||||
normal = rotateX(rotateY(normal, yaw), pitch);
|
||||
|
||||
float3 lightDir = normalize(float3(0.5, 0.8, 0.6));
|
||||
float diffuse = saturate(dot(normal, lightDir)) * 0.75 + 0.25;
|
||||
float edge = 1.0 - saturate(max(face.x, max(face.y, face.z)) - halfExtent + 0.03) / 0.03;
|
||||
|
||||
float3 shaded = faceColor.rgb * diffuse;
|
||||
shaded = lerp(shaded, shaded + 0.12, edge * 0.35);
|
||||
|
||||
return float4(saturate(shaded), 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user