Crosshatch shaders

Here’s something I was playing with over the summer and never got round to posting. Most of the WebGL stuff in my tutorials aims at something like realism; this post by my friend and colleague Jonathan Hartley persuaded me that there’s much more to creating interesting 3D stuff.

So let’s take a look at some WebGL code that uses the cross-hatching technique used in drawing, where shading is done using layers of perpendicular lines. Here’s a first example, a cross-hatched sphere:

A cross-hatched sphere

(Click here for the live version)

The JavaScript code is really very simple — it’s just the moon demo from lesson 11 with the texture and mouse-handling stuff stripped out. The interesting bit is in the fragment shader where, once we’ve worked out how bright the fragment is going to be and put it in a variable lightWeighting, we do this:

        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        if (length(lightWeighting) < 1.00) {
            if (mod(gl_FragCoord.x + gl_FragCoord.y, 10.0) == 0.0) {
                gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
            }
        }
        if (length(lightWeighting) < 0.75) {
            if (mod(gl_FragCoord.x - gl_FragCoord.y, 10.0) == 0.0) {
                gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
            }
        }
        if (length(lightWeighting) < 0.50) {
            if (mod(gl_FragCoord.x + gl_FragCoord.y - 5.0, 10.0) == 0.0) {
                gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
            }
        }

        if (length(lightWeighting) < 0.3465) {
            if (mod(gl_FragCoord.x - gl_FragCoord.y - 5.0, 10.0) == 0.0) {
                gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
            }
        }

The interesting thing here is the GLSL built-in variable gl_FragCoord. According to the OpenGL ES 2.0 GLSL specification, this holds "holds the window relative coordinates x, y, z, and 1/w values for the fragment". For our purposes, what matters here is that the x and y parts hold 2D coordinates that tell us what pixel the current fragment will be rendered to. Given that, a bit of simple geometry lets us work out how to detect whether or not our current fragment lies on a particular line, and that makes it easy to draw increasing numbers of cross-hatch lines as the shadows get darker.

So having done the hard work by writing that shader, it was easy to put together something more fun: a spinning cross-hatched teapot!

A cross-hatched teapot

(Click here for the live version)

You can leave a response, or trackback from your own site.

Leave a Reply