a short journey

last december, the nice people at Cher Ami (“dear friend” in french) contacted me to give them a hand on a little animation / wish card they wanted to release in january, I gladly accepted which gave birth to this refreshing little thing http://www.ashortjourney.com/

Cher Ami has qualified people in house, a creative director, a copywriter/musician, a front end developer (Etienne Chaumont), a motion designer (Yannice Berthault ) and for this project, they worked with super talented 3D artist Benoît Challand who did all the 3D.

I mostly did the WebGL (with THREE.js), took some shortcuts and had to find a couple of interesting solutions that I would like to share here.

the site is divided into 9 scenes, the first thing I did was to export all the 3D scenes as series of binary 3D objects. as we were on a relatively tight deadline, my project was to merge all the objects of each scene into one big ass binary and so long for the bandwith but I noticed that 2 scenes could be reused as they were a X axis flipped version of the same models with different textures. after taking a closer look, I realized that many objects found in the second scene (the bag closeup) were reused all the way, the car was reused four times, the bag 3, the camera 2 etc. so I chose to use a reusable mesh system instead. I was quite happy with my Model class ; it allowed multiple assets loading and async instanciation that proved quite handy. this is the code:

a given model then inherits from this (yeah I know … inheritance vs composition blahblah… ^^ )

then if you call a new Car( scene1 ) and new Car( scene2 ), both will appear in their respective scenes when the first has finished loading. of course if a Model is already loaded, it is instantiated directly using the same assets.

the textures class is basically static Dictionary that can load textures and materials exposes the raw materials that each object will copy.

I used 3 custom materials: a regular “bitmap”, a “blend” and a “transition” material.

the bitmap material displays a texture, as we have objects that can have 2 versions of their textures, I added a deltaUv and scaleUv uniforms that let me shift and squash the uvs.  for example the following texture is used on the bag in the last bedroom.

bag_room

it can be used with scaleUv = .5 a deltaUv.y = 0 to display the top half and deltaUv.y = .5 to display the bottom half, using a deltaUv.x of .5 will switch to the “night”(right side) version of both textures.

the material has brightness saturation contrast + alpha uniforms, brightness is used to animate the popups on the desktop for instance.

desktop

at some point I added a “binoculars” effect hence some extra uniforms and a slightly heavier shader. that’s the vertex shader (nothing fancy…) it’s the same for all 3 materials.

and the fragment

here’s a live example of the binoculars effect (press play to start):

the blend material is used to render animated materials, it basically reads a spritesheet and blends 2 “frames” hence its name. at some point we had to choose between morphing two meshes or using this little trick. I did a quick proof of concept which was convincing enough. it is used to animate the shadow of the laptop in the first scene, the bag closing in the second scene and on the beach, it is used for the parasol and the bag.

this shader needs a single texture with X frames, a frameCount and a transition value, this is the fragment shader:

even if it’s a bit counter intuitive, it remains simple enough. beware to create uvs frrameCount times smaller for your geometry. here’s a live demo (works best with an actual spritesheet)

finally the transition shader is used to go from scene to scene, it uses two rendertargets in which the source and destinations scenes are rendered then it performs an opening based on a normalized ratio [0,1]

the shader is slightly more hairy this time as it involves some geometry:

p0 and p1 are the 2 handles used when you drag the slider on the screen.
here’s not exactly the same but the demo I used as a starting point for this shader, click drag around, press play to view the transition itself.

that’s all there is to materials in the whole website :)

now there was a problem: the character.

it was the second thing I tackled right after the meshes ; I thought we could use some sort of 2d animation system à la After Effects.
the source animation was done in AE exported as a 23000+ * 500 pixels spritesheet which triggered a loud “no way!”. indeed, there had to be a lighter way to recompose such simple motions. I don’t have After Effects and would have had too little time to study the scripting language. but I still have an old version of Flash so I did a quick prototype ; I moved different parts of the body, exported the clips transform matrices at each frame along with a spritesheet of the body parts. this gave me 3 lists:

and the final spritesheet

ref_perso
then on the JS side, I rebuild the clips and during the update, I recompose the transforms according to a normalized time and a frame count (much like the blend material).

this sounds complex but is in fact pretty easy (and performant):

apart from the transform matrices, we need the scene dimensions (512*512 in this case) to offset the clips around the center of the 3D object’s center, that’s not mandatory but was more convenient.
now this was all well and good BUT it didn’t work…

rendering this object with a perspective camera will cause 2 issues:

  1. it will apply a perspective transform that will “disconnect”  the limbs
  2. transparencies will screw up

1 indeed Flash or whatever animation software usually works with an orthographic projection system, this allows to animate various elements without bothering with their 3D depth ; their position in the display list (or layer) gives them their render order and the occlusion can be computed quite easily.

2 was more problematic, there are several ways to deal with transparency in THREE none of which would work in my particular case : to render the element in the proper order (1 binoculars > 2 shoulder > 3 body ) I had to add a consequent depth between each mesh which made the perspective projection very visible and disconnected the limbs.
if I reduced the space between them, I ended up with transparency collisions ; a mesh supposed to be behind another would punch it’s alpha transparency through the one before it, even if I used the renderer’s logarithmicDepthBuffer and / or the sortObjects flag and / or the objects’ renderOrder property, there was no way to properly reproduce the animation.

the solution was – of course – to render this 3D objects into a renderTarget with an orthographic camera and use it as a texture for a quad mesh the size of the scene (512*512). no more depth problems and no more alpha transparency problem.

animation

actually there is one transparency problem left : the animation is rendered on a black background causing a dark outline, I didn’t spend enough time on this issue but rather dodged it by thresholding the alpha in the material shader

there are subtle interactions: in the first scene you can play with the ‘pen pot’ and with the ball or the bag on the beach. this is a simple raycasting test that triggers Tweens with a Bounce easeOut and random rotations… no physics or black magic here, yet it adds a playful touch (I literally spent 30 minutes playing with the pens on the desktop ^^).

pens

ball

the rest of the website was taken care of by Etienne and Yannice – imho – the soundtrack and sound FX are pretty awesome and serve the whole experience.

to sum it up, I really enjoyed working on this project, it is far from perfect, lots of things could be optimized further but all in all this short journey has a unique look & feel.
hope you’ll enjoy it much as I do :)

4 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *