Reputation: 3317
I made a graphical website with three.js.
It's concept is universe that has so many texts.
When distance of between camera and mesh is close, mesh is text.
But if distance is far, it change to square.
I wonder that change mesh related to distance is possible?
I searched in google few hours, there is no information about this.
code here:
// 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">↓</a>
</div>
<section id="section">
SECTION
</section>
</body>
</html>
(Mouse left click : move forward / right click : move backward) I implemented almost done except change mesh.
Is it possible or any solution about this issue?
Thanks.
Upvotes: 1
Views: 3095
Reputation: 2237
You can use the THREE.LOD() (Level of Detail) to replace the a mesh by another mesh at a certain distance.
The example (https://threejs.org/examples/#webgl_lod) uses only same type of geometry for the different distances. But if you look into the code, each distance has its own geometry and mesh instance.
And so can you, to change to 100% different mesh.
// 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 );
}
EDIT: LOD class modification for smooth transition
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;
}
}
...
}
Of course, material.transparent
property of objects should be set true
, so opacity will work.
Adding square to LOD object
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
Remove opacity modification in animation loop.
Upvotes: 2