Skip to content

Commit cad9716

Browse files
committed
2 parents 4334ea2 + bcaa208 commit cad9716

File tree

7 files changed

+210
-0
lines changed

7 files changed

+210
-0
lines changed

abstract-dan/README.md

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# Circles of light
2+
Ho ho, shader friends!
3+
Today, I have a Christmas tale to share—a tale of circles.
4+
Not just any circles, but circles that begin as humble shapes and evolve into radiant, magical rings of light.
5+
But let’s not skip ahead.
6+
The story begins with me agreeing to write this article about shader coding.
7+
“Me? Is this really a good idea?” I wondered, considering I’m still a beginner in the world of shaders?
8+
Days passed, the deadline loomed, and progress... well, it didn’t.
9+
10+
One evening, in a fit of procrastination, I lazily searched YouTube for shader tutorials.
11+
That’s when I stumbled upon [An Introduction to Shader Art Coding by Kishimisu](https://www.youtube.com/watch?v=f4s1h2YETNY).
12+
Wow. Starting with the simplest of circles,
13+
Kishimisu used ingenious techniques to transform them into dazzling, animated works of art.
14+
15+
Inspired, I thought, “Could I pull off something similar but with my own twist?”
16+
17+
## The basic idea
18+
19+
It turns out that it is very simple to draw a circle using a shader.
20+
21+
```glsl
22+
void mainImage( out vec4 fragColor, in vec2 fragCoord )
23+
{
24+
vec2 uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y;
25+
vec2 center = vec2(0.0,0.0);
26+
float radius = 0.7;
27+
28+
// Calculate distance to circle. Use smoothstep
29+
// to get smooth edges.
30+
float dist =
31+
smoothstep(
32+
0.0,
33+
0.05,
34+
abs(length(uv + center) - radius)
35+
);
36+
37+
fragColor = vec4(dist, dist, dist, 1.0);
38+
}
39+
```
40+
41+
<img src="assets/single circle.png" width="250px">
42+
43+
A single circle is not interesting enough though but what about a circles that contains other circles?
44+
45+
If $r_n$ is the radius of the outer circle and $c_n$ is the center we can calculate the radius of the inner circle by multiplying by a number less than one, for example
46+
47+
$r_{n+1} = r_{n} * 0.8$
48+
49+
If we want the inner circle to touch the outer, the new center will be
50+
51+
$c_{n+1} = c_n + (r_{n} - r_{n+1}) \times [cos(\alpha), sin(\alpha)]$
52+
53+
where $\alpha$ is the rotation of the inner circle around the center of the outer. Let's try that out.
54+
55+
```glsl
56+
void mainImage( out vec4 fragColor, in vec2 fragCoord )
57+
{
58+
vec2 uv = ...;
59+
60+
float r_0 = ...;
61+
vec2 c_0 = ...;
62+
float dist_0 = smoothstep(...);
63+
64+
float r_1 = r_0 * 0.8;
65+
float alpha = ...; // Arbitrary rotation angle
66+
vec2 c_1 = c_0 + (r_0 - r_1) * vec2(cos(alpha), sin(alpha));
67+
float dist_1 = smoothstep(...);
68+
69+
// The combined distance is the minimum distance of the parts
70+
float dist = min(dist_0, dist_1);
71+
72+
fragColor = ...;
73+
}
74+
```
75+
76+
<img src="assets/two_nested_circles.png" width="300px" />
77+
78+
79+
Using a for loop we can create as many as we want.
80+
81+
```glsl
82+
void mainImage( out vec4 fragColor, in vec2 fragCoord )
83+
{
84+
vec2 uv = ...;
85+
86+
float ro = ...;
87+
vec2 co = ...;
88+
float dist = ...;
89+
90+
for(float i=1.0; i<5.0; i++)
91+
{
92+
float ri = ...;
93+
float alpha = ...;
94+
vec2 ci = ...;
95+
float disti = ...;
96+
dist = min(disti, dist);
97+
98+
ro = ri;
99+
co = ci;
100+
}
101+
102+
fragColor = vec4(dist, dist, dist, 1.0);
103+
}
104+
```
105+
106+
<img src="assets/many_nested_circles.png" width="250px" />
107+
108+
Still no magic but maybe it could be interesting enough using some tricks?
109+
110+
## Trick 1: Animation
111+
112+
It is easy to animate by letting the rotation $\alpha$ vary by time and circle index.
113+
114+
```glsl
115+
float alpha = <arbitrary constant> * i * iTime;
116+
```
117+
118+
## Trick 2: A nice color palette
119+
120+
Using the general palette function from https://iquilezles.org/articles/palettes/ combined with Kishimisu's parameter selection I got this
121+
122+
```glsl
123+
vec3 palette( in float t )
124+
{
125+
vec3 a = vec3(0.5, 0.5, 0.5);
126+
vec3 b = vec3(0.5, 0.5, 0.5);
127+
vec3 c = vec3(1.0, 1.0, 1.0);
128+
vec3 d = vec3(0.263, 0.416, 0.557);
129+
return a + b*cos( 6.283185*(c*t+d) );
130+
}
131+
132+
void mainImage( out vec4 fragColor, in vec2 fragCoord )
133+
{
134+
...
135+
136+
vec3 color =
137+
palette(
138+
length(uv) + iTime * <arbitrary constant>
139+
) * (1.0 - dist);
140+
fragColor = vec4(color, 1.0);
141+
}
142+
```
143+
144+
Again, time is used to animate the effect.
145+
146+
## Trick 3: Color saturation overload
147+
148+
A very simple and cheap trick to get more saturated colors seems to be to subract a little. I do not know why it works but I tried, and I liked the result.
149+
150+
```glsl
151+
dist -= 0.5; // Magic: Subtract a little for more saturation
152+
vec3 color = palette(...) * (1.0 - dist);
153+
```
154+
155+
## Trick 4: Warp the plane by a pinch of noise
156+
157+
At this point the result look as below.
158+
159+
<img src="assets/without-noise.png" width="300px" />
160+
161+
Colorful, but a bit boring. What if I could warp the plane with some noise to make it more interesting?
162+
163+
Searching for noise effects on shadertoy.com did not disappoint. I copied to noise function from [Warping - procedural 2](https://www.shadertoy.com/view/lsl3RH).
164+
165+
```glsl
166+
const mat2 m = mat2( 0.80, 0.60, -0.60, 0.80 );
167+
168+
float noise( in vec2 p )
169+
{
170+
return sin(p.x)*sin(p.y);
171+
}
172+
173+
float fbm4( vec2 p )
174+
{
175+
float f = 0.0;
176+
f += 0.5000*noise( p ); p = m*p*2.02;
177+
f += 0.2500*noise( p ); p = m*p*2.03;
178+
f += 0.1250*noise( p ); p = m*p*2.01;
179+
f += 0.0625*noise( p );
180+
return f/0.9375;
181+
}
182+
183+
vec2 fbm4_2( vec2 p )
184+
{
185+
return vec2(fbm4(p), fbm4(p+vec2(7.8)));
186+
}
187+
```
188+
189+
Using this noise function it was just a matter of mixing it with the uv-coordinates.
190+
191+
```glsl
192+
vec2 uv = ...;
193+
float warpAmount = 0.75; // rather arbitrary
194+
uv = mix(
195+
fbm4_2(uv + iTime * <arbitrary constant>),
196+
uv,
197+
warpAmount
198+
);
199+
```
200+
201+
The result looked like below.
202+
203+
<img src="assets/with-noise.png" width="300px" />
204+
205+
# Summary
206+
207+
The final shader, after some tweaking of the constants, can be seen [here](https://www.shadertoy.com/view/McdcWM).
208+
With only a basic understanding and a few simple concepts, I managed to create a vibrant, colorful effect. Sure, it carries the mark of a beginner, but it’s mine—and it’s unique. It feels like there’s an entire universe of undiscovered shaders waiting to be explored. Good times. Wishing you a bright and creative St. Lucia’s Day—and a joyful Advent!
209+
210+
*Abstract Dan, 2024*

abstract-dan/assets/idea.png

1.3 MB
Loading
58.1 KB
Loading

abstract-dan/assets/single circle.png

19.8 KB
Loading
33.1 KB
Loading

abstract-dan/assets/with-noise.png

258 KB
Loading

abstract-dan/assets/without-noise.png

260 KB
Loading

0 commit comments

Comments
 (0)