learn GLSL from zero
ToolsCreate with shadersShader MakerFree online shader builderToolkitShader works ready for your siteSketcher LabPaint your landing’s hero with shaders3D StudioThe Three.js editor in your browser
GamesPlay with shadersOnomatoShaderType a word, blow it upThe WatcherAn eye that follows youAetherLiquid glass with an eye insideBloomcoreA word grows a garden of lightPyrokineThrow fire at the wallVoltaicThe pyramid throws lightningSurfSurf a shader ocean

Lesson 1 of 10 · Shader patterns

Shapes

Rings, squares, diamonds. Each shape is a distance equation.

In Start Here every pixel asked what color am I? A shape answers a sharper question: how far am I from this edge?

Before measuring distances, a new tool: M. It is a vec2 that always holds the mouse position, normalized between 0 and 1, it comes from the browser and updates every frame. In the shader we translate it to the same space as p with one line:

vec2 m = (2.*M*R - R) / min(R.x, R.y);

Now m lives in the same world as p, the cursor is just another coordinate, ready to enter the equations.

Each shape is a distance function. length(p) - .65 is positive outside the ring, negative inside, and exactly zero on the edge. Same for the square and the diamond, only the way of measuring changes.

One equation. One shape. Three rulers for distance.

M and mM comes from the browser, the mouse in normalized coordinates (0 to 1). m is M translated to the same space as p, where screen center is (0,0). That formula (2.*M*R - R) / min(R.x, R.y) you will see in every lesson with interaction.
The distance trickThe ring measures with length(p), the straight ruler from center. The square measures with max, largest coordinate wins. The diamond measures with +, sum of both coordinates. Three rulers, three geometries.
TryMove the mouse in and out. Each shape warms up when your cursor crosses its edge, distance is continuous, not binary. Change .65 on the ring to .85 and the ring drifts away from center.

The code

void main(){
  vec2 p = (2.*gl_FragCoord.xy - R) / min(R.x, R.y);
  vec2 m = (2.*M*R - R) / min(R.x, R.y);

  vec3 col = vec3(0.025, 0.018, 0.035);

  float dRing    = abs(length(p) - .65);
  vec2  sq       = abs(p) - .42;
  float dSquare  = max(sq.x, sq.y);
  float dDiamond = abs(p.x) + abs(p.y) - .25;

  float hRing    = exp(-abs(length(m) - .65) * 3.);
  vec2  msq      = abs(m) - .42;
  float hSquare  = exp(-abs(max(msq.x, msq.y)) * 3.);
  float hDiamond = exp(-abs(abs(m.x) + abs(m.y) - .25) * 3.);

  vec3 cRing    = vec3(.98, .55, .12);
  vec3 cSquare  = vec3(.85, .25, .08);
  vec3 cDiamond = vec3(.96, .80, .28);

  col += cRing    * smoothstep(.018, 0., abs(dRing))
       + cRing    * exp(-abs(dRing)    * 5.) * (.30 + hRing    * .9);
  col += cSquare  * smoothstep(.018, 0., abs(dSquare))
       + cSquare  * exp(-abs(dSquare)  * 5.) * (.30 + hSquare  * .9);
  col += cDiamond * smoothstep(.018, 0., abs(dDiamond))
       + cDiamond * exp(-abs(dDiamond) * 5.) * (.30 + hDiamond * .9);

  gl_FragColor = vec4(col, 1.);
}
close