Surrogate Rings – WebGL & Three.js
Posted 27th July, 2015 in 3D, Animation, Design, Programming, Web Development
The demo featured in this post is based directly on an animated gif by Surrogate Self, who’s Tumblr account features a whole series of similar pieces he’s created using Cinema 4D, Photoshop and After Effects (alongside a wealth of other awesome and inspiring imagery).
My initial introduction to interactive 3D graphics was an online course by Udacity, which I highly recommend, particularly to anyone with any kind of programming background. Rather than teaching a specific application such as C4D or Maya, the course is taught entirely in WebGL, via the popular Three.js library. I feel as though the inherent abstraction of working with code is really well suited to the subject matter here, providing a fundamental understanding of the rendering pipeline, an introducing to procedural modelling and animation, and a whole lot more.
On completing the course material, other things came up and I told myself I’d get around to a related personal project when time allowed but the idea fell by the wayside. That is until I saw the Surrogate Self gif and immediately felt like it would be an interesting challenge to model and animate exclusively in code.
While Three.js supports the manipulation of geometry via transformation matrices, it’s also kind enough to supply simple position, rotation and scale properties for each of the 3D objects in your scene. These objects can also be grouped together in parent / child relationships whereby the various transformation properties of a child can be set and modified relative to it’s parent.
Our scene is composed entirely of simple cube geometry; ten rings, each built from twelve individual cubes. Once an empty ring object is instantiated at the scenes origin, each of it’s child cubes are created at that same location before being translated a fixed distance along the same axis. Prior to the translation of each individual cube, the parent object is rotated along the appropriate perpendicular axis by 360/n degrees (where n = number of cubes) to construct a single ring of cubes around the scenes origin.
The above principle can then be repeated with each individual ring to create the required geometry for a single static frame of our larger composition. In order to animate this larger form as seen in original gif, the final rotational value of each individual ring object is incremented on each frame.
With the basic geometry and animation in place, I paid a visit to John Valledy, a good friend who works in sound design. I explained I was looking for kind of a analogue machine tone with a gently alternating pitch, something mechanical sounding with steady feeling of fixed pace. I also required four variations of a ‘glitch’ sound to accompany a visual effect at varying intensities. I left him a link to the intro of a Metamatics track I had in mind as a reference point then returned to work on the visuals.
At first glance, the cubes in the original gif almost appear as wireframes but they are in fact opaque. I decided the simplest way to reproduce this look was a single square texture, black with outlined blue edges, applied to the faces of each cube.
I knew from the start that I wanted to something different with the background and had been thinking about how I might introduce a more analogue aesthetic overall. Three.js includes an effects manager (EffectsComposer) allowing for additional rendering passes with GLSL shaders. A little experimenting with some pixel shaders and I soon had suitably scaled scan lines combined with a vignette effect, creating something close to the feel of a traditional CRT monitor.
Finally, I placed a large background plane with a subtle vertical gradient, in the back of the scene. While the gradients upper tone remains a constant black, the bottom tone flickers between two subtle variations, gently emphasising the effect of the two pixel shaders.
John’s first pass at the sound design was dead on in terms of character. After a single follow up session sitting together, we had the alternating background tone and five variations on the glitch sound sorted. While experimenting with the previously mentioned pixel shaders, I’d come across another called DigitalGlitch that when combined with it’s accompanying GlitchPass post-processing script, came very close to what I was looking for.
My plan was to trigger a glitch event (chosen at random from five variants) at set intervals matching the pace of my background sound. Unlike the scan line and vignette shaders who’s parameters remain static after initial setup, the parameters of the DigitalGlitch shader require modulation to animate it’s resulting effect. After a little studying of the accompanying GlitchPass script I elected to recreate the relevant logic in a couple of succinct functions, handling the triggering and sustaining of events, with five distinct brackets of randomised modulation.
The final challenge was to add an element of user interactivity. With the assistance of the RayCaster() class built into Three.js, an object picking function can be written as follows. By casting a ray who’s origin lies at the position of the scenes camera, outward along a vector defined by the current position of the mouse cursor, all subsequently intersected objects can be gathered in order of proximity and returned in an array. This allows you to identify the first object in the returned array as the cube clicked on by the user. So now I just needed to create some suitable audio and visual feedback.
For the audio component, I returned to John and requested a complementary bell sound at various pitches. Triggering a random pitch each time, creates a nice melodic effect when clicking cubes in quick succession. For the visual feedback I decided I’d animate the textures of cubes so that their edges lit up on initial selection, gently pulsing in unison thereafter. For this I borrowed the TextureAnimator class from Lee Stemkoski, who’s numerous Three.js examples are a fantastic showcase of what you can do with the library.