Files
video-shader-toys/shaders/feedback-data-blocks/shader.slang
Aiden 8fcb51d140
All checks were successful
CI / React UI Build (push) Successful in 11s
CI / Native Windows Build And Tests (push) Successful in 2m24s
CI / Windows Release Package (push) Successful in 2m34s
example data store
2026-05-10 21:11:17 +10:00

153 lines
4.8 KiB
Plaintext

static const int kProbeCount = 8;
static const int kMetadataIndex = 8;
float2 probeUvForIndex(int index)
{
if (index == 0)
return float2(0.18, 0.28);
if (index == 1)
return float2(0.39, 0.28);
if (index == 2)
return float2(0.61, 0.28);
if (index == 3)
return float2(0.82, 0.28);
if (index == 4)
return float2(0.18, 0.72);
if (index == 5)
return float2(0.39, 0.72);
if (index == 6)
return float2(0.61, 0.72);
return float2(0.82, 0.72);
}
float2 cellCenterPixelForIndex(int index)
{
return float2(1.0 + float(index) * 3.0, 1.0);
}
float2 cellCenterUvForIndex(ShaderContext context, int index)
{
return (cellCenterPixelForIndex(index) + 0.5) / context.outputResolution;
}
bool pixelIsInsideCell(float2 pixelCoord, int index)
{
float minX = float(index) * 3.0;
float maxX = minX + 3.0;
return pixelCoord.x >= minX && pixelCoord.x < maxX && pixelCoord.y >= 0.0 && pixelCoord.y < 3.0;
}
float4 readStoredCell(ShaderContext context, int index)
{
if (context.feedbackAvailable <= 0)
return float4(0.0, 0.0, 0.0, 0.0);
return sampleFeedback(cellCenterUvForIndex(context, index));
}
bool shouldRefreshStoredData(ShaderContext context)
{
if (context.feedbackAvailable <= 0)
return true;
float4 metadata = readStoredCell(context, kMetadataIndex);
float previousRefreshBucket = metadata.r;
float previousTriggerCount = metadata.g;
float refreshInterval = max(refreshSeconds, 0.001);
float currentRefreshBucket = floor(context.time / refreshInterval);
float currentTriggerCount = float(refresh);
return currentRefreshBucket > previousRefreshBucket + 0.5 || currentTriggerCount > previousTriggerCount + 0.5;
}
float4 metadataValueForFrame(ShaderContext context, bool refreshNow)
{
float refreshInterval = max(refreshSeconds, 0.001);
float currentRefreshBucket = floor(context.time / refreshInterval);
float currentTriggerCount = float(refresh);
if (!refreshNow && context.feedbackAvailable > 0)
return readStoredCell(context, kMetadataIndex);
return float4(currentRefreshBucket, currentTriggerCount, refreshTime, 1.0);
}
float4 storedProbeValueForFrame(ShaderContext context, int index, bool refreshNow)
{
float3 liveColor = sampleLayerInput(probeUvForIndex(index)).rgb;
if (refreshNow || context.feedbackAvailable <= 0)
return float4(liveColor, 1.0);
return readStoredCell(context, index);
}
float4 storeProbeData(ShaderContext context)
{
// Reserve nine 3x3 texel cells along the top edge of the feedback surface:
// eight cells for visible probe colors and one hidden metadata cell that
// tracks the timed refresh bucket and last trigger count.
float2 pixelCoord = floor(context.uv * context.outputResolution);
bool refreshNow = shouldRefreshStoredData(context);
for (int index = 0; index < kProbeCount; ++index)
{
if (pixelIsInsideCell(pixelCoord, index))
return storedProbeValueForFrame(context, index, refreshNow);
}
if (pixelIsInsideCell(pixelCoord, kMetadataIndex))
return metadataValueForFrame(context, refreshNow);
return float4(0.0, 0.0, 0.0, 1.0);
}
float rectMask(float2 uv, float2 minUv, float2 maxUv)
{
if (uv.x < minUv.x || uv.x > maxUv.x)
return 0.0;
if (uv.y < minUv.y || uv.y > maxUv.y)
return 0.0;
return 1.0;
}
float borderMask(float2 uv, float2 minUv, float2 maxUv, float thickness)
{
float outer = rectMask(uv, minUv, maxUv);
float inner = rectMask(uv, minUv + thickness, maxUv - thickness);
return saturate(outer - inner);
}
float4 displayProbeData(ShaderContext context)
{
float3 baseColor = sampleLayerInput(context.uv).rgb;
float3 swatchColor = baseColor;
float swatchMask = 0.0;
float2 panelOrigin = float2(0.03, 0.04);
float2 gap = float2(swatchSize.x + 0.012, swatchSize.y + 0.012);
float borderThickness = min(swatchSize.x, swatchSize.y) * 0.08;
for (int index = 0; index < kProbeCount; ++index)
{
int column = index % 4;
int row = index / 4;
float2 swatchMin = panelOrigin + float2(float(column) * gap.x, float(row) * gap.y);
float2 swatchMax = swatchMin + swatchSize;
float3 storedColor = sampleVideo(cellCenterUvForIndex(context, index)).rgb;
float fill = rectMask(context.uv, swatchMin, swatchMax);
float outline = borderMask(context.uv, swatchMin, swatchMax, borderThickness);
if (fill > 0.5)
{
swatchColor = storedColor;
swatchMask = 1.0;
}
if (outline > 0.5)
{
swatchColor = float3(0.0, 0.0, 0.0);
swatchMask = 1.0;
}
}
float opacity = saturate(overlayOpacity) * swatchMask;
float3 displayColor = lerp(baseColor, swatchColor, opacity);
return float4(saturate(displayColor), 1.0);
}