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); }