Tuesday, January 11, 2011

Tunnel effect explained in WebGL





The tunnel effect builds on the circle equation we covered in the introductory XOR bitplasma. However, this time we need to add a two more concepts. First, we need to calculate the angle of each point on the screen, relative to the origin (center of screen). To find this we can use the properties of right-angle triangles, namely the tangent function (tan). That is, the tangent of the angle is equal to opposite over adjacent.

Visualization of the angle for each pixel
By plugging in our pixel x/y coordinates we get a range from zero to pi of angles. Dividing by pi gives us a range from zero to one. In code this is:

float a = atan(p.y,p.x);
result = a/(3.1416);


Tunnel depth
Second, we need to find the "depth" of each point in the tunnel. Luckily this is easy to do, as projecting a 3d point to a 2d viewer is just a "divide by z" operation (objects that are further away appear smaller than objects that are closer - a nicer explanation will follow in future posts). So we can just generate a gradient of circles which we can treat as the distance from the viewer (our "z" value) and invert this (ie: 1/z).

Finally to get a good effect we need to apply a texture map. So combining the two coordinates we use the angular coordinate for the "v" coordinate and the depth coordinate as out "u" value.

So the complete code for our tunnel-shader is:


uniform vec2 resolution;
uniform float time;
uniform sampler2D tex;

void main(void)
{
vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
vec2 uv;

float a = atan(p.y,p.x);
float r = sqrt(dot(p,p));

uv.x = 0.1/r;
uv.y = a/(3.1416);

vec3 col = texture2D(tex,uv).xyz;

gl_FragColor = vec4(col,1.0);
}


You can see the result below! (You will need a WebGL enabled browser)
Your browser doesn't appear to support the HTML5 <canvas> element.

As a small extension to this we can progressively change the power of the square function in the circle equation to generate a less and less round "curve", progressively changing the circular tunnel into a square tunnel.
Graph of increasing squares

So starting from:

float power = 1.0;
float r = pow( pow(p.x*p.x,power) + pow(p.y*p.y,power), 1.0/(2*power) );


We can slowly increase the power until we get a square-like tunnel. The image shows the effect of increasing the power term, the final bottom right two images show the contrast between the round and square-like tunnels.

2 comments:

Joakim D said...

Easy to follow and well written, thanks!

undersun said...

It seems in your shader text you doesn't use time variable. WebGL tunnel example has rotation how does it work? Could you explain more math background about "tunnel" deformation. I see simple formulas but can't understand how does it works :)