最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How do I add a taglabel to appear on top of several objects so that the tag always faces the camera when the user c

programmeradmin5浏览0评论

Essentially, what I am saying is I want to create a tag or label that appears on top of/on the surface of an object so that the tag always faces the camera when the user clicks the object even when the object is rotated.

How do I go about doing so?

I was told to use Orthogonal camera (but I'm not sure how?) and CSS for the label (see previous post: How can I make my text labels face the camera at all times? Perhaps using sprites?)

The label in CSS and html is below. However, I want to do this for several objects as well, so I guess I can make a list of all the tags I want for each cube in this case.

CSS:

label {
    vertical-align: middle;
    display: table-cell;
    background-color : #99FFCC;
border: 1px solid #008000;
width: 150px;
}

HTML:

<div id="Cube1">
    <label>Cube 1</label>
</div>

Previous Code:

<!DOCTYPE html>
<html lang="en">
<head>
        <title>three.js canvas - interactive - cubes</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style>
                body {
                        font-family: Monospace;
                        background-color: #f0f0f0;
                        margin: 0px;
                        overflow: hidden;
                }
        </style>
</head>
<body>

        <script src="js/three.min.js"></script>

        <script src="js/stats.min.js"></script>

        <script>

                var container, stats;
                var camera, scene, projector, renderer;
                var projector, mouse = { x: 0, y: 0 }, INTERSECTED;
                var particleMaterial;
                var currentLabel = null;

                var objects = [];

                init();
                animate();

                function init() {

                        container = document.createElement( 'div' );
                        document.body.appendChild( container );

                        var info = document.createElement( 'div' );
                        info.style.position = 'absolute';
                        info.style.top = '10px';
                        info.style.width = '100%';
                        info.style.textAlign = 'center';
                        info.innerHTML = '<a href="" target="_blank">three.js</a> - clickable objects';
                        container.appendChild( info );

                        camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
                        camera.position.set( 0, 300, 500 );

                        scene = new THREE.Scene();

                        var geometry = new THREE.CubeGeometry( 100, 100, 100 );

                        for ( var i = 0; i < 10; i ++ ) {

                                var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, opacity: 0.5 } ) );
                                object.position.x = Math.random() * 800 - 400;
                                object.position.y = Math.random() * 800 - 400;
                                object.position.z = Math.random() * 800 - 400;

                                object.scale.x = Math.random() * 2 + 1;
                                object.scale.y = Math.random() * 2 + 1;
                                object.scale.z = Math.random() * 2 + 1;

                                object.rotation.x = Math.random() * 2 * Math.PI;
                                object.rotation.y = Math.random() * 2 * Math.PI;
                                object.rotation.z = Math.random() * 2 * Math.PI;

                                object.label = "Object " + i;

                                scene.add( object );

                                objects.push( object );

                        }

                        var PI2 = Math.PI * 2;
                        particleMaterial = new THREE.ParticleCanvasMaterial( {

                                color: 0x000000,
                                program: function ( context ) {

                                        context.beginPath();
                                        context.arc( 0, 0, 1, 0, PI2, true );
                                        context.closePath();
                                        context.fill();

                                }

                        } );

                        projector = new THREE.Projector();

                        renderer = new THREE.CanvasRenderer();
                        renderer.setSize( window.innerWidth, window.innerHeight );

                        container.appendChild( renderer.domElement );

                        stats = new Stats();
                        stats.domElement.style.position = 'absolute';
                        stats.domElement.style.top = '0px';
                        container.appendChild( stats.domElement );

                        document.addEventListener( 'mousedown', onDocumentMouseDown, false );

                        //

                        window.addEventListener( 'resize', onWindowResize, false );

                }

                function onWindowResize() {

                        camera.aspect = window.innerWidth / window.innerHeight;
                        camera.updateProjectionMatrix();

                        renderer.setSize( window.innerWidth, window.innerHeight );

                }

                function onDocumentMouseDown( event ) {

                        event.preventDefault();

                        var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
                        projector.unprojectVector( vector, camera );

                        var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );

                        var intersects = raycaster.intersectObjects( objects );

                        if ( intersects.length > 0 ) {

                        if ( intersects[ 0 ].object != INTERSECTED ) 
                                        {

                                         // restore previous intersection object (if it exists) to its original color
                                        if ( INTERSECTED ) {


                                        INTERSECTED.material.color.setHex( INTERSECTED.currentHex ); } 

                                        // store reference to closest object as current intersection object
                                        INTERSECTED = intersects[ 0 ].object;
                                        // store color of closest object (for later restoration)
                                        INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
                                        // set a new color for closest object
                                        INTERSECTED.material.color.setHex( 0xffff00 );

                                        var canvas1 = document.createElement('canvas');
                                        var context1 = canvas1.getContext('2d');
                                        context1.font = "Bold 40px Arial";
                                        context1.fillStyle = "rgba(255,0,0,0.95)";
                                        context1.fillText(INTERSECTED.label, 0, 50);

                                        // canvas contents will be used for a texture
                                        var texture1 = new THREE.Texture(canvas1) 
                                        texture1.needsUpdate = true;

                                        var material1 = new THREE.MeshBasicMaterial( {map: texture1, side:THREE.DoubleSide } );
                                        material1.transparent = true;

                                        var mesh1 = new THREE.Mesh(
                                        new THREE.PlaneGeometry(canvas1.width, canvas1.height),
                                        material1



                                );
                                        mesh1.position = intersects[0].point;
                                        if (currentLabel)
                                                scene.remove(currentLabel);
                                        scene.add( mesh1 );                             
                                        currentLabel = mesh1;
                        } 

                        else // there are no intersections
                                        {
                                // restore previous intersection object (if it exists) to its original color
                                if ( INTERSECTED ) {
                                        console.log("hello");
                                        INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
                                        }
                                        // remove previous intersection object reference
                                        //     by setting current intersection object to "nothing"
                                        INTERSECTED = null;
                                        mesh1 = null; 
                                        mesh1.position = intersects[0].point; 
                                        scene.add( mesh1 );

                                        }






                                //var particle = new THREE.Particle( particleMaterial );
                                //particle.position = intersects[ 0 ].point;
                                //particle.scale.x = particle.scale.y = 8;
                                //scene.add( particle );

                        }

                        /*
                        // Parse all the faces
                        for ( var i in intersects ) {

                                intersects[ i ].face.material[ 0 ].color.setHex( Math.random() * 0xffffff | 0x80000000 );

                        }
                        */


                }

                //

                function animate() {

                        requestAnimationFrame( animate );

                        render();
                        stats.update();

                }

                var radius = 600;
                var theta = 0;

                function render() {

                        theta += 0.1;

                        camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
                        camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
                        camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
                        camera.lookAt( scene.position );

                        renderer.render( scene, camera );

                }

        </script>

