rankind rankind - 27 days ago 22
Javascript Question

Three JS Keep Label Size On Zoom

I'm working on a solar system in three.js and am curious if there is an easy way to make the labels for the planets I have below all show up the same size regardless of how far they are from the camera? I can't seem to find a solution to this. I figure you could calculate the distance from each label to the camera then come up with some sort of scaling factor based on that. Seems like there would be an easier way to accomplish this?

Thanks!

enter image description here

Answer

Finally I found the answer to your question:

First, create a DOM Element:

<div class="element">Not Earth</div>

Then set CSS styles for it:

.element {position: absolute; top:0; left:0; color: white}
//        |-------------------------------|  |-----------|
//            make the element on top of       canvas is 
//               the canvas                   black, so text
//                                            must be white

After that, create moveDom() function and run it every time you render the scene requestAnimationFrame()

  • geometry is the geometry of the mesh
  • cube is the mesh you want to create label
    var moveDom = function(){
    vector = geometry.vertices[0].clone();
    vector.applyMatrix4(cube.matrix);
    vector.project(camera);
    vector.x = (vector.x * innerWidth/2) + innerWidth/2;
    vector.y = -(vector.y * innerHeight/2) + innerHeight/2;
    //Get the DOM element and apply transforms on it
    document.querySelectorAll(".element")[0].style.webkitTransform = "translate("+vector.x+"px,"+vector.y+"px)";
    document.querySelectorAll(".element")[0].style.transform = "translate("+vector.x+"px,"+vector.y+"px)";
    };

You can create a for loop to set label for all the mesh in your scene.

Because this trick only set 2D position of DOM Element, the size of label is the same even if you zoom (the label is not part of three.js scene).

Full test case: https://jsfiddle.net/0L1rpayz/1/

var renderer, scene, camera, cube, vector, geometry;

var ww = window.innerWidth,
	wh = window.innerHeight;

function init(){

	renderer = new THREE.WebGLRenderer({canvas : document.getElementById('scene')});
	renderer.setSize(ww,wh);

	scene = new THREE.Scene();

	camera = new THREE.PerspectiveCamera(50,ww/wh, 0.1, 10000 );
	camera.position.set(0,0,500);
	scene.add(camera);

	light = new THREE.DirectionalLight(0xffffff, 1);
	light.position.set( 0, 0, 500 );
	scene.add(light);

	//Vector use to get position of vertice
	vector = new THREE.Vector3();

	//Generate Not Earth
	geometry = new THREE.BoxGeometry(50,50,50);
	var material = new THREE.MeshLambertMaterial({color: 0x00ff00});
  cube = new THREE.Mesh(geometry, material);
  scene.add(cube);
	//Render my scene
	render();
}
var moveDom = function(){
    vector = geometry.vertices[0].clone();
    vector.applyMatrix4(cube.matrix);
    vector.project(camera);
	  vector.x = (vector.x * ww/2) + ww/2;
	  vector.y = -(vector.y * wh/2) + wh/2;

    //Get the DOM element and apply transforms on it
   	document.querySelectorAll(".element")[0].style.webkitTransform = "translate("+vector.x+"px,"+vector.y+"px)";
   	document.querySelectorAll(".element")[0].style.transform = "translate("+vector.x+"px,"+vector.y+"px)";
};


var counter = 0;
var render = function (a) {
	requestAnimationFrame(render);

	counter++;

	//Move my cubes
		cube.position.x = Math.cos((counter+1*150)/200)*(ww/6+1*80);
		cube.position.y = Math.sin((counter+1*150)/200)*(70+1*80);
		cube.rotation.x += .001*1+.002;
		cube.rotation.y += .001*1+.02;

		//Move my dom elements
		moveDom();
	
	renderer.render(scene, camera);
};

init();
body,html, canvas{width:100%;height:100%;padding:0;margin:0;overflow: hidden;}
.element{color:white;position:absolute;top:0;left:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<!-- My scene -->
	<canvas id="scene"></canvas>
	<div class="element">
    <h1>Not Earth</h1>
	</div>

If you downvote this, please tell me why. I will try my best to improve my posts.

Comments