[noob-ish] Reflection texture coordinate modes 🪞

(I’m putting together a PR to clarify the reflection/refraction docs based on answers to my earlier question on reflection textures, but there’s a few things I can’t quite figure out…)

:ice_cube: (1) What’s the real difference between PLANAR_MODE and CUBIC_MODE?

I read deltakosh’s post and studied the code and experimented with flat textures (playground) and cube textures (playground) but they actually seem very similar. I found these differences:

  • CUBIC_MODE has an evil twin INVCUBIC_MODE (just inverts y for texture lookup) but there’s no INVPLANAR_MODE
  • PLANAR_MODE uses vec3(reflectionMatrix * vec4(coords, 1)) but CUBIC_MODE uses vec3(reflectionMatrix * vec4(coords, 0)). I think 1 is the right W-value here, not sure? This is a subtle effect (and no effect if no reflectionMatrix is used)

Otherwise they both implement the simple law of reflection (wikipedia). Both seem designed for cubemap (3D) textures, not flat textures, despite the name (they both produce (-1,-1,-1)-(+1,+1,+1) UVW, rather than (0,0,0)-(1,1,0) UV).

:crystal_ball: (2) What is SPHERICAL_MODE really, anyway?

This mode is an odd duck. It changes based on camera direction in an unusual way. Looking at reflectionFunction.fx:

vec3 viewDir = normalize(vec3(view * worldPos));
vec3 viewNormal = normalize(vec3(view * vec4(worldNormal, 0.0)));

These are the surface point and surface normal in screen (camera) coordinates (screen x/y, depth z), normalized (length 1). So it’s the direction from the eye to the point and the point normal, but in screen coordinates.

vec3 r = reflect(viewDir, viewNormal);
r = vec3(reflectionMatrix * vec4(r, 0));

This follows the law of reflection (very ordinary) in screen coordinates (hmm?).

r.z = r.z - 1.0;


float m = 2.0 * length(r);
return vec3(r.x / m + 0.5, 1.0 - r.y / m - 0.5, 0);

Finally, normalize again and map to (0,0)-(1,1) UV (centered at 0.5,0.5) for texture lookup. That part makes some sense. But I cannot figure out what the previous code is trying to accomplish, though I’m sure it is very clever and/or standard (I am still very noob).

Correct my confusion? Once clarified I can incorporate into a doc PR as best I can. :no_mouth:

CUBIC mode is mostly for reflection texture where we need a vector (and not a position) hence the w set to 0. Planar is about evaluating a position (hence w === 1). Planar can work with 2d textures but work better with cube textures

Spherical mode comes from 3dsmax (or I bet Maya). The idea is to project a texture onto a spherical target

Thanks for the reply!

:ice_cube: If it’s not too much trouble, can you explain why the W=0 vs W=1 difference matters at all? Both CUBIC_MODE and PLANAR_MODE seem to act nearly identically with both flat and cube textures. The W=0 vs W=1 would only make a different if reflectionMatrix is non-identity, and even then not usually.

(Neither one seems to work very well with flat textures, they end up with a projection of the X/Y texture onto the unit circle, also the texture is upside down, also the texture is repeated 4 times with seams at the zeroes, all of this is because it’s (-1,-1,-1)-(+1,+1,+1) X/Y/Z vectors.)

:crystal_ball: Do you have an example of SPHERICAL_MODE doing anything but just looking trippy and random and swirling around a lot? I can’t make any, and as currently coded I’m not sure it mathematically corresponds to anything sensible. (If people want to wrap a texture around a sphere, the equirectangular coordinate modes are a fine way to do that…)

It makes a difference as soon as the reflectionMatrix contains a translation

I do agree that both work better with cube:)

The other big difference is in the code itself as we provide different textures for each case:
Babylon.js/texture.ts at master · BabylonJS/Babylon.js (github.com)

Example: Babylon.js Playground (babylonjs.com)

The spherical mode here is great to simulate sky reflection

Thanks! My attempt to capture the meaning of the modes: Revise the "diving deeper" (howto) page on reflections & refractions by egnor · Pull Request #230 · BabylonJS/Documentation · GitHub