Stefan Habel

Visual Effects Software Engineer
Home

Frog In The Throat (2008)

08 / 07 / 2009

Directed by Wolfram Kampffmeyer

3D-animated short film
Trailer for the 2008 Stuttgart Festival of Animated Film (ITFS)

My role: Scripting
Software used: XSI

Wolfram needed an expression in XSI to calculate the intensity of a point light over a surface based on the position of the main point light source to create a fake caustics effect for the pearl necklace of his main character.
I used vector mathematics and the dot product to calculate the fake caustics light's intensity. Due to lack of variables and vector functions in the XSI expression language the expression got rather lengthy (see below for details).


ITFS 2008 took place May 1-6 in Stuttgart, Germany.

www.itfs.de

 

The Expression

The expression could have easily been given as this:

l = Hauptlicht - Caustics_Fake0
n = cross( grid0CornerA - grid0Origin, grid0CornerB - grid0Origin )
intensity = ( 90 - acos( dot(l, n) / (length(l) * length(n) ) ) / 90 * 2

Note that Hauptlicht is the main light source, Caustics_Fake0 is the fake caustics light, l is the direction to the main light, and n is the normal of the surface below the fake caustics light.

As stated above there were no vector functions available and no way to store values in variables, so the final expression became:

intensity = ( 90 - acos( ( ( Hauptlicht.kine.global.posx - Caustics_Fake0.kine.global.posx ) * ( ( grid0CornerB.kine.global.posy - grid0Origin.kine.global.posy ) * ( grid0CornerA.kine.global.posz - grid0Origin.kine.global.posz ) - ( grid0CornerB.kine.global.posz - grid0Origin.kine.global.posz ) * ( grid0CornerA.kine.global.posy - grid0Origin.kine.global.posy ) ) + ( Hauptlicht.kine.global.posy - Caustics_Fake0.kine.global.posy ) * ( ( grid0CornerB.kine.global.posz - grid0Origin.kine.global.posz ) * ( grid0CornerA.kine.global.posx - grid0Origin.kine.global.posx ) - ( grid0CornerB.kine.global.posx - grid0Origin.kine.global.posx ) * ( grid0CornerA.kine.global.posz - grid0Origin.kine.global.posz ) ) + ( Hauptlicht.kine.global.posz - Caustics_Fake0.kine.global.posz ) * ( ( grid0CornerB.kine.global.posx - grid0Origin.kine.global.posx ) * ( grid0CornerA.kine.global.posy - grid0Origin.kine.global.posy ) - ( grid0CornerB.kine.global.posy - grid0Origin.kine.global.posy ) * ( grid0CornerA.kine.global.posx - grid0Origin.kine.global.posx ) ) ) / ( sqrt( ( Hauptlicht.kine.global.posx - Caustics_Fake0.kine.global.posx ) * ( Hauptlicht.kine.global.posx - Caustics_Fake0.kine.global.posx ) + ( Hauptlicht.kine.global.posy - Caustics_Fake0.kine.global.posy ) * ( Hauptlicht.kine.global.posy - Caustics_Fake0.kine.global.posy ) + ( Hauptlicht.kine.global.posz - Caustics_Fake0.kine.global.posz ) * ( Hauptlicht.kine.global.posz - Caustics_Fake0.kine.global.posz ) ) * sqrt( ( ( grid0CornerB.kine.global.posy - grid0Origin.kine.global.posy ) * ( grid0CornerA.kine.global.posz - grid0Origin.kine.global.posz ) - ( grid0CornerB.kine.global.posz - grid0Origin.kine.global.posz ) * ( grid0CornerA.kine.global.posy - grid0Origin.kine.global.posy ) ) * ( ( grid0CornerB.kine.global.posy - grid0Origin.kine.global.posy ) * ( grid0CornerA.kine.global.posz - grid0Origin.kine.global.posz ) - ( grid0CornerB.kine.global.posz - grid0Origin.kine.global.posz ) * ( grid0CornerA.kine.global.posy - grid0Origin.kine.global.posy ) ) + ( ( grid0CornerB.kine.global.posz - grid0Origin.kine.global.posz ) * ( grid0CornerA.kine.global.posx - grid0Origin.kine.global.posx ) - ( grid0CornerB.kine.global.posx - grid0Origin.kine.global.posx ) * ( grid0CornerA.kine.global.posz - grid0Origin.kine.global.posz ) ) * ( ( grid0CornerB.kine.global.posz - grid0Origin.kine.global.posz ) * ( grid0CornerA.kine.global.posx - grid0Origin.kine.global.posx ) - ( grid0CornerB.kine.global.posx - grid0Origin.kine.global.posx ) * ( grid0CornerA.kine.global.posz - grid0Origin.kine.global.posz ) ) + ( ( grid0CornerB.kine.global.posx - grid0Origin.kine.global.posx ) * ( grid0CornerA.kine.global.posy - grid0Origin.kine.global.posy ) - ( grid0CornerB.kine.global.posy - grid0Origin.kine.global.posy ) * ( grid0CornerA.kine.global.posx - grid0Origin.kine.global.posx ) ) * ( ( grid0CornerB.kine.global.posx - grid0Origin.kine.global.posx ) * ( grid0CornerA.kine.global.posy - grid0Origin.kine.global.posy ) - ( grid0CornerB.kine.global.posy - grid0Origin.kine.global.posy ) * ( grid0CornerA.kine.global.posx - grid0Origin.kine.global.posx ) ) ) ) ) ) / 90 * 2

There were a number of pearls in the necklace that needed the fake caustics effect, so a lot of copy-and-paste was done, but surprisingly in the end everything worked out just fine.

Lady Sibyl Fake Caustics - fake caustic light and main light
Fake Caustics - all elements
Fake Caustics - render preview