Saturday, February 12, 2011

Sphere effect in WebGL




This effect is quit interesting to decompose. There are a few versions out there with some confusing formulations and constants, however in essence they all come from the same principles. This version is based on one by Luis Gonzalez, which in turn was based Iñigo Quilez's version (formula 14). (iq emailed me to tell me about his windows-only program, but the data file is plain-text - lots more effects to decompose! Thanks iq!)

First, we start with our trusty radius calculation:

vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
float r =sqrt(dot(p,p));


Now to generate a sphere-like looking object we want to create something that looks somewhat like a parabola when we take a slice of it. By careful choice of our equations we should be able to construct the appropriate curve. First, since we are creating a sphere we need to create the outline of the sphere, which of course will be a circle. We can use the fact that the square-root of a negative number will be 'undefined' (complex) to generate the outline of our sphere:

float f = sqrt(1.0 - r*r);
gl_FragColor = vec4(f,0.0,0.0, 1.0);

Inside the 'sphere' (inverted below)

This generates a circle from a re-arrangment of the circle equation (simplified for unit circle):

x²+y²=1
y²=1-x²
y=sqrt(1-x²)
If you apply a linear texture to this, you will notice that it seems like we are looking at the 'inside' of a parabola, and to get something that looks like a sphere, we need to be looking from the 'outside'. We can fix this by inverting our previous result:

vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
vec2 uv;
float r =sqrt(dot(p,p));
float f = 1.0 - sqrt(1.0 - r*r);
uv.x = p.x*f;
uv.y = p.y*f;
gl_FragColor = vec4(texture2D(tex,uv).xyz, 1.0);


Gee, that looks vaguely sphereoid already. We just need to make it look more 3D. Well, lets try generating a sphere from the equation of a circle:

z=r*r
r = sqrt(x*x+y*y);
z = r*r;

Recall from the tunnel tutorial that to project from 3D to 2D we simply need to divide by z. We can use the 3D equation of the sphere to calculate the Z depth of the sphere, (i.e. divide by r*r).

Its starting to look good, now we just need to finish it off with a perspective divide and we have our final result (we can also factor out the redundant square-root):

vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
vec2 uv;
float r = dot(p,p);
float f = (1.0-sqrt(1.0-r))/(r);
uv.x = p.x*f + time;
uv.y = p.y*f + time;
gl_FragColor = vec4(texture2D(tex,uv).xyz, 1.0);


It's not raytracing, but it looks pretty good.
Your browser doesn't appear to support the HTML5 <canvas> element.

3 comments:

Viper said...

The animation at the bottom is really cool!

Dan said...

Thanks, nice writeup :)

What are the constraints on the texture JPEG. I tried swapping in another (fairly randomly selected) texture for neheTexture.image.src but nothing shows. Does it need to be square, same dimensions etc?

Adrian said...

Hi Dan, the texture needs to be square, and it needs to be in a format supported by WebGL