我用Three.js创建了一个网站来进行3D工作。
它的概念是宇宙,有很多文字。
我必须实现两件事。
首先。当相机和物体之间的距离较远时,物体就是星星。
第二。相机和物体之间的距离近时,物体是文本。
因此,我使用LOD来实现它。
一切正常。
但是,星星<->文本更改的训练是如此粗糙。
我要在其中插入平滑变化过渡(动画)。
因此,我阅读了一份文档,可悲的是,它具有任何属性。
是否可以在LOD中插入动画?
或对此有任何可能的解决方案?
[源代码]
// VARIABLES
let clock, camera, scene, renderer, mixer;
var myElement = document.getElementById("threejs");
const mouse = new THREE.Vector2();
const 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 textCount = 200;
checkMobile()
init();
function init() {
// CAMERA
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2100 );
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 1200;
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0xf05fa6, 300, 400);
clock = new THREE.Clock();
// HELPER
const gridHelper = new THREE.PolarGridHelper( 8, 16 );
scene.add( gridHelper );
// LIGHT
const ambientLight = new THREE.AmbientLight( 0xffffff, 0.2 );
scene.add( ambientLight );
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 1, 1, - 1 );
scene.add( directionalLight );
// CONTROLS
if(isMobile) {
var controls = new THREE.DeviceOrientationControls(camera);
console.log('isMobile true');
} else {
console.log('isMobile false');
}
// ADD MESH
var group = new THREE.Group();
var size = ( isMobile ? 2 : 4 );
var starsLights = new THREE.Group();
for ( let i = 0; i < textCount; i ++ ) {
var lod = new THREE.LOD();
// Star
var geometry = new THREE.SphereGeometry(0.3, 16, 16);
var material = new THREE.MeshBasicMaterial({color: 0xffffff});
var star = new THREE.Mesh(geometry, material);
// Text
let sprite = new THREE.TextSprite({
textSize: size,
redrawInterval: 250,
texture: {
text: 'TEST TEST TEST',
fontFamily: 'Arial, Helvetica, sans-serif',
},
material: {
color: 'white',
},
});
// Star Light
var velX = (Math.random() + 0.1) * 0.1 * (Math.random()<0.5?-1:1);
var velY = (Math.random() + 0.1) * 0.1 * (Math.random()<0.5?-1:1);
star.vel = new THREE.Vector2(velX, velY);
var starLight = new THREE.PointLight(0x000000, 0.8, 3);
starLight.position.copy(star.position);
starLight.position.y += 0.5;
starsLights.add(starLight);
// Add
lod.addLevel(sprite, 1);
lod.addLevel(star, 200);
lod.position.x = Math.random() * 180-100;
lod.position.y = Math.random() * 180-100;
lod.position.z = Math.random() * 1000-40;
group.add(lod);
// group.add(starsLights);
}
scene.add(group);
scene.add(starLight);
// Renderer
renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.getElementById("universe").appendChild(renderer.domElement);
// Event handler
window.addEventListener( 'resize', onWindowResize, false );
document.addEventListener('mousemove', onMouseMove, false);
document.addEventListener('mousewheel', onMouseWheel, false);
document.addEventListener('contextmenu', onContextMenu, false);
function animate() {
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();
}
// Object change related to distance
group.children.forEach(function(child) {
child.update(camera);
var distance = camera.position.distanceTo(child.position);
var opacity = -1 / 400 * distance + 1;
if (opacity < 0) {
opacity = 0;
}
child.getObjectForDistance(distance).material.opacity = opacity;
})
// Render
requestAnimationFrame( animate );
render(scene, camera);
}
animate();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onMouseWheel(event) {
if(camera.position.z > 100) {
event.preventDefault();
camera.position.z -= event.deltaY * 0.2;
} else {
}
}
function render() {
const delta = clock.getDelta();
if ( mixer !== undefined ) mixer.update( delta );
renderer.render( scene, camera );
}
function onTransitionEnd( event ) {
console.log("Loading Complete");
event.target.remove();
}
// Exist functions
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;
}
}
function 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;
}
function 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 );
}
function onContextMenu(event) {
event.preventDefault();
}
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
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>
<section id="universe"></section>
<script src="src.js"></script>
<section id="section">
SECTION
</section>
</body>
</html>
答案 0 :(得分:0)
THREE.LOD
无法满足您的需求。但是编写一个自定义的LOD类并不难,它可以在两个级别之间执行平滑的alpha混合。这种方法的优点是,它被认为比离散的LOD更连续。但是,由于在过渡过程中渲染了两个对象,因此在计算上会更加昂贵。
以下小提琴说明了这种方法(我相信可以实现这种更优雅的方法,但至少是一个起点^^):https://jsfiddle.net/f2Lommf5/14585/
创建LOD对象时,可以调整定义过渡程度的阈值。
const lod = new LOD();
lod.threshold = 250;