Three JS Keep Label Size On Zoom

前端 未结 4 531
臣服心动
臣服心动 2020-12-05 16:08

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

相关标签:
4条回答
  • 2020-12-05 16:14

    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.

    0 讨论(0)
  • 2020-12-05 16:17

    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.

    And it's very simple. Let's say, a THREE.Sprite() object (label) is a child of a THREE.Mesh() object (planet), then in your animation loop you need to do

    var scaleVector = new THREE.Vector3();
    var scaleFactor = 4;
    var sprite = planet.children[0];
    var scale = scaleVector.subVectors(planet.position, camera.position).length() / scaleFactor;
    sprite.scale.set(scale, scale, 1); 
    

    I've made a very simple example of the Solar System, using this technique.

    0 讨论(0)
  • 2020-12-05 16:23

    For the benefit of future visitors, the transform controls example does exactly this:

    https://threejs.org/examples/misc_controls_transform.html

    Here's how its done in the example code:

    var factor;
    if ( this.camera.isOrthographicCamera ) {
    	factor = ( this.camera.top - this.camera.bottom ) / this.camera.zoom;
    } else {
    	factor = this.worldPosition.distanceTo( this.cameraPosition ) * Math.min( 1.9 * Math.tan( Math.PI * this.camera.fov / 360 ) / this.camera.zoom, 7 );
    }
    handle.scale.set( 1, 1, 1 ).multiplyScalar( factor * this.size / 7 );

    0 讨论(0)
  • 2020-12-05 16:25

    If you are using spriteMaterial to present your text, you could try to set the sizeAttenuation attribute to false.

    var spriteMaterial = new THREE.SpriteMaterial( { map: spriteMap, color: 0xffffff, sizeAttenuation:false } );
    

    See more information from here: https://threejs.org/docs/index.html#api/en/materials/SpriteMaterial.sizeAttenuation

    0 讨论(0)
提交回复
热议问题