Created
July 7, 2025 20:23
-
-
Save jangxx/9b9e273c1640efa5ab3695da1907a6dd to your computer and use it in GitHub Desktop.
Circular Waveform ISF shader to be used in Magic Music Visuals
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*{ | |
"CREDIT": "by jangxx", | |
"DESCRIPTION": "Circular waveform visualizer. Needs an AudioToImage node as input.\n\nradius:\nDistance from center to the waveform.\n\nline width:\nWidth of the waveform line.\n\nwave size:\nHow much the waveform expands and contracts.\n\nsolid:\nIf true, the waveform is fully filled and mirrored around the zero line.\n\ncircumference:\nHow much of the circle is used for the waveform, 0 = 0%, 1 = 100%.\n\nrotation:\nrotation of the waveform around the center.", | |
"INPUTS": [ | |
{ | |
"LABEL": "input image", | |
"NAME": "input", | |
"TYPE": "image" | |
}, | |
{ | |
"DEFAULT": 1, | |
"LABEL": "radius", | |
"NAME": "circle_radius", | |
"TYPE": "float", | |
"MIN": 0 | |
}, | |
{ | |
"DEFAULT": 0.01, | |
"LABEL": "line width", | |
"NAME": "line_width", | |
"TYPE": "float", | |
"MIN": 0 | |
}, | |
{ | |
"DEFAULT": 0.1, | |
"LABEL": "wave size", | |
"NAME": "wave_size", | |
"TYPE": "float", | |
"MIN": 0 | |
}, | |
{ | |
"DEFAULT": false, | |
"LABEL": "solid", | |
"NAME": "solid", | |
"TYPE": "bool" | |
}, | |
{ | |
"DEFAULT": 1, | |
"LABEL": "cicumference", | |
"NAME": "circumference", | |
"TYPE": "float", | |
"MIN": 0, | |
"MAX": 1 | |
}, | |
{ | |
"DEFAULT": 0, | |
"LABEL": "rotation", | |
"NAME": "rotation", | |
"TYPE": "float", | |
} | |
], | |
"ISFVSN": "2" | |
} | |
*/ | |
#define PI 3.14159265359 | |
void main() { | |
float aspect = RENDERSIZE.x / RENDERSIZE.y; | |
vec2 uv_center = (isf_FragNormCoord - vec2(0.5, 0.5)) * 2.0 * vec2(aspect, 1); | |
float dist = length(uv_center); | |
float angle = mod((atan(uv_center.y, -uv_center.x) + PI) / (2.0 * PI) + rotation, 1.0) / circumference; | |
float waveform = (IMG_NORM_PIXEL(input, vec2(angle, 0.5)).r - 0.5) * 2.0; | |
if (0 <= angle && angle <= 1) { | |
if (solid) { | |
float wave_thickness = line_width + waveform*wave_size; | |
if (dist <= circle_radius + wave_thickness && dist >= circle_radius - wave_thickness) { | |
gl_FragColor = vec4(1,1,1,1); | |
} else { | |
gl_FragColor = vec4(0,0,0,1); | |
} | |
} else { | |
float wave_radius = circle_radius + waveform * wave_size; | |
if (dist <= wave_radius + line_width/2 && dist >= wave_radius - line_width/2) { | |
gl_FragColor = vec4(1,1,1,1); | |
} else { | |
gl_FragColor = vec4(0,0,0,1); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*{ | |
"CREDIT": "by jangxx", | |
"DESCRIPTION": "Circular waveform visualizer. Needs an AudioToImage node as input.\n\nradius:\nDistance from center to the waveform.\n\nline width:\nWidth of the waveform line.\n\nwave size:\nHow much the waveform expands and contracts.\n\nrotation:\nrotation of the waveform around the center.\n\nvertex count:\nHow many vertices are used to draw the waveform, more vertices = smoother curve.", | |
"INPUTS": [ | |
{ | |
"LABEL": "input image", | |
"NAME": "input", | |
"TYPE": "image" | |
}, | |
{ | |
"DEFAULT": 1, | |
"LABEL": "radius", | |
"NAME": "circle_radius", | |
"TYPE": "float", | |
"MIN": 0 | |
}, | |
{ | |
"DEFAULT": 0.01, | |
"LABEL": "line width", | |
"NAME": "line_width", | |
"TYPE": "float", | |
"MIN": 0 | |
}, | |
{ | |
"DEFAULT": 0.1, | |
"LABEL": "wave size", | |
"NAME": "wave_size", | |
"TYPE": "float", | |
"MIN": 0 | |
}, | |
{ | |
"DEFAULT": 0, | |
"LABEL": "rotation", | |
"NAME": "rotation", | |
"TYPE": "float", | |
}, | |
{ | |
"DEFAULT": 100, | |
"LABEL": "vertex count", | |
"NAME": "vertex_count", | |
"TYPE": "float", | |
} | |
], | |
"ISFVSN": "2" | |
} | |
*/ | |
#define PI 3.14159265359 | |
#define TWO_PI 6.28318530718 | |
#define HALF_PI 1.57079632679 | |
float angle(vec2 v1, vec2 v2) { | |
return acos(clamp(dot(v1, v2) / (length(v1) * length(v2)), -1.0, 1.0)); | |
} | |
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line | |
float point_line_dist(vec2 l1, vec2 l2, vec2 p) { | |
return abs((l2.x - l1.x) * (l1.y - p.y) - (l1.x - p.x) * (l2.y - l1.y)) / length(l2 - l1); | |
} | |
void draw_rounded_line_segment(inout vec4 color, vec2 uv, vec2 p1, vec2 p2, float width, vec3 line_color) { | |
vec2 dir = p2 - p1; | |
float line_dist = point_line_dist(p1, p2, uv); | |
// Check if point is near the line segment (excluding the rounded ends) | |
if (line_dist <= width && angle(dir, uv - p1) <= HALF_PI && angle(-dir, uv - p2) <= HALF_PI) { | |
color.rgb = line_color; | |
} | |
// Check if point is near the start cap | |
else if (length(p1 - uv) <= width) { | |
color.rgb = line_color; | |
} | |
// Check if point is near the end cap | |
else if (length(p2 - uv) <= width) { | |
color.rgb = line_color; | |
} | |
} | |
void draw_point(inout vec4 color, vec2 uv, vec2 point, float width, vec3 point_color) { | |
float dist = length(uv - point); | |
if (dist <= width) { | |
color.rgb = point_color; | |
color.a = 1.0; // clamp((1.0 - dist / width) * 20.0, 0.0, 1.0); | |
} | |
} | |
void main() { | |
float aspect = RENDERSIZE.x / RENDERSIZE.y; | |
vec2 uv_center = (isf_FragNormCoord - vec2(0.5, 0.5)) * 2.0 * vec2(aspect, 1); | |
float rot_radians = -rotation * TWO_PI; | |
float rot_sin = sin(rot_radians); | |
float rot_cos = cos(rot_radians); | |
mat2 rotation_matrix = mat2(rot_cos, -rot_sin, rot_sin, rot_cos); | |
vec2 rotated_uv = rotation_matrix * uv_center; | |
float angle = atan(rotated_uv.x, rotated_uv.y) / TWO_PI; | |
// angle = mod(angle, 1.0); | |
float vc = round(vertex_count); | |
float idx_left1 = floor(angle * vc); | |
float pos_left1 = idx_left1 / vc; | |
float idx_left2 = mod(idx_left1 - 1, vc); | |
float pos_left2 = idx_left2 / vc; | |
float idx_right1 = ceil(angle * vc); | |
float pos_right1 = idx_right1 / vc; | |
float idx_right2 = mod(idx_right1 + 1, vc); | |
float pos_right2 = idx_right2 / vc; | |
float val_left2 = (IMG_NORM_PIXEL(input, vec2(fract(pos_left2), 0.5)).r - 0.5) * 2.0; | |
float val_left1 = (IMG_NORM_PIXEL(input, vec2(fract(pos_left1), 0.5)).r - 0.5) * 2.0; | |
float val_right1 = (IMG_NORM_PIXEL(input, vec2(fract(pos_right1), 0.5)).r - 0.5) * 2.0; | |
float val_right2 = (IMG_NORM_PIXEL(input, vec2(fract(pos_right2), 0.5)).r - 0.5) * 2.0; | |
vec2 point_left2 = vec2(sin(pos_left2 * TWO_PI), cos(pos_left2 * TWO_PI)) * (circle_radius + val_left2 * wave_size); | |
vec2 point_left1 = vec2(sin(pos_left1 * TWO_PI), cos(pos_left1 * TWO_PI)) * (circle_radius + val_left1 * wave_size); | |
vec2 point_right1 = vec2(sin(pos_right1 * TWO_PI), cos(pos_right1 * TWO_PI)) * (circle_radius + val_right1 * wave_size); | |
vec2 point_right2 = vec2(sin(pos_right2 * TWO_PI), cos(pos_right2 * TWO_PI)) * (circle_radius + val_right2 * wave_size); | |
vec4 color = vec4(0,0,0,0); | |
draw_rounded_line_segment(color, rotated_uv, point_left2, point_left1, line_width/2, vec3(1, 1, 1)); | |
draw_rounded_line_segment(color, rotated_uv, point_left1, point_right1, line_width/2, vec3(1, 1, 1)); | |
draw_rounded_line_segment(color, rotated_uv, point_right1, point_right2, line_width/2, vec3(1, 1, 1)); | |
gl_FragColor = color; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment