如何在Three.js世界空间中表示像素宽度?

时间:2016-06-27 22:35:42

标签: three.js

Three.js提供了一个特殊的渲染器examples/js/renderers/CSS2DRenderer,它允许在标准的WebGL渲染场景上使用html叠加(请参阅official demo, here。)

CSS2DRenderer通过CSS转换完成html项的定位。以下是渲染器如何将世界空间与屏幕空间联系起来:

        vector.setFromMatrixPosition( object.matrixWorld );
        vector.applyProjection( viewProjectionMatrix );

        var element = object.element;
        var style = 'translate(-50%,-50%) translate(' + ( vector.x * _widthHalf + _widthHalf ) + 'px,' + ( - vector.y * _heightHalf + _heightHalf ) + 'px)';

        element.style.WebkitTransform = style;
        element.style.MozTransform = style;
        element.style.oTransform = style;
        element.style.transform = style;

在下面的实时代码段中,我在网格旁边放置了几个文本元素,就像数据图中的轴标签一样。 我的问题是在three.js世界空间中为html标签选择一个位置来考虑它们的像素宽度。我用一个平面框住每个标签以显示到网格边缘的间隙 - 我需要消除这个差距!



var renderer, labelRenderer, scene, camera, controls, sprite, stats, rot, planes, ctx, fontFamily, fontSize;

rot = 0; // this drives load(?)

init();
//animate();
render();

function init() {
  fontFamily = "monospace";
  fontSize = "10px";
  stats = new Stats();
  stats.showPanel(1);
  document.body.appendChild(stats.dom);
  var canvas = document.createElement('canvas')
  ctx = canvas.getContext('2d')
  ctx.font = fontSize + " " + fontFamily;

  // renderer
  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  labelRenderer = new THREE.CSS2DRenderer();
  labelRenderer.setSize(window.innerWidth, window.innerHeight);
  labelRenderer.domElement.style.position = 'absolute';
  labelRenderer.domElement.style.top = '0';
  labelRenderer.domElement.style.pointerEvents = 'none';
  document.body.appendChild(labelRenderer.domElement);

  // scene
  scene = new THREE.Scene();

  // camera
  camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.set(20, 20, 20);

  // controls
  controls = new THREE.OrbitControls(camera);

  // ambient
  scene.add(new THREE.AmbientLight(0x222222));

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

  // axes
  scene.add(new THREE.AxisHelper(20));

  var size = 5;
  var step = 5;

  var gridHelper = new THREE.GridHelper(size, step);
  gridHelper.translateX(5);
  gridHelper.translateZ(5);
  scene.add(gridHelper);

  var geometry, material, text, label;
  planes = new Array(5);
  var texts = ["one", "two", "three", "four", "five"];



  for (var i = 0; i < 5; i++) {
    geometry = new THREE.PlaneGeometry(2, 2);
    material = new THREE.MeshBasicMaterial({
      transparent: true,
      opacity: 0
    });
    planes[i] = new THREE.Mesh(geometry, material);
    planes[i].position.set(10 + 1, 1, i * 2 + 1)
    planes[i].lookAt(camera.position)
    scene.add(planes[i]);
    scene.add(new THREE.EdgesHelper(planes[i]))

    text = document.createElement('div');
    text.className = 'label';
    text.style.color = "white";
    text.style["font-family"] = fontFamily;
    text.style["font-size"] = fontSize;
    text.textContent = texts[i];
    var textWidth = ctx.measureText(texts[i]).width;
    console.log("textWidth", textWidth);
    label = new THREE.CSS2DObject(text);
    label.position.copy(planes[i].position);
    scene.add(label);
    console.log("label", label);
  }

}

function randomPos(scale) {
  return scale * Math.random();
}

function render() {

  renderer.render(scene, camera);
  labelRenderer.render(scene, camera);
  var x = camera.position.x;
  var z = camera.position.z;
  camera.position.x = x * Math.cos(rot) + z * Math.sin(rot);
  camera.position.z = z * Math.cos(rot) - x * Math.sin(rot);
  camera.lookAt(scene.position);
  requestAnimationFrame(render);
  planes.forEach(function(plane) {
    plane.lookAt(camera.position);
  });
  stats.update();

}

function animate() {

  requestAnimationFrame(animate);
  //controls.update();
  renderer.render(scene, camera);
  stats.update();

}
&#13;
body {
  background-color: #000;
  margin: 0px;
  overflow: hidden;
}
&#13;
<script src="https://rawgit.com/mrdoob/three.js/dev/build/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/renderers/CSS2DRenderer.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/libs/stats.min.js"></script>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:0)

像素宽度不是您的问题。它的起源。您正在抵消头寸(10 + 1,i,i * 2 + 1)... https://jsfiddle.net/7j4jypfL/1/

label = new THREE.CSS2DObject(text);
label.position.set(10+(0.5),0, (i * 2)+0.5);
scene.add(label);

你需要记住,在OpenGL中,广告牌之类的东西是以原点为中心的(即-1,-1到1,1),因此(0,0)将是中心。

你需要在比例尺1测量,当你进行缩放等时,由于透视数学,它会正确排列。看看我的小提琴,我将你的位置改为纯粹的(10,i,i * 2)并看看文本是如何排列的。如果你想从那里向下移动,(10 +(0.5),0,(i * 2)+0.5)