</body>

Please let me know if I'm being unclear.

Essentially, what I am saying is I want to create a tag or label that appears on top of/on the surface of an object so that the tag always faces the camera when the user clicks the object even when the object is rotated.

How do I go about doing so?

I was told to use Orthogonal camera (but I'm not sure how?) and CSS for the label (see previous post: How can I make my text labels face the camera at all times? Perhaps using sprites?)

The label in CSS and html is below. However, I want to do this for several objects as well, so I guess I can make a list of all the tags I want for each cube in this case.

CSS:

label {
    vertical-align: middle;
    display: table-cell;
    background-color : #99FFCC;
border: 1px solid #008000;
width: 150px;
}

HTML:

<div id="Cube1">
    <label>Cube 1</label>
</div>

Previous Code:

<!DOCTYPE html>
<html lang="en">
<head>
        <title>three.js canvas - interactive - cubes</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style>
                body {
                        font-family: Monospace;
                        background-color: #f0f0f0;
                        margin: 0px;
                        overflow: hidden;
                }
        </style>
</head>
<body>

        <script src="js/three.min.js"></script>

        <script src="js/stats.min.js"></script>

        <script>

                var container, stats;
                var camera, scene, projector, renderer;
                var projector, mouse = { x: 0, y: 0 }, INTERSECTED;
                var particleMaterial;
                var currentLabel = null;

                var objects = [];

                init();
                animate();

                function init() {

                        container = document.createElement( 'div' );
                        document.body.appendChild( container );

                        var info = document.createElement( 'div' );
                        info.style.position = 'absolute';
                        info.style.top = '10px';
                        info.style.width = '100%';
                        info.style.textAlign = 'center';
                        info.innerHTML = '<a href="http://threejs" target="_blank">three.js</a> - clickable objects';
                        container.appendChild( info );

                        camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
                        camera.position.set( 0, 300, 500 );

                        scene = new THREE.Scene();

                        var geometry = new THREE.CubeGeometry( 100, 100, 100 );

                        for ( var i = 0; i < 10; i ++ ) {

                                var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, opacity: 0.5 } ) );
                                object.position.x = Math.random() * 800 - 400;
                                object.position.y = Math.random() * 800 - 400;
                                object.position.z = Math.random() * 800 - 400;

                                object.scale.x = Math.random() * 2 + 1;
                                object.scale.y = Math.random() * 2 + 1;
                                object.scale.z = Math.random() * 2 + 1;

                                object.rotation.x = Math.random() * 2 * Math.PI;
                                object.rotation.y = Math.random() * 2 * Math.PI;
                                object.rotation.z = Math.random() * 2 * Math.PI;

                                object.label = "Object " + i;

                                scene.add( object );

                                objects.push( object );

                        }

                        var PI2 = Math.PI * 2;
                        particleMaterial = new THREE.ParticleCanvasMaterial( {

                                color: 0x000000,
                                program: function ( context ) {

                                        context.beginPath();
                                        context.arc( 0, 0, 1, 0, PI2, true );
                                        context.closePath();
                                        context.fill();

                                }

                        } );

                        projector = new THREE.Projector();

                        renderer = new THREE.CanvasRenderer();
                        renderer.setSize( window.innerWidth, window.innerHeight );

                        container.appendChild( renderer.domElement );

                        stats = new Stats();
                        stats.domElement.style.position = 'absolute';
                        stats.domElement.style.top = '0px';
                        container.appendChild( stats.domElement );

                        document.addEventListener( 'mousedown', onDocumentMouseDown, false );

                        //

                        window.addEventListener( 'resize', onWindowResize, false );

                }

                function onWindowResize() {

                        camera.aspect = window.innerWidth / window.innerHeight;
                        camera.updateProjectionMatrix();

                        renderer.setSize( window.innerWidth, window.innerHeight );

                }

                function onDocumentMouseDown( event ) {

                        event.preventDefault();

                        var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
                        projector.unprojectVector( vector, camera );

                        var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );

                        var intersects = raycaster.intersectObjects( objects );

                        if ( intersects.length > 0 ) {

                        if ( intersects[ 0 ].object != INTERSECTED ) 
                                        {

                                         // restore previous intersection object (if it exists) to its original color
                                        if ( INTERSECTED ) {


                                        INTERSECTED.material.color.setHex( INTERSECTED.currentHex ); } 

                                        // store reference to closest object as current intersection object
                                        INTERSECTED = intersects[ 0 ].object;
                                        // store color of closest object (for later restoration)
                                        INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
                                        // set a new color for closest object
                                        INTERSECTED.material.color.setHex( 0xffff00 );

                                        var canvas1 = document.createElement('canvas');
                                        var context1 = canvas1.getContext('2d');
                                        context1.font = "Bold 40px Arial";
                                        context1.fillStyle = "rgba(255,0,0,0.95)";
                                        context1.fillText(INTERSECTED.label, 0, 50);

                                        // canvas contents will be used for a texture
                                        var texture1 = new THREE.Texture(canvas1) 
                                        texture1.needsUpdate = true;

                                        var material1 = new THREE.MeshBasicMaterial( {map: texture1, side:THREE.DoubleSide } );
                                        material1.transparent = true;

                                        var mesh1 = new THREE.Mesh(
                                        new THREE.PlaneGeometry(canvas1.width, canvas1.height),
                                        material1



                                );
                                        mesh1.position = intersects[0].point;
                                        if (currentLabel)
                                                scene.remove(currentLabel);
                                        scene.add( mesh1 );                             
                                        currentLabel = mesh1;
                        } 

                        else // there are no intersections
                                        {
                                // restore previous intersection object (if it exists) to its original color
                                if ( INTERSECTED ) {
                                        console.log("hello");
                                        INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
                                        }
                                        // remove previous intersection object reference
                                        //     by setting current intersection object to "nothing"
                                        INTERSECTED = null;
                                        mesh1 = null; 
                                        mesh1.position = intersects[0].point; 
                                        scene.add( mesh1 );

                                        }






                                //var particle = new THREE.Particle( particleMaterial );
                                //particle.position = intersects[ 0 ].point;
                                //particle.scale.x = particle.scale.y = 8;
                                //scene.add( particle );

                        }

                        /*
                        // Parse all the faces
                        for ( var i in intersects ) {

                                intersects[ i ].face.material[ 0 ].color.setHex( Math.random() * 0xffffff | 0x80000000 );

                        }
                        */


                }

                //

                function animate() {

                        requestAnimationFrame( animate );

                        render();
                        stats.update();

                }

                var radius = 600;
                var theta = 0;

                function render() {

                        theta += 0.1;

                        camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
                        camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
                        camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
                        camera.lookAt( scene.position );

                        renderer.render( scene, camera );

                }

        </script>

