Surrogate Rings: WebGL & Three.js
July, 2015.
The demo featured in this post is based directly on an animated gif by artist Surrogate Self, who’s Tumblr accountfeatures 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 can recommend highly, particularly to anyone with any kind of programming background. Rather than teaching a specific application such as Blender, Cinema 4D or Maya, the course is taught entirely in WebGL, via the popular Three.js library. For the more technically minded, the inherent abstraction of working purely in code can be really well suited to the subject matter here, providing a fundamental understanding of the rendering pipeline, an introduction to the concept of procedural modelling and animation, and a whole lot more.
On completing the course material I was pulled away by various work obligations and I told myself I’d get around to using what I’d learnt for a related personal project when time allowed but other things continued to come up and the idea fell by the wayside. That is until I came across the animation loop below by Surrogate Self and immediately felt like it could make for an interesting challenge to model and animate exclusively in code. The rest of this post is a basic breakdown of steps involved in doing just that.
Original GIF by Surrogate Self
While Three.js does support direct manipulation of geometry via transformation matrices, it’s also kind enough to also offer simple position, rotation and scale properties for each of the 3D objects in a scene. As is the convention, objects can also be grouped together in hierarchal parent / child relationships whereby the various transformation properties of a child are relative relative to its parent.
Our scene is composed entirely of simple cube geometry with ten rings, each built from twelve individual cubes instances. Do do this, an empty ‘parent’ ring object is instantiated at the scenes origin with each of its twelve child cubes instantiated at that same location before being translated a fixed distance along the same relative axis. Prior to this translation of each subsequent cube, the parent object is first rotated along a perpendicular axis by an amount of 360/n degrees (where n = number of cubes). The result is successful construction of a single ring of cubes around the scenes origin.
The above principle is then repeated for each individual ring instance, additionally setting discrete position values for each parent object so as to distribute and arrange them to form a static frame of our final composition. To then animate this larger form as seen in original gif, the rotational value of individual ring (parent) objects are incremented on each frame.
With the geometry and animation in place, I paid a visit to John Valledy, a good friend who works in sound design, and I explained I was looking for a kind of analogue machine tone with gently alternating pitch, something mechanical sounding with a steady feeling of fixed pace. I also wanted four variations of a ‘glitch’ sound to accompany a visual effect that I was imagining at varying intensities. Leaving him a link to an old Metamatics track as a point of reference, I returned to work on the visuals.
At first glance, the cubes in the original gif almost appear as raw mesh wireframes but are in fact opaque. The simplest way to reproduce this look was a single square black texture with outlined blue/green edges, mapped 1/1 to each of the six faces on each cube.
From the start, I wanted to something different with the background and had been thinking about how I might use it as an opportunity to introduce a more analogue aesthetic overall. Three.js includes a post processing effects manager (EffectsComposer), allowing for additional rendering passes with GLSL shaders. A little experimenting with some pixel shaders and I soon had a suitably scaled scan line effect combined with a basic vignette, creating something leaning towards the feel of a traditional CRT monitor.
To finish, I placed a large plane in the scenes background with a subtle procedural gradient. While its upper tone remains a constant black, the lower tone that it blends to flickers between two subtle colour variations, gently emphasising the effect of the two post processing pixel shaders.
John soon came back with a first pass at the sound design that was dead on in terms of character. A single follow up session sitting together and we had the final background tone sorted along with five variations on the glitch sound. In the meantime I’d come across an existing pixel shader called DigitalGlitch that came very close to what I was looking for when combined with an accompanying control script.
The plan was to randomly trigger discreet glitch events from a set of five pre-defined variants, at intervals reflecting the movement of the background tone. Unlike the scan line and vignette shaders who’s various parameters remain static after initial setup, the Glitch shader requires modulation to animate its effect. The relevant logic for this was distilled into a couple of succinct functions, handling the triggering and sustaining of events, using five distinct sets of modulation values.
My final challenge was to add an element of user interactivity, so with the assistance of the Three.js RayCaster() class, I wrote an object picking function to interact with individual cubes. By casting a ray that starts at the position of the scenes camera and follows a vector toward the current position of the mouse cursor, all subsequently intersected objects can be gathered in an array, order by proximity to the rays origin. This first object in the returned array is then easily identified as the cube clicked by the user so with this functionality in place, I just needed to create some suitable audio and visual feedback to accompany the action.
For an accompanying audio element, I returned to John and requested a complementary bell sound at various pitches. Triggering a random pitch on each interaction creates an interesting melodic effect when clicking cubes in quick succession. For visual feedback I settled on a simple animation cycle for the texture of the user selected cubes, so that their edges lit up, pulsing gently in unison.
Roles: Designer, Developer.