1

In Three.js I have an rectangle which is moving away from the camera, how can I compute a 3D x and y scale to apply to the rectangle such that the rectangle retains its same size on the screen in pixels, no matter how far away from the camera it is.

Here I made a codepen which shows the rectangle moving away from the camera. I would like it to retain its same width and height on the screen no matter how far away it is in the z index, that means the x and y scale of the rectangle needs to vary by some scale dependent on the distance in the z axis, this needs to occur dynamically dependent on the z index.

I don't know how to compute this, I suspect its some function taking as input some of the following, the z index offset, the height or width of the object, the field of view of the camera, the aspect ratio, the screen width and height. The end result would be that the object would not appear to be moving away from the camera, even though it is moving away.

https://codepen.io/philip368320/pen/bGwJPvE

var width = window.innerWidth;
var height = window.innerHeight;

var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);

var scene = new THREE.Scene();

var cubeGeometry = new THREE.CubeGeometry(100, 100, 0.000001);
var cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
//cube.rotation.y = Math.PI * 45 / 180;
scene.add(cube);


var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);
camera.position.y = 0;
camera.position.z = 400;
camera.lookAt(cube.position);

scene.add(camera);

var skyboxGeometry = new THREE.CubeGeometry(10000, 10000, 10000);
var skyboxMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.BackSide });
var skybox = new THREE.Mesh(skyboxGeometry, skyboxMaterial);

scene.add(skybox);

var pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(0, 300, 200);

scene.add(pointLight);

var clock = new THREE.Clock();

var n = 0;

function render() {
  requestAnimationFrame(render);
  n = n + clock.getDelta() * 30;
  cube.position.z = -n;
  renderer.render(scene, camera);
}

render();
Phil
  • 46,436
  • 33
  • 110
  • 175
  • 1
    I did [something similar](https://github.com/jscastro76/threebox/issues/179) in my plug-in using Three.js and Mapbox, to keep the size of some objects when zooming out below a concrete distance (zoom level). I used the transformation matrix scale value. Not the same you are trying to do with pure Three.js but I hope it points you to the right direction – jscastro Jan 23 '21 at 11:37
  • 1
    Have a look: https://stackoverflow.com/a/40452050/4045502 – prisoner849 Jan 23 '21 at 12:13

2 Answers2

1

To keep the cube in place in your codepen example, just scale it according to the distance. If it's z away, the size of the cube on the screen is 1/z, so scale to the inverse of that.

The render function with the scaling (initial distance between the camera and the cube is 400):


    function render() {
      requestAnimationFrame(render);
      n = n + clock.getDelta() * 30;
      cube.position.z = -n;
      cube.scale.set((400+n)/400, (400+n)/400, 1);
      renderer.render(scene, camera);
    }

Or by calculating the distance between the camera and the cube:


    function render() {
      requestAnimationFrame(render);
      n = n + clock.getDelta() * 30;
      cube.position.z = -n;
      let distance = camera.position.distanceTo(cube.position);
      cube.scale.set(distance/400, distance/400, 1);
      renderer.render(scene, camera);
    }

Updated codepen here: https://codepen.io/mikael-l-nnroth/pen/LYRoaBB

mglonnro
  • 151
  • 10
0

Based upon the information as given by https://stackoverflow.com/users/4045502/prisoner849 in the comments above, which was this link Three JS Keep Label Size On Zoom which discusses an example which keeps the text labels at the same size no matter how far from the camera. The example codepen has been updated with two surfaces, one is moving away from the camera but retaining its current width and height in on screen pixels. Therefore it retains the same width and height in on screen pixels as it moved away from the camera. "Object to retain same size on screen regardless of its distance from the camera". That is cube1. The other cube2 is not moving.

https://codepen.io/philip368320/pen/bGwJPvE

var width = window.innerWidth;
var height = window.innerHeight;

var useScaleFactor = true;
var scaleVector = new THREE.Vector3();

var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);

var scene = new THREE.Scene();

var cubeGeometry = new THREE.CubeGeometry(100, 100, 0.000001);
var cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
var cube1 = new THREE.Mesh(cubeGeometry, cubeMaterial);
var cube2 = new THREE.Mesh(cubeGeometry, cubeMaterial);
//cube.rotation.y = Math.PI * 45 / 180;
scene.add(cube1);
scene.add(cube2);


var camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 10000);
camera.position.y = 0;
camera.position.z = 400;
camera.lookAt(cube1.position);

scene.add(camera);

var skyboxGeometry = new THREE.CubeGeometry(10000, 10000, 10000);
var skyboxMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, side: THREE.BackSide });
var skybox = new THREE.Mesh(skyboxGeometry, skyboxMaterial);

scene.add(skybox);

var pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(0, 300, 200);

scene.add(pointLight);

var clock = new THREE.Clock();

var n = 0;


function render() {
  requestAnimationFrame(render);
  n = n + clock.getDelta() * 30;
  cube1.position.z = -n;
  cube2.position.x = 200;
  
  if (useScaleFactor) {
        var scaleFactor = 400.0;
        var scale = scaleVector.subVectors(cube1.position, camera.position).length() / scaleFactor;
        cube1.scale.set(scale, scale, 1);
  }
  
  renderer.render(scene, camera);
}

render();
Phil
  • 46,436
  • 33
  • 110
  • 175
  • This doesn't appear to answer your question. Instead you should update your question with this answer's content and delete your answer. – Peter O. Jan 24 '21 at 05:09
  • Why doesn't it answer the question? – Phil Jan 24 '21 at 22:27
  • Sorry, that didn't seem obvious to me from reading the post, as it looked like it should have been an edit to your question, especially since the code is almost the same as the code in your question. – Peter O. Jan 24 '21 at 22:44