Stefan Habel

Visual Effects Software Developer
Home
Photo from Flickr: Ãœberseequartier Station Sign in Hamburg's HafenCity

Frog In The Throat (2008)

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

Last updated: 30 December 2021

Directed by Wolfram Kampffmeyer

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 maths 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 resulting expression turned out to be rather lengthy (see below).


ITFS 2008 took place May 1-6 in Stuttgart, Germany.
You can watch the short on YouTube (link below).

itfs.de/en/
youtube.com/watch?v=HiWTJEi7v1I

 

The Expression

The expression could have been given as:

l = Hauptlicht - Caustics_Fake0
n = cross( grid0CornerA - grid0Origin, grid0CornerB - grid0Origin )
intensity = ( 90 - acos( dot(l, n) / (length(l) * length(n) ) ) / 90 * 2
  • 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 XSI didn't support functions or variables, the resulting expression became rather lengthy:

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 copying and pasting were done, but 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