Posts

Gradients, Distance, and Why Shaders Start With a Line

0 comments·0 reblogs
hey2d
25
·
0 views
·
min-read

Most people think shaders begin with shapes, or light, or noise.

But they don’t.

They begin with a line, and what you choose to do with distance along that line.

This code, pulled from The Book of Shaders, is one of those quiet lessons that looks simple on the surface but carries the entire idea of procedural graphics underneath it.

Not because it’s complex, but because it teaches you what a gradient really is.
Image from thread


We start with the usual inputs:

uniform vec2 u_resolution; 
uniform vec2 u_mouse; 
uniform float u_time; 

Only one of these matters for now: u_resolution.

Because everything begins by normalizing space.

vec2 st = gl_FragCoord.xy / u_resolution.xy; 

This is the first real shift in thinking.

Instead of pixels, we now live in a 0 to 1 coordinate system.

Left to right becomes 0 to 1.
Bottom to top becomes 0 to 1.

And just like that, distance becomes readable.


Now the colors:

vec3 colorA = vec3(0.149,0.141,0.912); 
vec3 colorB = vec3(1.000,0.833,0.224); 

Two endpoints again.
One cold, one warm.
A simple contrast that lets distance become visible.
Image from thread


The core idea sits here:

vec3 pct = vec3(st.x); 

We are taking only the X-axis — left to right — and stretching it into RGB space.

That means:

  • Red follows X
  • Green follows X
  • Blue follows X

So the entire image becomes a single gradient line, repeated across channels.

Even before effects, this is already the foundation of most shader transitions: a value derived from position.


Then the actual blending:

color = mix(colorA, colorB, pct); 

This is where the gradient is born.

mix() doesn’t pick a color. It interpolates between two states based on distance.

At the left edge, st.x = 0, so you see colorA.
At the right edge, st.x = 1, so you see colorB.
Everything in between is a smooth transition.

No edges. No steps. Just continuous change.


But the interesting part isn’t the gradient itself — it’s how we see the gradient.

That’s where the plot function comes in:

float plot (vec2 st, float pct){ 
  return smoothstep(pct-0.01, pct, st.y) - 
         smoothstep(pct, pct+0.01, st.y); 
} 

This tiny function draws a thin line on top of the gradient.

Not to decorate it — but to reveal structure.

It compares st.y (vertical position) against a value, and uses smoothstep to carve out a soft band.

What it’s really doing is showing “where something equals something else.”

That idea becomes important later in everything: curves, masks, fields, physics simulations.


Then the visualization:

color = mix(color, vec3(1.0,0.0,0.0), plot(st,pct.r)); 
color = mix(color, vec3(0.0,1.0,0.0), plot(st,pct.g)); 
color = mix(color, vec3(0.0,0.0,1.0), plot(st,pct.b)); 

Each channel gets its own highlight:

  • Red line shows red channel gradient
  • Green line shows green channel gradient
  • Blue line shows blue channel gradient

This is less about aesthetics and more about debugging perception — seeing how each value behaves across space.

You start realizing something simple but powerful:

A shader is just stacked interpretations of the same coordinate system.


Finally:

gl_FragColor = vec4(color,1.0); 

The image appears.

But what you’re really looking at isn’t a picture.

It’s a measurement of distance turned into color.

And that’s the quiet truth behind gradients in GLSL.

They aren’t decoration. They are structure.

Once you understand that, everything else — noise, distortion, fields, animation — becomes just gradients that stopped behaving linearly.

Even in an age where AI can generate visuals instantly, this still matters.

Because knowing how a gradient works is not about writing shader code.

It’s about understanding how space becomes something you can control.

Posted Using INLEO