Tag Archives: ray

Stained Glass

For my next project, I wanted to do something a bit more challenging than simply ray casting against spheres, so I chose something deceptively simple: I chose to ray trace some stained glass. Now, this seemed like a good idea at the time, and I’m pretty happy with the result, but my weekend was eaten by this little project because once I got one part working, another idea popped into my head – “wouldn’t it be cool if…” was a recurring theme.

There were more than a few subtle (and non-subtle) challenges with this little shader:

  1. Up until this point, I’ve been sticking to a single ray cast, with the exception of the Light and Shadow shader, which also cast rays to check if a position was in shadow.  For this shader, I am doing multiple bounces.
  2. A stained glass window does not cast a simple shadow.  It transmits some of its received light.
  3. The original thought was to simply have a “window pane” cast its light at the ground and be done with it.  It turned out that it was an extremely dull and uninteresting scene.  So I added a sphere, thinking “how much complexity could it add?”
  4. I kept running into more ideas like: “I could add light attenuation!” or, “what if I made it look like my light is rising and falling like the Sun?” And who could forget, “what if I made this light a simple flare instead of a janky looking sphere?”

So, all told, I managed to try a number of new things in this latest shader, and as a result it took me far longer than the original vision.  I think it was the right choice, although there are certainly nitpicks that I have that I may have to revisit someday.  For example, I’m not happy with how unconvincing the “glass” material is (I didn’t refract my light!), or with how shiny the sphere is.

Still, I’m happy with the end result.  A few highlights:

float iRectangle(in ray sceneRay, in vec4 plane, in vec2 constraintsXY, in vec2 widthHeight, out vec2 uvCoord) {
	float planeIntersection = iPlane(sceneRay, plane);
	vec3 hitPoint = sceneRay.origin + sceneRay.direction*planeIntersection;
	if (hitPoint.z < constraintsXY.x || hitPoint.z > constraintsXY.x+widthHeight.x) {
		planeIntersection = -1.0;
	}
	else if (hitPoint.y < constraintsXY.y || hitPoint.y > constraintsXY.y+widthHeight.y) {
		planeIntersection = -1.0;
	}
 
	if (planeIntersection >= 0.0) {
		uvCoord = vec2(hitPoint.z - constraintsXY.x, hitPoint.y - constraintsXY.y);
	}
	else {
		uvCoord = vec2(-1.0);
	}
	return planeIntersection;
}

What this little function does is to test for plane intersection via iPlane(), but then it also takes in some constraints, such as a starting (u,v) coordinate and a width and height from those coordinates, creating a rectangle.  Granted, this only works for axis-aligned planes, but that was fine for this particular shader.  However, the real purpose of this function was to return a UV coordinate of where a ray hit, allowing me to map my color scheme for the glass to the coordinates, essentially allowing me to texture a quad.

Once I textured a quad, the rest was fairly simple – firing a primary ray at the quad would give me a color, which I would apply to the shadow ray, resulting in a stained glass lighting effect.

Adding a sphere to the scene caused a few headaches, such as doing shadowing, but the most pain probably came from taking a whole bunch of crazy special purpose code and simplifying it so that it would survive a ray trace loop.  There’s still quite a bit of it in the shader, but I’ve tried to comment it and use meaningful variable identifiers so that it’s clear what I’m trying to do in each section.

Also new for this post, I’m going to embed a live version of the shader in this post.  To rotate the scene, simply click and drag below.  If you’d like to see the full listing of code, click on “Stained” in the upper left after you mouse over.  Enjoy!

Light and Shadow

At the request of a friend from work, I put together a little something with some “light and shadow“.  The trick to basic shadows when working with ray casting is this:

  1. Do a normal ray cast and find your ray intersection with the scene.
  2. From that intersection point, try to ray cast toward the light.  If an intersection is encountered, the point is in shadow and should be shaded appropriately.

In code, this is pretty straightforward.  From tonight’s shader:

	//intersect the ray with scene
	vec2 t;
	vec4 sphHit;
	float id = intersect(rayOrigin, rayDirection, t, sphHit);

This is the initial intersection with the scene. We send out a ray from our origin point with a direction, and in return we get a t (ray length) and sphere (defined by a vec4) as well as an identifier for which sphere was hit.  If nothing is hit, we’ll get -1.0 for this identifier.

	//If we hit a sphere
	if(id >= 0.0)
	{
		//find the point where we hit the sphere and evaluate luminance
		vec3 pos = rayOrigin + t.x*rayDirection;
		vec3 nor = nSphere(pos, sphHit);
		float dif = clamp(dot(nor, normalize(light-pos)), 0.0, 1.0);
		col = vec4(vec3(dif), 1.0);

If we determine that we’ve hit something, we’ll figure out exactly where we hit it and calculate Lambertian reflectance for that point.

		//check to see if this point is in shadow
		vec2 shadowT;
		vec4 shadowHit;
		//check for intersect between the sphere and the light
		float shadowId = intersect(pos, normalize(light-pos), shadowT, shadowHit);
 
		//if we have a non-negative id, we've hit something other than the light
		if (shadowId >= 0.0) {
			col = vec4(0.0);
		}
	}

Then, we try to intersect the scene from our original intersection point with a ray in the direction toward the light. If we hit something, our point is in shadow, so we’ll replace the color with black, which will give us a hard shadow.

	//If we hit the light
	else if (id == -2.0) {
		col = vec4(1.0);
	}

Finally, we’ll add the light to the scene as its own special case, making it totally white. And that’s it!

lightandshadow
Click on the image to see the “Light and Shadow” shader in action.

Putting things together – Magnifying Lens (Fresnel) shader

In an effort to start putting concepts together, I’ve combined some parts from two of my initial efforts (the interactive sphere and Perlin noise) into a single demonstration.  I’ve also been interested in understanding how Fresnel reflection and refraction work, and so today I spent my afternoon assembling a fun shader to do all of this.  The subtle challenges inherent in this shader:

  1. Getting the Perlin noise to scroll infinitely to the right
  2. Using a Fresnel reflection and a Fresnel refraction
  3. Doing it all by ray tracing a sphere
magnifyinglens
Click on the image to see the “Magnifying Lens” Fresnel shader

RenderLoupe: the art and science of real-time computer graphics

I decided at some point in the last couple of weeks that I wanted to put together a site that would look at some of the real-time computer graphics gems that I’ve either created or come across in my work. RenderLoupe is the culmination of this desire.

Why “RenderLoupe”?  Well two reasons:

  1. To anyone knowledgeable about computer graphics, there’s an obvious play on words here with “render loop”.
  2. Since we’re going to be looking at things, and perhaps scrutinizing them, the idea of looking at things through a loupe really appealed to me.

To start off with, just to whet your appetite, I have links to a couple of sites that allow you to write GLSL in the browser:

  1. ShaderToy (a lot of times this is a very slow loading site, so be patient!)
  2. GLSL Sandbox Gallery

On ShaderToy, I wrote a couple of simple shaders to get started – if you’re interested in seeing “what is possible” in the browser, there are a lot of great examples.  The first one I wrote is an edge detection shader, which uses matrix convolutions, such as the Sobel kernel, to highlight places in the image where there is a significant change in luminance from one pixel to another.  I decided to include a number of other kernels as options.  The shader source is documented, so here you go:

simpleEdgeDetection
Click on the image to see the “Real-time edge Detection” shader

I also wrote a shader that casts rays at a sphere in real time.  You can drag the mouse and the eye/viewpoint will update accordingly:

simpleSphere
Click to see the “Simple sphere ray casting” shader (trace with no bounce)