Search code examples
three.jsglsltexturesmeshpanoramas

Three.js: How to Rotate Equirectangular Panorama Texture


I have 2 textures inside a single mesh that represent 2 equirectangular panoramic images. I need to rotate one of 2 textures in a desirable position using supplied coordinates. In other words, the position of one texture needs to be modified (shifted/offset) before I display it. I cannot rotate the mesh, because it holds 2 textures where only needs to be adjusted.

Here are my fragment shaders:

uniform samplerCube tCube0;uniform samplerCube tCube1;uniform vec3 tCubePosition0;uniform vec3 tCubePosition1;uniform float scale0;uniform float scale1;uniform float tFlip;uniform float opacity;uniform float mixAlpha;varying vec3 vWorldPosition;
#include <common>
void main() {
    vec3 vWorldPositionScaled0 = mix(vWorldPosition, tCubePosition0, scale0);
    vWorldPositionScaled0.x *= -1.0;
    
    vec3 vWorldPositionScaled1 = mix(vWorldPosition, tCubePosition1, scale1);
    vWorldPositionScaled1.x *= -1.0;
    
    vec4 tex0, tex1;
    tex0 = textureCube(tCube0, vec3( tFlip * vWorldPositionScaled0.x, vWorldPositionScaled0.yz ));
    tex1 = textureCube(tCube1, vec3( tFlip * vWorldPositionScaled1.x, vWorldPositionScaled1.yz ));
    
    gl_FragColor = mix(tex1, tex0, mixAlpha);
}

I thought to add the offset to my uniforms

offset0: { type: "v2", value: new THREE.Vector2(0, 0) }, 

and adjust shaders:

    vec3 texCoord0 = vec3(tFlip * vWorldPositionScaled0.x, vWorldPositionScaled0.yz) + vec3(offset0, 0.0);
    tex0 = textureCube(tCube0, texCoord0);

but that produces wrong results. How can I shift/offset/rotate the texture by supplying offset coordinates?


Solution

  • Equirectangular environment maps are not supported natively in three.js. Before they are processed in the shader, they are internally converted to the cube map format.

    Cube maps are normally sampled like so in three.js:

    vec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );
    
    • envMap represents the cube map sampler.
    • flipEnvMap controls whether px/nx should flipped since cube render targets are rendered differently than cube map textures.
    • reflectVec is the reflection vector which controls the sampling.

    When you want to rotate the sampling of a cube map, you need this:

    vec4 texColor = textureCube( envMap, reflectionMatrix * vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) );
    

    The reflectionMatrix transforms the reflection vector and is 3x3 matrix uniform. You ideally compute it from an Euler object in your JS code.

    Hopefully, THREE.WebGLRenderer will support this feature at some point: see https://github.com/mrdoob/three.js/issues/16328.