admin管理员组

文章数量:1415645

Update 5

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.

var MatcapTransformer = function(uvs, face) {
  for (var i = uvs.length; i-- > 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-- > 0;) {
    callback(a, i);
  }

  if (!(i < 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('.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);
  });
})();
body {
  background-color: #000;
  margin: 0px;
  overflow: hidden;
}
<script src=".min.js"></script>
<script src=".js"></script>

<div id='container1'></div>

Update 5

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.

var MatcapTransformer = function(uvs, face) {
  for (var i = uvs.length; i-- > 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-- > 0;) {
    callback(a, i);
  }

  if (!(i < 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/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);
  });
})();
body {
  background-color: #000;
  margin: 0px;
  overflow: hidden;
}
<script src="https://threejs/build/three.min.js"></script>
<script src="https://threejs/examples/js/controls/TrackballControls.js"></script>

<div id='container1'></div>


Update 4

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.


Update 3

Created a new fiddle to show what is NOT the expected behaviour

  • Code

var MatcapTransformer=function(uvs, face) {
	for(var i=uvs.length; i-->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-->0;) {
		callback(a, i);
	}

	if(!(i<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/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);
	});
})();
body {
	background-color: #000;
	margin: 0px;
	overflow: hidden;
}
<script src="https://threejs/build/three.min.js"></script>
<script src="https://threejs/examples/js/controls/TrackballControls.js"></script>

<div id='container1'></div>

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 .. ?)


Update 2

(Has deprecated as code snippet is applied. )


Update

OK .. I've added 3 methods:

  • TransformUv accepts a geometry, and a transformer method which handles uv-transform. The callback accepts an uvs array for each face and the corresponding Face3 of geometry.faces[] as its parameters.

  • MatcapTransformer is the uv-transform handler callback to do the matcap transform.

    and

  • fixTextureWhenRotateAroundZAxis works like what it named.

So far none of the fixTexture.. methods can work alltogether, also, fixTextureWhenRotateAroundXAxis is not figured out. The problem remains unsolved, I wish what's just added could help you to help me out.


I'm trying to make the texture of a mesh always face an active perspective camera, no matter what are the relative positions.

For constructing a real case of my scene and the interaction would be quite plex, I built a minimal example to demonstrate my intention.

  • Code
    var MatcapTransformer=function(uvs, face) {
    	for(var i=uvs.length; i-->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-->0;) {
    		callback(a, i);
    	}
    
    	if(!(i<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/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();
    
    		// Unment the following line and ment out `fixTextureWhenRotateAroundYAxis` to see the demo
    		// fixTextureWhenRotateAroundZAxis();
    
    		// fixTextureWhenRotateAroundAllAxis();
        
    		// controls.update();
    		plane.visible=false;
    		mirror.update(renderer, scene);
    		plane.visible=true; 
    		renderer.render(scene, camera);
    	});
    })();
    body {
    	background-color: #000;
    	margin: 0px;
    	overflow: hidden;
    }
    <script src="https://threejs/build/three.min.js"></script>
    <script src="https://threejs/examples/js/controls/TrackballControls.js"></script>
    
    <div id='container1'></div>

Please note that although the mesh itself rotates in this demonstration, my real intention is making the camera move like orbiting around the mesh.

I've added the wireframe to make the movement more clear. As you can see I use fixTextureWhenRotateAroundYAxis to do it correctly, but it's only for the y-axis. The mesh.rotation.y in my real code is calculated something like

var ve=camera.position.clone();
ve.sub(mesh.position);
var rotY=Math.atan2(ve.x, ve.z);
var offsetX=rotY/(2*Math.PI);

However, I lack the knowledge of how to do fixTextureWhenRotateAroundAllAxis correctly. There are some restrictions of solving this:

  • CubeCamera/CubeMap cannot be used as the client machines might have performance issues

  • Do not simply make the mesh lookAt the camera as they are eventually of any kind of geometry, not only the spheres; tricks like lookAt and restore .quaternion in a frame would be ok.

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 :)

