Recently I was using a parallax corrected cubemapping technique to add some glass reflections to a project (you can read about parallax corrected cubemapping in this excellent writeup). In general, doing planar reflections with cubemaps is not that easy, the cubemap is considered “infinite” and since it is accessed only with the reflection vector it has no notion of location (that means it does not register well with the scene, it seems detached from it and the reflections do not correspond to actual scene items/features).
The basic idea behind the parallax corrected cubemapping technique is to add this missing position/locality to the cubemap by “fitting” it to a bounding box that surrounds a scene (or part of it, you can use and blend more cubemaps if needed). For this to work well though, the cubemap must have been generated specifically for this part of the scene, taking its dimensions into consideration. Trying to fit a generic cubemap to any scene will not work well.
In my case I needed to quickly add some general reflections to the glass, the main requirement was that they should not float and sort of take the camera motion in the room into consideration. Parallax corrected cubemapping is very suitable for this, my only problem was that I did not have but a generic, blurry, cubemap to use. The room to apply the cubemap to was not a “cube” (the z dimension was double that of x, and the y dimension was half that of x) and in the end all I got was some ugly warped (but parallax correct 🙂 ) reflection effects.
A quick fix to this was to take the non uniform dimensions of the room (bounding box) into consideration when calculating the parallax corrected reflection vector. The code from the original article was:
float3 DirectionWS = PositionWS - CameraWS; float3 ReflDirectionWS = reflect(DirectionWS, NormalWS); // Following is the parallax-correction code // Find the ray intersection with box plane float3 FirstPlaneIntersect = (BoxMax - PositionWS) / ReflDirectionWS; float3 SecondPlaneIntersect = (BoxMin - PositionWS) / ReflDirectionWS; // Get the furthest of these intersections along the ray // (Ok because x/0 give +inf and -x/0 give –inf ) float3 FurthestPlane = max(FirstPlaneIntersect, SecondPlaneIntersect); // Find the closest far intersection float Distance = min(min(FurthestPlane.x, FurthestPlane.y), FurthestPlane.z); // Get the intersection position float3 IntersectPositionWS = PositionWS + ReflDirectionWS * Distance; // Get corrected reflection ReflDirectionWS = IntersectPositionWS - CubemapPositionWS; // End parallax-correction code return texCUBE(envMap, ReflDirectionWS);
The scale due to non-cube bounding box dimensions was calculated as following:
float3 BoxDiff = (BoxMax - BoxMin); float minDim = min(BoxDiff.z, min(BoxDiff.x, BoxDiff.y)); float3 BoxScale = minDim / BoxDiff;
and finally the corrected reflection vector was scaled by BoxScale just before sampling the cubemap:
ReflDirectionWS *= BoxScale;
With this small trick any cubemap can be used along with the parallax corrected cubemapping technique (the cubemap will still not match the contents of the scene of course, but at least it won’t seem detached).