example data store
This commit is contained in:
66
shaders/feedback-data-blocks/shader.json
Normal file
66
shaders/feedback-data-blocks/shader.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"id": "feedback-data-blocks",
|
||||
"name": "Feedback Data Blocks",
|
||||
"description": "Demonstrates using the feedback surface as coarse data storage by reserving eight 3x3 texel cells for sampled colors and a hidden metadata cell for timed or trigger-driven refresh state.",
|
||||
"category": "Feedback",
|
||||
"entryPoint": "storeProbeData",
|
||||
"passes": [
|
||||
{
|
||||
"id": "store",
|
||||
"source": "shader.slang",
|
||||
"entryPoint": "storeProbeData",
|
||||
"output": "dataBuffer"
|
||||
},
|
||||
{
|
||||
"id": "display",
|
||||
"source": "shader.slang",
|
||||
"entryPoint": "displayProbeData",
|
||||
"inputs": [
|
||||
"dataBuffer"
|
||||
],
|
||||
"output": "layerOutput"
|
||||
}
|
||||
],
|
||||
"feedback": {
|
||||
"enabled": true,
|
||||
"writePass": "store"
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"id": "refresh",
|
||||
"label": "Refresh",
|
||||
"type": "trigger",
|
||||
"description": "Forces the stored probe colors to resample immediately."
|
||||
},
|
||||
{
|
||||
"id": "refreshSeconds",
|
||||
"label": "Refresh Seconds",
|
||||
"type": "float",
|
||||
"default": 15.0,
|
||||
"min": 1.0,
|
||||
"max": 60.0,
|
||||
"step": 0.1,
|
||||
"description": "Automatic interval for resampling all stored probe colors."
|
||||
},
|
||||
{
|
||||
"id": "overlayOpacity",
|
||||
"label": "Overlay Opacity",
|
||||
"type": "float",
|
||||
"default": 1.0,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"step": 0.01,
|
||||
"description": "Strength of the swatch overlay drawn from the stored data cells."
|
||||
},
|
||||
{
|
||||
"id": "swatchSize",
|
||||
"label": "Swatch Size",
|
||||
"type": "vec2",
|
||||
"default": [0.045, 0.055],
|
||||
"min": [0.02, 0.02],
|
||||
"max": [0.12, 0.12],
|
||||
"step": [0.001, 0.001],
|
||||
"description": "Size of the top-left preview swatches that show the stored cell values."
|
||||
}
|
||||
]
|
||||
}
|
||||
152
shaders/feedback-data-blocks/shader.slang
Normal file
152
shaders/feedback-data-blocks/shader.slang
Normal file
@@ -0,0 +1,152 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user