Last year I read an article describing the mathematical structure of Escher’s Print Gallery. Escher’s Print Gallery (de Prentententoonstelling in Dutch) shows a man standing in an exhibition gallery, viewing a print of a seaport. As his eyes follow the buildings shown on the print clockwise, he discovers among them the very same gallery in which he is standing.

The Droste effect used in the Print Gallery is special: the image is mapped in a spiral. I decided to create a webgl fragment shader to visualise the mathematics of the article myself. Instead of applying the transformation to a two dimensional image, I ended up with applying the transformation to a 3D model of Escher’s image. My shader toggles between the ‘normal’ Droste effect of an image-in-image-in-image-..  and the ‘spiral’ Droste effect used by Escher.


Animation of Escher’s Droste effect.

Because the webgl fragment shader is really heavy, some browsers will crash by running the shader. Therefore I captured the output of the shader in the movie embedded above. You can find the realtime version, with code/maths open for you to explore, here: https://www.shadertoy.com/view/Mdf3zM.

How does it work?

First I started by generating a ‘normal’ Droste image of a 3D model of Escher’s image using a fragment shader. The 3D model contains a small (Mediterranean?) city with different buildings. One of the buildings is the print gallery. In this gallery you can find a print of the same small city, which gives rise to the Droste effect. The size of the print is 1/256 of the size of an image of the total city.

droste_1

The fragment shader generates the image above by raymarching a distance field of a 3D model of the city for each pixel. You can find an excellent article about ray marching distance fields by Iñigo Quilez here.

When you generate an image by ray marching, you have to construct a (3D) ray for each pixel of your image (just like by ray casting). With the constructed ray you march a distance field to find the first object the ray will hit. Shading this object at the intersection point will give you a color for each pixel.

To get the Droste image above, first all pixel coordinates uv are scaled to [-1,1]. If |u| or |v| is bigger than 1, uv is divided by 256.

After that a ray is constructed for each coordinate uv. If this ray hits the print in the gallery, the original coordinate uv is multiplied by 256:

if( hitDrostePicture(uv) ) uv*=256.;
if( hitDrostePicture(uv) ) uv*=256.;

The position of the gallery, and the view direction, are carefully chosen to ensure the print in the gallery is exactly in the center of the screen (coordinate 0,0). By multiplying the coordinates for rays hitting the print by 256, and constructing new rays for these multiplied coordinates, the Droste image shown above can be generated by raymarching the distance field of the city using the newly constructed rays.

Escher’s domain transformation

Given the setup described, Escher’s ‘spiral’ Droste effect can be replicated by applying a domain transformation to the initial coordinate uv:

h(w) = wα = w^((2πi+log scale)/(2πi))  (w = u + iv)

.
Or,  as coded in glsl in the fragment shader:

vec2 escherDeformation( in vec2 uv ) {
	float lnr = log(length(uv));
	float th = atan( uv.y, uv.x )+(0.4/256.)*deformationScale;
	float sn = -log(deformationScale)*(1./(2.*3.1415926));
	float l = exp( lnr - th*sn ); 
	
	vec2 ret = vec2( l );
	
	ret.x *= cos( sn*lnr+th );
	ret.y *= sin( sn*lnr+th );
		
	return ret;
}

In the fragment shader, the variable deformationScale automatically changes in time between values 1 and 256, which results in an animation between the original Droste image, and the ‘warped’ spiral Droste effect as used by Escher.

droste_2 droste_3

Fragment shader

The shader shown is only a fragment shader, rendered using two triangles. So no 3D models, textures or any other external sources are used.

You can find (the full source of) the fragment shader on shadertoy: https://www.shadertoy.com/view/Mdf3zM.

 

Escher and the Droste effect – webgl fragment shader