我如何添加一个标签/标签出现在几个对象的顶部,以便当用户单击对象时,标签始终面向相机

How do I add a tag/label to appear on top of several objects so that the tag always faces the camera when the user clicks the object?

本文关键字:标签 对象 便当 顶部 相机 用户 单击 添加 何添加 一个 几个      更新时间:2023-09-26

本质上,我想说的是,我想创建一个标签或标签,出现在对象的顶部/表面上,这样当用户点击对象时,即使对象旋转,标签也始终面向相机。

我该怎么做呢?

我被告知使用正交相机(但我不确定如何?)和CSS标签(见以前的帖子:我如何使我的文本标签在任何时候都面对相机?也许使用精灵?)

CSS和html中的标签如下。然而,我也想对几个对象这样做,所以我想我可以为每个立方体创建一个我想要的所有标签的列表。

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>

前一段代码:

<!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.org" 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>

如果我没说清楚,请让我知道。

我对Three.js不是特别熟悉,但以下是通常的步骤:

  • 在你的对象表面上选择一个你想要对齐标签的点。
  • 使用projector.projectVector从世界内的点获得屏幕上的点。
  • (您可能需要将结果从NDC(-1到1)缩放到画布(0到canvas.width)坐标;我不确定)
  • 使用X和Y来设置标签的CSS绝对定位。

下面是我写的代码,在我的立方体项目中做同样的事情(不使用Three.js,但原理是相同的)。它稍微复杂一些,因为它所做的是定位元素,使它在旁边的对象由一组点表示(这些点提供给传递给pointGenerator的回调)。当物体在相机视野之外时,它也会尝试做一些明智的事情。

请随意重用此代码并根据您的喜好进行调整。

// 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";
  }
}

- GitHub上的永久链接

相关文章: