【问题标题】:three.js change object dynamically related to distance三.js 动态改变与距离相关的对象
【发布时间】:2018-10-01 21:35:45
【问题描述】:

我用three.js做了一个图形网站。

它的概念是有这么多文本的宇宙。

当相机和网格之间的距离很近时,网格是文本。

但是如果距离很远,它会变成正方形。

我想知道与距离相关的改变网格是可能的吗?

我在谷歌搜索了几个小时,没有这方面的信息。

代码在这里:

// Define Variables
var myElement = document.getElementById("threejs");
let camera, scene, renderer;
const mouse = new THREE.Vector2();
clicked = new THREE.Vector2();
const target = new THREE.Vector2();
const windowHalf = new THREE.Vector2( window.innerWidth / 2, window.innerHeight / 2 );
const moveState = {forward: 0, back: 0};
var isMobile = false;
var hold = -1;

/****** Define Function ******/
/*****************************/

checkMobile = () => {
  var UserAgent = navigator.userAgent;

  if (UserAgent.match(/iPhone|iPod|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i) != null || UserAgent.match(/LG|SAMSUNG|Samsung/) != null) {
      isMobile = true;
  } else {
      isMobile = false;
  }
}
checkMobile();

onMouseMove = (event) => {
  mouse.x = ( (event.clientX/2) - (windowHalf.x/2) );
  mouse.y = ( (event.clientY/2) - (windowHalf.y/2) );
  clicked.x = ( event.clientX / window.innerWidth ) * 2 - 1;
  clicked.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}


onResize = (event) => {
  const width = window.innerWidth;
  const height = window.innerHeight;
  
  windowHalf.set( width / 2, height / 2 );
	
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize( width, height );
}

onContextMenu = (event) => { 
  event.preventDefault();
}

onMouseDown = (event) => {
  hold = event.which;
}

onMouseUp = () => {
  hold = -1;
};

// TEST

//

// Start Script
init = () => {
  camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 5000 );
  camera.position.x = 0;
  camera.position.y = 0;
  camera.position.z = 1000;

  scene = new THREE.Scene();

  const geometry = new THREE.BoxBufferGeometry();
  const material = new THREE.MeshNormalMaterial({ transparent: true });
  
  if(isMobile) {
    var controls = new THREE.DeviceOrientationControls(camera);
  } else {
    console.log('isMobile false');
  }
  
  group = new THREE.Group();
  
  for ( let i = 0; i < 800; i ++ ) {   
    let sprite = new THREE.TextSprite({
      textSize: 2,
      redrawInterval: 1,
      texture: {
        text: 'TEST',
        fontFamily: 'Arial, Helvetica, sans-serif',
      },
      material: {
        color: 'white',
      },
    });
    sprite.position.x = Math.random() * 180-100;
    sprite.position.y = Math.random() * 180-100;
    sprite.position.z = Math.random() * 1000-40;
    group.add(sprite);

  }
  scene.add(group);

  renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );
  
  // Event handler
  document.addEventListener('mousemove', onMouseMove, false);
  document.addEventListener('mousedown', onMouseDown, false);
  document.addEventListener('mouseup', onMouseUp, false);
  document.addEventListener('contextmenu', onContextMenu, false);
  
  window.addEventListener('resize', onResize, false);
  
  // Helper
  var axesHelper = new THREE.AxesHelper( 15 );
  scene.add( axesHelper );

  animate = () => {
    // For camera follow mouse cursor
    target.x = ( 1 - mouse.x ) * 0.002;
    target.y = ( 1 - mouse.y ) * 0.002;
    camera.rotation.x += 0.05 * ( target.y - camera.rotation.x );
    camera.rotation.y += 0.05 * ( target.x - camera.rotation.y );
    
    if(isMobile) {
      controls.update();
    }
    switch(hold) {
      case 1:
        if(camera.position.z > 0) {
          camera.position.z -= 4;
        }
        break;
      case 3:
        camera.position.z += 4;
        break;
    }
    
    // Object opacity related to distance between camera and object
    for (i = 0; i < 800; i++) {
      var distance = camera.position.distanceTo(group.children[i].position);
      var opacity = -1 / 400 * distance + 1;
      if (opacity < 0) {
        opacity = 0;
      }
      group.children[i].material.opacity = opacity;
    }
    
    requestAnimationFrame( animate );
    renderer.render( scene, camera );
  }
  // Run
  animate();
}
// Run
init();
canvas {
  width: 100%;
  height: 100%;
  
/*  background: #11e8bb;  Old browsers */
/*  background: -moz-linear-gradient(top,  #11e8bb 0%, #8200c9 100%);  FF3.6-15 */
/*  background: -webkit-linear-gradient(top,  #11e8bb 0%,#8200c9 100%);  Chrome10-25,Safari5.1-6 */
/*  background: linear-gradient(to bottom,  #11e8bb 0%,#8200c9 100%);  W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
  background: radial-gradient(circle, #ed1654, #f61e6c, #f76098);
  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#11e8bb', endColorstr='#8200c9',GradientType=0 ); /* IE6-9 */
}

