javascript - 如何添加标签/标签以显示在多个对象的顶部,以便在用户单击对象时标签始终面向相机?

标签 javascript three.js webgl

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

我该怎么做?

我被告知要使用正交相机(但我不确定如何使用?)和标签的 CSS(参见上一篇文章:How can I make my text labels face the camera at all times? Perhaps using sprites?)

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)缩放到 Canvas (0 到 canvas.width)坐标;我不确定。)
  • 使用 X 和 Y 为您的标签设置 CSS 绝对定位。

这是我在 Cubes 中编写的用于执行相同操作的代码项目(不使用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";
  }
}

Permalink on GitHub

关于javascript - 如何添加标签/标签以显示在多个对象的顶部,以便在用户单击对象时标签始终面向相机?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16151255/

相关文章:

Javascript if 语句失败

javascript - 如何在 Highcharts 中隐藏鼠标悬停时的特定点和标签

javascript - 可以从 Angular 4 组件模板属性调用函数吗?

javascript - 无需 Three.js 即可对 JSON 模型进行动画处理

javascript - webGL 忽略背面?

javascript - 我可以做些什么来优化我的 WebGL 动画?

javascript - 滚动上的 jQuery 无法与 JS 上的 createElement 一起使用

python - Three.js - 使用 python 导出 obj 文件

javascript - 尝试通过 A-Frame 内的 Three.js 编辑 Material 的侧面属性会导致渲染不正确

javascript - THREE.js - 如何实现这种效果?