</body>

Please let me know if I'm being unclear.

Share Improve this question edited May 23, 2017 at 12:29 CommunityBot 11 silver badge asked Apr 22, 2013 at 15:48 Kala JKala J 2,0704 gold badges46 silver badges86 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

I'm not especially familiar with Three.js, but here's the usual steps:

  • Choose a point on the surface of your object you want to align the label to.
  • Use projector.projectVector to get an on-screen point from the in-world point.
  • (You may need to scale the result from NDC (-1 to 1) to canvas (0 to canvas.width) coordinates here; I'm not sure.)
  • Use the X and Y to set CSS absolute positioning for your label.

Here's code I wrote to do the same thing in my Cubes project (not using Three.js, but the principles are the same). It is slightly more plex because what it does is position the element so that it is next to an object represented by a set of points (which are provided to the callback passed to pointGenerator). It also tries to do sensible things when the object is out of view of the camera.

Feel free to reuse this code and adapt it to your liking.

// Position an overlay HTML element adjacent to the provided set of points.
function positionByWorld(element, keepInBounds, pointGenerator) {
  var canvasStyle = window.getComputedStyle(theCanvas,null);
  var canvasWidth = parseInt(canvasStyle.width, 10);
  var canvasHeight = parseInt(canvasStyle.height, 10);

  var elemStyle = window.getComputedStyle(element, null);
  var elemWidth = parseInt(elemStyle.width, 10);
  var elemHeight = parseInt(elemStyle.height, 10);

  var slx = Infinity;
  var sly = Infinity;
  var shx = -Infinity;
  var shy = -Infinity;
  var toScreenPoint = vec4.create();

  pointGenerator(function (x, y, z, w) {
    toScreenPoint[0] = x;
    toScreenPoint[1] = y;
    toScreenPoint[2] = z;
    toScreenPoint[3] = w;
    renderer.transformPoint(toScreenPoint);
    toScreenPoint[0] /= toScreenPoint[3];
    toScreenPoint[1] /= toScreenPoint[3];
    toScreenPoint[2] /= toScreenPoint[3];
    if (toScreenPoint[3] > 0) {
      slx = Math.min(slx, toScreenPoint[0]);
      shx = Math.max(shx, toScreenPoint[0]);
      sly = Math.min(sly, toScreenPoint[1]);
      shy = Math.max(shy, toScreenPoint[1]);
    }
  });

  if (shx > -1 && shy > -1 && slx < 1 && sly < 1 /* visible */) {
    // convert to screen
    slx = (slx + 1) / 2 * canvasWidth;
    //shx = (shx + 1) / 2 * canvasWidth;
    //sly = (sly + 1) / 2 * canvasHeight;
    shy = (shy + 1) / 2 * canvasHeight;
    if (keepInBounds) {
      slx = Math.max(0, Math.min(canvasWidth - elemWidth, slx));
      shy = Math.max(0, Math.min(canvasHeight - elemHeight, shy));
    }
    element.style.left   = slx + "px";
    element.style.bottom = shy + "px";
  } else {
    element.style.left   = canvasWidth + "px";
  }
}

—Permalink on GitHub

Have a look at the ThreeJS documentation for Sprites.

A sprite is a plane that always faces towards the camera, generally with a partially transparent texture applied.

Also, there are elements called CSS2DLables, they also face the camera.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论