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 4 of 10 · Fractals in GLSL

Smoothing

The steps melt into a continuous gradient.

Last lesson the count is an integer: 7 steps, 8 steps, 9. That is why you see stepped rings. But the escape holds more info: by how much it overshot the threshold on the final step. With that we interpolate between steps.

The magic formula: n - log2(log2(|z|²)). It sounds heavy, but it just turns the discrete jump into a number with decimals. The rings become silk.

sn = n - log2(log2(dot(z,z))) + 4.0The smooth iteration count. n is the integer escape step; the double log2 measures how far z flew as it crossed, filling the gap between one integer and the next. The +4.0 just re-seats the range.
TryRaise the second number in pow(g, 0.4) to 0.8 to darken the mids, or drop it to 0.2 to flatten the brightness. Push dot(z,z)>256.0 higher and the smoothing gets even cleaner.

The code

precision highp float;
void main(){
  vec2 uv=(gl_FragCoord.xy*2.0 - R)/R.y;
  float zoom=1.5;
  vec2 c=uv*zoom+vec2(-0.6,0.0);
  vec2 z=vec2(0.0); float n=0.0; bool esc=false;
  const int IT=140;
  for(int i=0;i<IT;i++){
    z=vec2(z.x*z.x-z.y*z.y, 2.0*z.x*z.y)+c;
    if(dot(z,z)>256.0){ n=float(i); esc=true; break; }
  }
  float sn=esc ? (n - log2(log2(dot(z,z))) + 4.0) : 0.0;
  float g=sn/float(IT);
  float shade=pow(g, 0.4);
  vec3 col=esc ? mix(vec3(0.03,0.04,0.09), vec3(0.95,0.98,1.0), shade) : vec3(0.01,0.01,0.03);
  gl_FragColor=vec4(col,1.0);
}
close