Share Improve this question edited Oct 24, 2019 at 20:38 Ken Kin asked Oct 14, 2019 at 18:14 Ken KinKen Kin 4,7113 gold badges41 silver badges77 bronze badges 4
  • Do you know GLSL shader language? The only way to achieve this effect is to write a custom shader that overrides the default behavior of UV coordinates. – M - Commented Oct 15, 2019 at 18:48
  • @Marquizzo I'm no expert at GLSL, however, I've dug some source code of three.js like WebGLRenderTargetCube; I can find the GLSL wrapped with ShaderMaterial. Like I've told, 'm lacking knowledges around this and it would be too much to drink at the moment. I believe three.js wrapped GLSL good enough and also lightweight enough that I thought we can achive things like this using the library without dealing with GLSL ourselves. – Ken Kin Commented Oct 15, 2019 at 20:59
  • 2 Sorry, but the only way I can think of doing this is through GLSL, since the textures are always drawn in the shader, and you're trying to change the default way the texture position is calculated. You might have better luck asking this type of "how to" questions at discourse.threejs – M - Commented Oct 15, 2019 at 22:55
  • I can confirm, that's solvable in GPU pipeline by a pixel shader – Mosè Raguzzini Commented Oct 17, 2019 at 10:59
Add a ment  | 

1 Answer 1

Reset to default 7 +100

Facing the camera will look like:

Or, even better, as in this question, where the opposite fix is asked:

To achieve that, you have to setup a simple fragment shader (as the OP accidentally did):

Vertex shader

void main() {
  gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

Fragment shader

uniform vec2 size;
uniform sampler2D texture;

void main() {
  gl_FragColor = texture2D(texture, gl_FragCoord.xy / size.xy);
}

A working mock of the shader with Three.js

function main() {
  // Uniform texture setting
  const uniforms = {
    texture1: { type: "t", value: new THREE.TextureLoader().load( "https://threejsfundamentals/threejs/resources/images/wall.jpg" ) }
  };
  // Material by shader
   const myMaterial = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: document.getElementById('vertexShader').textContent,
        fragmentShader: document.getElementById('fragmentShader').textContent
      });
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});

  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 5;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = 2;

  const scene = new THREE.Scene();

  const boxWidth = 1;
  const boxHeight = 1;
  const boxDepth = 1;
  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);

  const cubes = [];  // just an array we can use to rotate the cubes
  
  const cube = new THREE.Mesh(geometry, myMaterial);
  scene.add(cube);
  cubes.push(cube);  // add to our list of cubes to rotate

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;
    
    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }

    cubes.forEach((cube, ndx) => {
      const speed = .2 + ndx * .1;
      const rot = time * speed;
      
      
      cube.rotation.x = rot;
      cube.rotation.y = rot;      
    });
   

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
body {
  margin: 0;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
<script src="https://cdnjs.cloudflare./ajax/libs/three.js/109/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
  void main() {
    gl_Position =   projectionMatrix * 
                    modelViewMatrix * 
                    vec4(position,1.0);
  }
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
  uniform sampler2D texture1;
  const vec2  size = vec2(1024, 512);
  
  void main() {
    gl_FragColor = texture2D(texture1,gl_FragCoord.xy/size.xy); 
  }
</script>
<canvas id="c"></canvas>
  

A viable alternative: Cube Mapping

Here I've modified a jsfiddle about cube mapping, maybe is what are you looking for:

https://jsfiddle/v8efxdo7/

The cube project its face texture on the underlying object and it's looking at the camera.

Note: lights changes with rotation because light and inner object are in fixed position, while camera and projection cube rotates both around the center of the scene.

If you carefully look to the example, this technique is not perfect, but what are you looking for (applied to a box) is tricky, because the UV unwrap of the texture of a cube is cross-shaped, rotating the UV itself will not be effective and using projection techniques has its drawbacks too, because the projector object shape and projection subject shape matters.

Just for better understanding: in the real world, where do you see this effect in 3d space on boxes ? The only example that es in my mind is a 2D projection on a 3D surface (like projection mapping in visual design).

本文标签: javascriptHow to make a texture always face the camera Stack Overflow