body {
  margin: 0;
}
canvas {
  width: 100%;
  height: 100%;
}
#threejs {
  position: absolute;
  overflow: hidden;
  width: 100%;
  height: 100%;
}
header {
  position: fixed;
  z-index: 9999;
  background-color: white;
  width: 100%;
  top: 0;
  display: flex;
  align-items: center;
  height: 50px;
}
/* Header Left */
.header-left {
  display: flex;
  justify-content: center;
  flex: 1;
}
.header-left img {
  width: 80px;
  height: 20px;
}
/* Header Right */
.header-right {
  flex: 1;
  padding-left: 200px;
}
.header-right a {
  text-decoration: none;
  color: black;
  font-weight: 600;
}
.header-right a:nth-child(2) {
  padding-left: 50px;
  padding-right: 50px;
}
/* Main Company */
.down-btn {
  display: flex;
  position: absolute;
  justify-content: center;
  align-items: center;
  bottom: 0;
  color: white;
  left: 50%;
  font-size: 2rem;
  cursor: pointer;
}
.down-btn a {
  text-decoration: none;
  color: white;
  padding-bottom: 20px;
}
/* Section */
section {
  background-color: aliceblue;
  height: 100%;
}
<html>
<head>
  <link rel="stylesheet" type="text/css" href="style.css">
<!--  <script src="three.js"></script>-->
  <script src="https://cdn.rawgit.com/mrdoob/three.js/master/build/three.min.js"></script> 
  <script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
  <script src="https://unpkg.com/three.texttexture"></script>
  <script src="https://unpkg.com/three.textsprite"></script>
  <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
</head>
<body>
  <header>
    <div class="header-left">
      <a href="#">MAIN</a>
    </div>
    <div class="header-right">
      <a href="#">ABOUT</a>
      <a href="#">PRODUCT</a>
      <a href="#">CONTACT</a>
    </div>
  </header>
  <div id="threejs"></div>
  <script src="src.js"></script>
  <div class="down-btn">
    <a href="#section">&darr;</a>
  </div>
  <section id="section">
    SECTION
  </section>
</body>
</html>

(鼠标左键:前进/右键:后退) 除了更改网格,我几乎完成了。

这个问题有可能或任何解决方案吗?

谢谢。

【问题讨论】:

标签: three.js


【解决方案1】:

您可以使用THREE.LOD()(细节级别)在一定距离处将一个网格替换为另一个网格。

示例 (https://threejs.org/examples/#webgl_lod) 仅对不同距离使用相同类型的几何图形。但是如果你查看代码,每个距离都有自己的几何和网格实例。

你也可以,换成 100% 不同的网格。

// create meshes and LOD objects

for ( let i = 0; i < 800; i ++ ) {

  let lod = new THREE.LOD();

  let sprite = new THREE.TextSprite(...);

  let squareGeo = new THREE.PlaneBufferGeometry(2,2),
      squareMat = new THREE.MeshBasicMaterial(),
      square = new THREE.Mesh(squareGeo, squareMat);

  lod.addLevel(sprite, 1);
  lod.addLevel(square, 100); // will be visible from 100 and beyond

  lod.position.x = Math.random() * 180-100;
  lod.position.y = Math.random() * 180-100;
  lod.position.z = Math.random() * 1000-40;

  group.add(lod);
}

// animation loop
function animate() {

  // ...

  group.children.forEach(function (child) {
    // LOD update
    child.update(camera);

    // opacity
    var distance = camera.position.distanceTo(child.position);
    var opacity = -1 / 400 * distance + 1;
    if (opacity < 0) {
      opacity = 0;
    }
    child.getObjectForDistance(distance).material.opacity = opacity;

  });

  requestAnimationFrame( animate );
  renderer.render( scene, camera );

}

编辑: LOD class 修改平滑过渡

addLevel: function ( object, distance, fadeDistance ) {

    ...

    levels.splice( l, 0, { distance: distance, fadeDistance: fadeDistance || distance, object: object } );

    ...

}

update: function () {

    ...

    levels[ 0 ].object.visible = true;
    levels[ 0 ].object.material.opacity = 1.0;

    for ( var i = 1, l = levels.length; i < l; i ++ ) {

        if ( distance >= levels[ i ].distance && distance < levels[ i ].fadeDistance ) {

            levels[ i ].object.visible = true;

            var t = (distance - levels[i].distance) / (levels[i].fadeDistance - levels[i].distance);
            levels[ i - 1 ].object.material.opacity = 1.0 - t;
            levels[ i ].object.material.opacity = t;

        } else if ( distance >= levels[ i ].fadeDistance ) {

            levels[ i - 1 ].object.visible = false;
            levels[ i ].object.visible = true;
            levels[ i ].object.material.opacity = 1.0;

        } else {

            break;

        }

    }

    ...

}

当然,对象的material.transparent属性应该设置为true,这样不透明度就可以了。

将正方形添加到 LOD 对象

lod.addLevel(sprite, 1);
lod.addLevel(star, 100, 140); // will fade in at distance of 100 to 140, fully visible beyond 140
lod.addLevel(dummy, 200, 400); // if dummy is an object with empty geometry, star will fade out between 200 and 400

移除动画循环中的不透明度修改。

【讨论】:

  • 完美运行。然而,变化是如此的艰难。能顺利改变吗?
  • 我要插入平滑变化动画。
  • 好吧,不是开箱即用。您需要修改 LOD 类,以便在更新时它不仅设置可见性,而且还会在一定距离范围内插入两个对象的不透明度。
  • 你的意思是,动画没有默认属性,对吧?所以,我必须修改 LOD js 文件。
  • 是的。我已经更新了我的答案。这就是我修改 LOD 类的方式。如果我没有遗漏重要的东西,它应该像这样工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多