Friday, January 28, 2011

Raycasting two planes in WebGL





I always implemented this effect by actually writing a complete raytracer/caster, however the version of this effect in ShaderToy greatly simplifies this effect using some old fashioned trigonometry. With some help from my old democoding partner Fractoid we generated a mathematical explanation for this routine.

In essence we use the properties of self-similar triangles to find the intersection distance from the viewer to the plane (see image below).



The camera is shown in red, and the ray from the viewer to the plane is shown in green. The height of the plane above the viewer is 'h', and the distance along the plane to the intersection point is 'd'. We can use our friend from last time, the tangent function, to find the intersection point.

For the lower triangle, if we assume one unit of travel (x) then the angle (theta) will be y / 1.
tan(theta) = y/1;
For the upper triangle, theta will be h / d.
tan(theta) = h/d;
Since the triangles are similar triangles, both theta values are the same, so we can equate these two equations. That is:
y / 1 = h / d
y = h / d
d = h / y

Now, to solve the same problem but flipped by 90 degrees for the horizontal axis we have the same formulation, except we no longer have a unit travel distance, but 'x' instead. This gives:
y / x = h / d
y = h.x / d
d = h.x / y

Now we can plug these into our texture map to give us the overall effect:

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 h = 0.25;
uv.x = h*p.x / p.y;
uv.y = 0.1*time + h / abs(p.y);

gl_FragColor = vec4(texture2D(tex,uv).xyz * p.y*p.y, 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.

No comments: