Using Polar Coordinates in WebGL Shaders to Create Complex Patterns
This article demonstrates how to convert Cartesian coordinates to polar coordinates in GLSL, providing reusable shader functions and examples that generate a variety of intriguing shapes such as blades, clovers, buds, flowers, gourds, apples, and infinity symbols, while encouraging experimentation with parameters.
Continuing from the previous post "[Zero‑Basis] Fully Understanding WebGL (Part 4)", this article introduces the use of polar coordinates in WebGL fragment shaders to create a range of interesting visual patterns.
Polar Coordinates
Earlier drawings used the standard Cartesian coordinate system; by applying a coordinate transformation we can convert the system to polar coordinates.
vec2 polar(vec2 st, vec2 c) {
vec2 p = c - st;
float r = length(p) * 2.0;
float a = atan(p.y, p.x);
return vec2(r, a);
}The function above maps a point vec2(0.5) (the center of the canvas) to the polar origin, enabling us to draw circles and more complex shapes.
float shape_blade(vec2 st, vec2 center, float num) {
vec2 pt = polar(st, vec2(center));
float x = pt.x;
float y = cos(pt.y * num);
return smoothstep(x - 0.01, x + 0.01, y);
} float shape_clover(vec2 st, vec2 center, float num) {
vec2 pt = polar(st, vec2(center));
float x = pt.x;
float y = abs(cos(pt.y * num * 0.5));
return smoothstep(x - 0.01, x + 0.01, y);
} float shape_bud(vec2 st, vec2 center, float num) {
vec2 pt = polar(st, vec2(center));
float x = pt.x;
float y = smoothstep(-0.5, 1.0, cos(pt.y * num)) * 0.2 + 0.5;
return smoothstep(x - 0.01, x + 0.01, y);
} float shape_flower(vec2 st, vec2 center, float num) {
vec2 pt = polar(st, vec2(center));
float x = pt.x;
float y = abs(cos(pt.y * num * 0.5)) * 0.5 + 0.3;
return smoothstep(x - 0.01, x + 0.01, y);
}
float shape_gourd(vec2 st, vec2 center) {
return shape_flower(vec2(st.y, st.x), center, 1.7);
}
float shape_apple(vec2 st, vec2 center) {
return shape_clover(vec2(st.y - 0.2, st.x), center, 1.3);
}
float shape_infinity(vec2 st, vec2 center) {
return shape_blade(st, center, 2.0);
}These functions can be combined and animated to produce a variety of decorative graphics, as shown in the accompanying images.
void main() {
vec2 st = gl_FragCoord.xy / resolution;
vec2 pt = polar(st, vec2(0.5));
pt = fract(pt * sin(count) * 10.0);
float x = pt.x;
float y = abs(cos(pt.y * 3.0 * 0.5)) * 0.5 + 0.3;
float d = smoothstep(x - 0.01, x + 0.01, y);
FragColor.rgb = stroke(1.0 - d, 0.0, 1.0, 0.05) * vec3(1.0);
FragColor.a = 1.0;
}The final fragment shader demonstrates how a simple Cartesian‑to‑polar conversion, combined with repetition and smoothstep, yields rich, animated patterns. Readers are encouraged to tweak the parameters of the examples to discover new visual effects and share their results.
This section focuses on visual demonstration; the theory is straightforward—just a coordinate conversion—but the resulting patterns are surprisingly diverse and fun to explore.
ByteFE
Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.