Parallax-Corrected Cubemap Shaders (GLSL)

Hello again!

I’ve also adapted some work from here and here to create parallax-corrected cubemaps, something I’ve been using for a project. This technique still blows my mind. All we’re looking at here are GridSOPs.

TDMovieOut.0

The main bulk of the work is here. There’s essentially a simulated cube that we check for intersections with, casting a ray from the camera. We then use that intersection as a position in the cubemap texture.

vec4 parallexCorrectedCubemap(samplerCube cubemap, 
   						  vec3 camDirection, 
   						  vec3 worldPos, 
   						  vec3 boxMin, 
   						  vec3 boxMax, 
   						  vec3 worldSpaceCubemapPos) {

   // get ray from camera to pos in world
   vec3 ray = normalize(worldPos - camDirection);

   // gets min / max intersections with ray and cube
   // (not sure about this vector division or how it works tbh)
   vec3 planeIntersect1 = (boxMax - worldPos) / ray;
   vec3 planeIntersect2 = (boxMin - worldPos) / ray;

   // pick the furthest intersection
   vec3 furthestPlane = max(planeIntersect1, planeIntersect2);

   // get the distance to closest intersection on this cube plane
   float dist = min(min(furthestPlane.x, furthestPlane.y), furthestPlane.z);

   // use this to recover the final intersected world space
   vec3 intersectedWorldSpacePos = worldPos + ray * dist;
   
   // now get the ray in cubemap coords
   ray = intersectedWorldSpacePos - worldSpaceCubemapPos;

   // return cubemap texture value
   if (uTextureInstancing) {
   	return TDInstanceTexture(iVert.instanceTextureIndex, ray);
   }
   return texture(sColorMap, ray);
}

This part gets the direction from the interpolated world space position to the back corner of the simulated cube.

vec3 planeIntersect1 = (boxMax - worldPos) / ray;

Why dividing by ray works to find the intersection along the ray is a gap in my knowledge, happy to hear an explanation from someone :smiley:

The simulated cube is assembled in the vertex shader and is built around the GridSOP we’re instancing…

void populatePCStruct() {
	// Parallax Correct Cubemap stuff
	vec3 instancePos = texelFetch(sInstancePos, ivec2(TDInstanceID(), 0), 0).xyz;

	// calculate simulated cube using the
	// plane we're rendering to (GridSOP)
	vec3 pc = uPlaneCenter;
	vec2 ps = uPlaneSize;
	pC.boxMinPos = vec3(pc.x + ps.x / 2, pc.y - ps.y / 2, pc.z) + instancePos;
	pC.boxMaxPos = vec3(pc.x - ps.x / 2, pc.y + ps.y / 2, pc.z - ps.x) + instancePos;

	// calculate center of simulated cube
	// and adjust center if were instancing
	vec3 cubeCenter = vec3(pc.x, pc.y, -ps.x / 2);
	pC.worldSpaceCubemapPos = cubeCenter + instancePos;
}

As always I hope this helps someone!

ParallaxCubemap.toe (10.1 KB)

4 Likes