title: How to make a texture always face the camera ..?
body: <p><strong>Update 5</strong></p>
  
  <p>Created another fiddle to show what is expected would look like. An invisible skydome and a cubecamera are added and environment map is used; in my case, none of these technique should be used for the reasons already mentioned. </p>
  
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var MatcapTransformer = function(uvs, face) {
    for (var i = uvs.length; i-- &gt; 0;) {
      uvs[i].x = face.vertexNormals[i].x * 0.5 + 0.5;
      uvs[i].y = face.vertexNormals[i].y * 0.5 + 0.5;
    }
  };
  
  var TransformUv = function(geometry, xformer) {
    // The first argument is also used as an array in the recursive calls 
    // as there's no method overloading in javascript; and so is the callback. 
    var a = arguments[0],
      callback = arguments[1];
  
    var faceIterator = function(uvFaces, index) {
      xformer(uvFaces[index], geometry.faces[index]);
    };
  
    var layerIterator = function(uvLayers, index) {
      TransformUv(uvLayers[index], faceIterator);
    };
  
    for (var i = a.length; i-- &gt; 0;) {
      callback(a, i);
    }
  
    if (!(i &lt; 0)) {
      TransformUv(geometry.faceVertexUvs, layerIterator);
    }
  };
  
  var SetResizeHandler = function(renderer, camera) {
    var callback = function() {
      renderer.setSize(window.innerWidth, window.innerHeight);
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
    };
  
    // bind the resize event
    window.addEventListener('resize', callback, false);
  
    // return .stop() the function to stop watching window resize
    return {
      stop: function() {
        window.removeEventListener('resize', callback);
      }
    };
  };
  
  (function() {
    var fov = 45;
    var aspect = window.innerWidth / window.innerHeight;
    var loader = new THREE.TextureLoader();
  
    var texture = loader.load('https://i.postimg.cc/mTsN30vx/canyon-s.jpg');
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    texture.center.set(1 / 2, 1 / 2);
  
    var cubeCam = new THREE.CubeCamera(.1, 200, 4096);
    cubeCam.renderTarget.texture.wrapS = THREE.RepeatWrapping;
    cubeCam.renderTarget.texture.wrapT = THREE.RepeatWrapping;
    cubeCam.renderTarget.texture.center.set(1 / 2, 1 / 2);
  
    var geoSky = new THREE.SphereGeometry(2, 16, 16);
    var matSky = new THREE.MeshBasicMaterial({
      'map': texture,
      'side': THREE.BackSide
    });
    var meshSky = new THREE.Mesh(geoSky, matSky);
    meshSky.visible = false;
  
    var geometry = new THREE.IcosahedronGeometry(1, 1);
    var material = new THREE.MeshBasicMaterial({
      'envMap': cubeCam.renderTarget.texture
    });
    var mesh = new THREE.Mesh(geometry, material);
  
    var geoWireframe = new THREE.WireframeGeometry(geometry);
    var matWireframe = new THREE.LineBasicMaterial({
      'color': 'red',
      'linewidth': 2
    });
    mesh.add(new THREE.LineSegments(geoWireframe, matWireframe));
  
    var camera = new THREE.PerspectiveCamera(fov, aspect);
    camera.position.setZ(20);
  
    var scene = new THREE.Scene();
    scene.add(mesh);
    scene.add(meshSky);
  
    {
      var mirror = new THREE.CubeCamera(.1, 2000, 4096);
      var geoPlane = new THREE.PlaneGeometry(16, 16);
      var matPlane = new THREE.MeshBasicMaterial({
        'envMap': mirror.renderTarget.texture
      });
  
      var plane = new THREE.Mesh(geoPlane, matPlane);
      plane.add(mirror);
      plane.position.setZ(-4);
      plane.lookAt(mesh.position);
      scene.add(plane);
    }
  
    var renderer = new THREE.WebGLRenderer();
  
    var container = document.getElementById('container1');
    container.appendChild(renderer.domElement);
  
    SetResizeHandler(renderer, camera);
    renderer.setSize(window.innerWidth, window.innerHeight);
  
    var controls = new THREE.TrackballControls(camera, container);
  
    var fixTextureWhenRotateAroundAllAxis = function() {
      mesh.rotation.y += 0.01;
      mesh.rotation.x += 0.01;
      mesh.rotation.z += 0.01;
  
      cubeCam.update(renderer, scene);
    };
  
    renderer.setAnimationLoop(function() {
      // controls.update();
  
      plane.visible = false;
  
      {
        meshSky.visible = true;
        mesh.visible = false;
  
        fixTextureWhenRotateAroundAllAxis();
  
        mesh.visible = true;
  
        meshSky.visible = false;
      }
  
      mirror.update(renderer, scene);
      plane.visible = true;
  
      renderer.render(scene, camera);
    });
  })();</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>body {
    background-color: #000;
    margin: 0px;
    overflow: hidden;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://threejs.org/build/three.min.js"&gt;&lt;/script&gt;
  &lt;script src="https://threejs.org/examples/js/controls/TrackballControls.js"&gt;&lt;/script&gt;
  
  &lt;div id='container1'&gt;&lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <hr>
  
  <p><strong>Update 4</strong></p>
  
  <p>Important: Please note there is a reflective plane in back of the target mesh which is for observing if the texture binds to the mesh surface correctly, it has nothing to do with what I'm trying to solve. </p>
  
  <hr>
  
  <p><strong>Update 3</strong></p>
  
  <p>Created a new fiddle to show what is NOT the expected behaviour</p>
  
  <ul>
  <li>Code </li>
  </ul>
  
  <p><div class="snippet" data-lang="js" data-hide="true" data-console="false" data-babel="false">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var MatcapTransformer=function(uvs, face) {
   for(var i=uvs.length; i--&gt;0;) {
    uvs[i].x=face.vertexNormals[i].x*0.5+0.5;
    uvs[i].y=face.vertexNormals[i].y*0.5+0.5;
   }
  };
  
  var TransformUv=function(geometry, xformer) {
   // The first argument is also used as an array in the recursive calls 
   // as there's no method overloading in javascript; and so is the callback. 
   var a=arguments[0], callback=arguments[1];
  
   var faceIterator=function(uvFaces, index) {
    xformer(uvFaces[index], geometry.faces[index]);
   };
  
   var layerIterator=function(uvLayers, index) {
    TransformUv(uvLayers[index], faceIterator);
   };
  
   for(var i=a.length; i--&gt;0;) {
    callback(a, i);
   }
  
   if(!(i&lt;0)) {
    TransformUv(geometry.faceVertexUvs, layerIterator);
   }
  };
  
  var SetResizeHandler=function(renderer, camera) {
   var callback=function() {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect=window.innerWidth/window.innerHeight;
    camera.updateProjectionMatrix();
   };
  
   // bind the resize event
   window.addEventListener('resize', callback, false);
  
   // return .stop() the function to stop watching window resize
   return {
    stop: function() {
     window.removeEventListener('resize', callback);
    }
   };
  };
  
   var getVertexShader=function() {
    return `
  void main() {
   gl_Position=projectionMatrix*modelViewMatrix*vec4(position, 1.0);
  }
  `;
   };
  
   var getFragmentShader=function(size) {
    return `
  uniform sampler2D texture1;
  const vec2 size=vec2(`+size.x+`, `+size.y+`);
  
  void main() {
   gl_FragColor=texture2D(texture1, gl_FragCoord.xy/size.xy);
  }
  `;
   };
  
  
  (function() {
   var fov=45;
   var aspect=window.innerWidth/window.innerHeight;
   var loader=new THREE.TextureLoader();
  
   var texture=loader.load('https://i.postimg.cc/mTsN30vx/canyon-s.jpg');
   texture.wrapS=THREE.RepeatWrapping;
   texture.wrapT=THREE.RepeatWrapping;
   texture.center.set(1/2, 1/2);
  
   var geometry=new THREE.SphereGeometry(1, 16, 16);
   // var geometry=new THREE.BoxGeometry(2, 2, 2);
  
   // var material=new THREE.MeshBasicMaterial({ 'map': texture });
   var material=new THREE.ShaderMaterial({
    'uniforms': { 'texture1': { 'type': 't', 'value': texture } }
    , 'vertexShader': getVertexShader()
    , 'fragmentShader': getFragmentShader({ 'x': 512, 'y': 256 })
   });
  
   var mesh=new THREE.Mesh(geometry, material);
   var geoWireframe=new THREE.WireframeGeometry(geometry);
   var matWireframe=new THREE.LineBasicMaterial({ 'color': 'red', 'linewidth': 2 });
   mesh.add(new THREE.LineSegments(geoWireframe, matWireframe));
  
   var camera=new THREE.PerspectiveCamera(fov, aspect);
   camera.position.setZ(20);
  
   var scene=new THREE.Scene();
   scene.add(mesh);
    
   {
    var mirror=new THREE.CubeCamera(.1, 2000, 4096);
    var geoPlane=new THREE.PlaneGeometry(16, 16);
    var matPlane=new THREE.MeshBasicMaterial({
     'envMap': mirror.renderTarget.texture
    });
  
    var plane=new THREE.Mesh(geoPlane, matPlane);
    plane.add(mirror);
    plane.position.setZ(-4);
    plane.lookAt(mesh.position);
    scene.add(plane);
   }
  
   var renderer=new THREE.WebGLRenderer();
  
   var container=document.getElementById('container1');
   container.appendChild(renderer.domElement);
  
   SetResizeHandler(renderer, camera);
   renderer.setSize(window.innerWidth, window.innerHeight);
  
   var fixTextureWhenRotateAroundYAxis=function() {
    mesh.rotation.y+=0.01;
    texture.offset.set(mesh.rotation.y/(2*Math.PI), 0);
   };
  
   var fixTextureWhenRotateAroundZAxis=function() {
    mesh.rotation.z+=0.01;
    texture.rotation=-mesh.rotation.z
    TransformUv(geometry, MatcapTransformer);
   };
  
   var fixTextureWhenRotateAroundAllAxis=function() {
    mesh.rotation.y+=0.01;
    mesh.rotation.x+=0.01;
    mesh.rotation.z+=0.01;
   };
    
   var controls=new THREE.TrackballControls(camera, container);
  
   renderer.setAnimationLoop(function() {
     fixTextureWhenRotateAroundAllAxis();
  
     controls.update();
     plane.visible=false;
     mirror.update(renderer, scene);
     plane.visible=true;   
  
    renderer.render(scene, camera);
   });
  })();</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>body {
   background-color: #000;
   margin: 0px;
   overflow: hidden;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://threejs.org/build/three.min.js"&gt;&lt;/script&gt;
  &lt;script src="https://threejs.org/examples/js/controls/TrackballControls.js"&gt;&lt;/script&gt;
  
  &lt;div id='container1'&gt;&lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>Maybe I should rephrase my question, but I lack the knowledge to describe accurately about what I'm trying to solve, please help .. (Panoramic-Transform-With-Texture-Looking-At-Direction-Locked-Onto-The-Camera maybe .. ?)</p>
  
  <hr>
  
  <p><strong>Update 2</strong> </p>
  
  <p>(Has deprecated as code snippet is applied. )</p>
  
  <hr>
  
  <p><strong>Update</strong></p>
  
  <p>OK .. I've added 3 methods: </p>
  
  <ul>
  <li><p><code>TransformUv</code> accepts a geometry, and a transformer method which handles uv-transform. The callback accepts an uvs array for each face and the corresponding <code>Face3</code> of <code>geometry.faces[]</code> as its parameters. </p></li>
  <li><p><code>MatcapTransformer</code> is the uv-transform handler callback to do the matcap transform. </p>
  
  <p>and </p></li>
  <li><p><code>fixTextureWhenRotateAroundZAxis</code> works like what it named. </p></li>
  </ul>
  
  <p>So far none of the <code>fixTexture..</code> methods can work alltogether, also, <code>fixTextureWhenRotateAroundXAxis</code> is not figured out. The problem remains unsolved, I wish what's just added could help you to help me out. </p>
  
  <hr>
  
  <p>I'm trying to make the texture of a mesh always face an active perspective camera, no matter what are the relative positions. </p>
  
  <p>For constructing a real case of my scene and the interaction would be quite complex, I built a minimal example to demonstrate my intention. </p>
  
  <ul>
  <li>Code
  <div class="snippet" data-lang="js" data-hide="false" data-console="false" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>var MatcapTransformer=function(uvs, face) {
   for(var i=uvs.length; i--&gt;0;) {
    uvs[i].x=face.vertexNormals[i].x*0.5+0.5;
    uvs[i].y=face.vertexNormals[i].y*0.5+0.5;
   }
  };
  
  var TransformUv=function(geometry, xformer) {
   // The first argument is also used as an array in the recursive calls 
   // as there's no method overloading in javascript; and so is the callback. 
   var a=arguments[0], callback=arguments[1];
  
   var faceIterator=function(uvFaces, index) {
    xformer(uvFaces[index], geometry.faces[index]);
   };
  
   var layerIterator=function(uvLayers, index) {
    TransformUv(uvLayers[index], faceIterator);
   };
  
   for(var i=a.length; i--&gt;0;) {
    callback(a, i);
   }
  
   if(!(i&lt;0)) {
    TransformUv(geometry.faceVertexUvs, layerIterator);
   }
  };
  
  var SetResizeHandler=function(renderer, camera) {
   var callback=function() {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect=window.innerWidth/window.innerHeight;
    camera.updateProjectionMatrix();
   };
  
   // bind the resize event
   window.addEventListener('resize', callback, false);
  
   // return .stop() the function to stop watching window resize
   return {
    stop: function() {
     window.removeEventListener('resize', callback);
    }
   };
  };
  
  (function() {
   var fov=45;
   var aspect=window.innerWidth/window.innerHeight;
   var loader=new THREE.TextureLoader();
  
   var texture=loader.load('https://i.postimg.cc/mTsN30vx/canyon-s.jpg');
   texture.wrapS=THREE.RepeatWrapping;
   texture.wrapT=THREE.RepeatWrapping;
   texture.center.set(1/2, 1/2);
  
   var geometry=new THREE.SphereGeometry(1, 16, 16);
   var material=new THREE.MeshBasicMaterial({ 'map': texture });
   var mesh=new THREE.Mesh(geometry, material);
  
   var geoWireframe=new THREE.WireframeGeometry(geometry);
   var matWireframe=new THREE.LineBasicMaterial({ 'color': 'red', 'linewidth': 2 });
   mesh.add(new THREE.LineSegments(geoWireframe, matWireframe));
  
   var camera=new THREE.PerspectiveCamera(fov, aspect);
   camera.position.setZ(20);
  
   var scene=new THREE.Scene();
   scene.add(mesh);
    
   {
    var mirror=new THREE.CubeCamera(.1, 2000, 4096);
    var geoPlane=new THREE.PlaneGeometry(16, 16);
    var matPlane=new THREE.MeshBasicMaterial({
     'envMap': mirror.renderTarget.texture
    });
  
    var plane=new THREE.Mesh(geoPlane, matPlane);
    plane.add(mirror);
    plane.position.setZ(-4);
    plane.lookAt(mesh.position);
    scene.add(plane);
   }
  
   var renderer=new THREE.WebGLRenderer();
  
   var container=document.getElementById('container1');
   container.appendChild(renderer.domElement);
  
   SetResizeHandler(renderer, camera);
   renderer.setSize(window.innerWidth, window.innerHeight);
  
   var fixTextureWhenRotateAroundYAxis=function() {
    mesh.rotation.y+=0.01;
    texture.offset.set(mesh.rotation.y/(2*Math.PI), 0);
   };
  
   var fixTextureWhenRotateAroundZAxis=function() {
    mesh.rotation.z+=0.01;
    texture.rotation=-mesh.rotation.z
    TransformUv(geometry, MatcapTransformer);
   };
  
   // This is wrong
   var fixTextureWhenRotateAroundAllAxis=function() {
    mesh.rotation.y+=0.01;
    mesh.rotation.x+=0.01;
    mesh.rotation.z+=0.01;
  
    // Dun know how to do it correctly .. 
    texture.offset.set(mesh.rotation.y/(2*Math.PI), 0);
   };
    
   var controls=new THREE.TrackballControls(camera, container);
  
   renderer.setAnimationLoop(function() {
    fixTextureWhenRotateAroundYAxis();
  
    // Uncomment the following line and comment out `fixTextureWhenRotateAroundYAxis` to see the demo
    // fixTextureWhenRotateAroundZAxis();
  
    // fixTextureWhenRotateAroundAllAxis();
      
    // controls.update();
    plane.visible=false;
    mirror.update(renderer, scene);
    plane.visible=true; 
    renderer.render(scene, camera);
   });
  })();</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>body {
   background-color: #000;
   margin: 0px;
   overflow: hidden;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://threejs.org/build/three.min.js"&gt;&lt;/script&gt;
  &lt;script src="https://threejs.org/examples/js/controls/TrackballControls.js"&gt;&lt;/script&gt;
  
  &lt;div id='container1'&gt;&lt;/div&gt;</code></pre>
  </div>
  </div>
  </li>
  </ul>
  
  <p>Please note that although the mesh itself rotates in this demonstration, my real intention is making the camera move like orbiting around the mesh. </p>
  
  <p>I've added the wireframe to make the movement more clear. As you can see
   I use <code>fixTextureWhenRotateAroundYAxis</code> to do it correctly, but it's only for the y-axis. The <code>mesh.rotation.y</code> in my real code is calculated something like </p>
  
  <pre><code>var ve=camera.position.clone();
  ve.sub(mesh.position);
  var rotY=Math.atan2(ve.x, ve.z);
  var offsetX=rotY/(2*Math.PI);
  </code></pre>
  
  <p>However, I lack the knowledge of how to do <code>fixTextureWhenRotateAroundAllAxis</code> correctly. There are some restrictions of solving this: </p>
  
  <ul>
  <li><p>CubeCamera/CubeMap cannot be used as the client machines might have performance issues</p></li>
  <li><p>Do not simply make the mesh <code>lookAt</code> the camera as they are eventually of any kind of geometry, not only the spheres; tricks like <code>lookAt</code> and restore <code>.quaternion</code> in a frame would be ok. </p></li>
  </ul>
  
  <p>Please don't get me wrong that I'm asking an XY problem as I don't have the right to expose proprietary code or I wouldn't have to pay the effort to build a minimal example :)</p>