Documentation/Source example
Example source
samples/vis/Shader - Feedback and Two Posts.js
Referenced by:vis.CreateAsync
include('docs/Flags.js');
window.DrawMode = 1;
// Demonstrates an image-shader chain over the native waveform source.
// Click to switch between source-only rendering, accumulated feedback,
// glow post-processing and a second color/vignette post pass.
// The feedback and post shaders sample images from t0/s0, not audio PCM.
// Re-creating the visualization when switching modes resets its history.
vis.UpdateIntervalMs = 16; // Default: 16 ms; allowed: unsigned integer 1..4294967295 ms.
const sourceShader = `
Texture2D<float> Waveform : register(t0);
cbuffer VisConstants : register(b0) {
float2 viewportSize;
float time;
float pointCount;
};
struct PSInput { float4 clipSpacePos:SV_POSITION; float4 sceneSpacePos:SCENE_POS; };
float4 main(PSInput p) : SV_Target {
float2 sz = max(viewportSize, float2(1.0, 1.0));
int count = max(1, (int)pointCount);
int index = clamp((int)(p.sceneSpacePos.x / sz.x * count), 0, count - 1);
float value = Waveform.Load(int3(index, 0, 0)).r;
float targetY = (1.0 - value) * 0.5 * sz.y;
float opacity = (1.0 - smoothstep(0.2, 1.8,
abs(p.sceneSpacePos.y - targetY))) * saturate(abs(value) * 8.0);
return float4(0.10, 0.85, 0.72, 1.0) * opacity; // Premultiplied; no silence floor.
}`;
const feedbackShader = `
Texture2D Previous : register(t0);
SamplerState InputSampler : register(s0);
cbuffer ImageConstants : register(b0) {
float2 viewportSize;
float time;
float frameStep;
};
struct PSInput {
float4 clipSpacePos:SV_POSITION;
float4 sceneSpacePos:SCENE_POS;
float4 texelSpacePos:TEXEL_POS;
};
float4 main(PSInput p) : SV_Target {
float2 px = 1.0 / max(viewportSize, float2(1.0, 1.0));
float2 uv = p.texelSpacePos.xy - float2(0.0, 0.7) * px * frameStep;
if (any(uv < 0.0) || any(uv > 1.0)) return float4(0, 0, 0, 1);
float3 prev = Previous.SampleLevel(InputSampler, uv, 0.0).rgb;
return float4(prev * pow(0.94, frameStep), 1.0);
}`;
const glowShader = `
Texture2D Frame : register(t0);
SamplerState InputSampler : register(s0);
cbuffer ImageConstants : register(b0) {
float2 viewportSize;
float time;
float frameStep;
};
struct PSInput {
float4 clipSpacePos:SV_POSITION;
float4 sceneSpacePos:SCENE_POS;
float4 texelSpacePos:TEXEL_POS;
};
float4 main(PSInput p) : SV_Target {
float2 uv = p.texelSpacePos.xy;
float2 px = 2.0 / max(viewportSize, float2(1.0, 1.0));
float3 core = Frame.SampleLevel(InputSampler, uv, 0.0).rgb;
float3 halo = (
Frame.SampleLevel(InputSampler, uv + float2(px.x, 0), 0).rgb +
Frame.SampleLevel(InputSampler, uv - float2(px.x, 0), 0).rgb +
Frame.SampleLevel(InputSampler, uv + float2(0, px.y), 0).rgb +
Frame.SampleLevel(InputSampler, uv - float2(0, px.y), 0).rgb +
Frame.SampleLevel(InputSampler, uv + px, 0).rgb +
Frame.SampleLevel(InputSampler, uv - px, 0).rgb +
Frame.SampleLevel(InputSampler, uv + float2(px.x, -px.y), 0).rgb +
Frame.SampleLevel(InputSampler, uv + float2(-px.x, px.y), 0).rgb
) * 0.125;
return float4(saturate(max(core, halo * 1.7)), 1.0);
}`;
const colorShader = `
Texture2D Frame : register(t0);
SamplerState InputSampler : register(s0);
cbuffer ImageConstants : register(b0) {
float2 viewportSize;
float time;
float frameStep;
};
struct PSInput {
float4 clipSpacePos:SV_POSITION;
float4 sceneSpacePos:SCENE_POS;
float4 texelSpacePos:TEXEL_POS;
};
float4 main(PSInput p) : SV_Target {
float3 rgb = Frame.SampleLevel(InputSampler, p.texelSpacePos.xy, 0.0).rgb;
float intensity = max(rgb.r, max(rgb.g, rgb.b));
float2 q = (p.sceneSpacePos.xy - 0.5 * viewportSize) /
max(viewportSize, float2(1.0, 1.0));
float vignette = 1.0 - 0.55 * saturate(dot(q, q) * 2.0);
return float4(saturate(float3(1.0, 0.20, 0.68) * intensity * vignette), 1.0);
}`;
const labels = [
"0: source only (cyan)",
"1: source + feedback (trails)",
"2: feedback + glow (wide halos)",
"3: feedback + glow + color (magenta)"
];
const font = d2d.Font("Segoe UI", 14);
let stage = 3;
let visual = null;
let requestId = 0;
function rebuild() {
const currentRequest = ++requestId;
const render = { shader: sourceShader };
if (stage >= 1) render.feedback = feedbackShader;
if (stage >= 2) render.post = stage === 2 ? glowShader : [glowShader, colorShader];
vis.CreateAsync(VisSourceType.Waveform, null, render)
.then(value => {
if (currentRequest !== requestId) return;
visual = value;
window.Repaint();
})
.catch(error => console.log("Shader chain: " + error));
}
rebuild();
function on_mouse_lbtn_up() {
stage = (stage + 1) % labels.length;
visual = null;
rebuild();
}
function on_paint(gr) {
const w = window.Width, h = window.Height;
gr.FillSolidRect(0, 0, w, h, 0xff000000);
if (visual && w > 0 && h > 0) visual.Draw(gr, 0, 0, w, h);
gr.DrawString(labels[stage] + " | click to switch", font, 0xffbdbdbd,
10, 8, Math.max(w - 20, 0), 25);
}
setInterval(() => window.Repaint(), 33);