Элементы управления положением камеры и устройства на примере waves

#javascript #three.js

#javascript #three.js

Вопрос:

У меня есть веб-сайт, где мне нужно использовать threejs. Я использовал пример waves на целевой странице, и он хорошо работает на настольных компьютерах / ноутбуках.

Тем не менее, мне нужно иметь возможность перемещать waves (аналогично поведению мыши на рабочем столе), но с помощью гироскопа / акселерометра устройства, и это работает с использованием devicecontrols, который я нашел в другом примере. Проблема в том, что я впервые использую three и работаю с 3d, и я заблудился. Точки действительно маленькие, кажется, что они движутся только по очень маленькой линии, и я не знаю, как или где должна выглядеть камера.

Например,

На мобильном устройстве должно выглядеть так же, как на рабочем столе, и волны должны перемещаться, но только горизонтально…

Возможно ли это?

Мобильный скрипт

 <script>
	if (screen amp;amp; screen.width < 900) {
		//alert("Mobile");
		if ( WEBGL.isWebGLAvailable() === false ) {
				document.body.appendChild( WEBGL.getWebGLErrorMessage() );
			}
			var SEPARATION = 100, AMOUNTX = 50, AMOUNTY = 50;
			var container;
			var camera, scene, renderer, controls;
			var particles, count = 0;
			var mouseX = 0, mouseY = 0;
			var windowHalfX = window.innerWidth / 2;
			var windowHalfY = window.innerHeight / 2;
			init();
			animate();
			function init() {
				container = document.createElement( 'div' );
				document.body.appendChild( container );
                camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
                controls = new THREE.DeviceOrientationControls( camera );
				camera.position.z = 1000;
				scene = new THREE.Scene();
				//
				var numParticles = AMOUNTX * AMOUNTY;
				var positions = new Float32Array( numParticles * 3 );
				var scales = new Float32Array( numParticles );
				var i = 0, j = 0;
				for ( var ix = 0; ix < AMOUNTX; ix    ) {
					for ( var iy = 0; iy < AMOUNTY; iy    ) {
						positions[ i ] = ix * SEPARATION - ( ( AMOUNTX * SEPARATION ) / 2 ); // x
						positions[ i   1 ] = 0; // y
						positions[ i   2 ] = iy * SEPARATION - ( ( AMOUNTY * SEPARATION ) / 2 ); // z
						scales[ j ] = 1;
						i  = 3;
						j   ;
					}
				}
				var geometry = new THREE.BufferGeometry();
				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
				geometry.addAttribute( 'scale', new THREE.BufferAttribute( scales, 1 ) );
				var material = new THREE.ShaderMaterial( {
					uniforms: {
						color: { value: new THREE.Color( 0xffffff ) },
					},
					vertexShader: document.getElementById( 'vertexshader' ).textContent,
					fragmentShader: document.getElementById( 'fragmentshader' ).textContent
				} );
				particles = new THREE.Points( geometry, material );
				scene.add( particles );
				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				container.appendChild( renderer.domElement );
				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
				window.addEventListener( 'resize', onWindowResize, false );
			}

            function animate() {
				window.requestAnimationFrame( animate );
				controls.update();
				renderer.render( scene, camera );
			}
			function onWindowResize() {
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();
				renderer.setSize( window.innerWidth, window.innerHeight );
			}
	}
</script>  

Решение

 console.log("Desktop");
if (WEBGL.isWebGLAvailable() === false) {
	document.body.appendChild(WEBGL.getWebGLErrorMessage());
}
var SEPARATION = 100,
	AMOUNTX = 50,
	AMOUNTY = 50;
var container;
var camera, scene, renderer, controls;
var particles, count = 0;
var mouseX = 0,
	mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

// var isMobile = isOSMobile();
var isMobile = true;

init();
animate();

function init() {
	container = document.createElement('div');
	document.body.appendChild(container);
	camera = new THREE.PerspectiveCamera(75, window.innerWidth /
		window.innerHeight, 1, 10000);
	if (isMobile) {
		controls = new THREE.DeviceOrientationControls(camera);
	}
	camera.position.z = 1000;
	scene = new THREE.Scene();
	//
	var numParticles = AMOUNTX * AMOUNTY;
	var positions = new Float32Array(numParticles * 3);
	var scales = new Float32Array(numParticles);
	var i = 0,
		j = 0;
	for (var ix = 0; ix < AMOUNTX; ix  ) {
		for (var iy = 0; iy < AMOUNTY; iy  ) {
			positions[i] = ix * SEPARATION - ((AMOUNTX *
				SEPARATION) / 2); // x
			positions[i   1] = 0; // y
			positions[i   2] = iy * SEPARATION - ((AMOUNTY *
				SEPARATION) / 2); // z
			scales[j] = 1;
			i  = 3;
			j  ;
		}
	}
	var geometry = new THREE.BufferGeometry();
	geometry.addAttribute('position', new THREE.BufferAttribute(
		positions, 3));
	geometry.addAttribute('scale', new THREE.BufferAttribute(scales,
		1));
	var material = new THREE.ShaderMaterial({
		uniforms: {
			color: {
				value: new THREE.Color(isMobile ? 0xffffff : 0x8D8D8F)
			},
		},
		vertexShader: document.getElementById(
			'vertexshader').textContent,
		fragmentShader: document.getElementById(
			'fragmentshader').textContent
	});

	particles = new THREE.Points(geometry, material);
	scene.add(particles);

	renderer = new THREE.WebGLRenderer({
		antialias: true
	});
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(window.innerWidth, window.innerHeight);
	container.appendChild(renderer.domElement);
	document.addEventListener('mousemove', onDocumentMouseMove,
		false);
	window.addEventListener('resize', onWindowResize, false);
	if (isMobile) {
		window.addEventListener("deviceorientation", handleOrientation, true);
	}
}

function handleOrientation(e) {
	var absolute = e.absolute;
	var alpha = e.alpha;// x -90 ... 90
	var beta = e.beta;// y 180 ... 0
	var gamma = e.gamma;// x -90 ... 90

	mouseX = -5 * windowHalfX * (gamma / 90);
	//mouseY = -windowHalfY * ((beta - 90) / 90);

	// console.log(mouseX.toFixed(2), ' x ', mouseY.toFixed(2));
}

function onWindowResize() {
	windowHalfX = window.innerWidth / 2;
	windowHalfY = window.innerHeight / 2;
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function onDocumentMouseMove(event) {
	mouseX = event.clientX - windowHalfX;
	mouseY = event.clientY - windowHalfY;
}

//
function animate() {
	requestAnimationFrame(animate);
	render();
	if (isMobile) {
		controls.update();
	}
}

function render() {
	camera.position.x  = (mouseX - camera.position.x) * .05;
	if(isMobile) {
		camera.position.y = 550;
	} else {
		camera.position.y  = (-mouseY - camera.position.y) * .05;
	}
	camera.lookAt(scene.position);
	var positions = particles.geometry.attributes.position.array;
	var scales = particles.geometry.attributes.scale.array;
	var i = 0,
		j = 0;
	for (var ix = 0; ix < AMOUNTX; ix  ) {
		for (var iy = 0; iy < AMOUNTY; iy  ) {
			positions[i   1] = (Math.sin((ix   count) * 0.3) * 50)  
				(Math.sin((iy   count) * 0.5) * 50);
			scales[j] = (Math.sin((ix   count) * 0.3)   1) * 8  
				(Math.sin((iy   count) * 0.5)   1) * 8;
			i  = 3;
			j  ;
		}
	}
	particles.geometry.attributes.position.needsUpdate = true;
	particles.geometry.attributes.scale.needsUpdate = true;
	renderer.render(scene, camera);
	count  = 0.1;
}

function isOSMobile() {
	var userAgent = navigator.userAgent || navigator.vendor || window.opera;

	if (/android/i.test(userAgent)) {
		return true;
	}
	if (/iPad|iPhone|iPod/.test(userAgent) amp;amp; !window.MSStream) {
		return true;
	}

	return false;
}  

Спасибо!

Комментарии:

1. Официальный пример отлично смотрится на моем смартфоне. Не могли бы вы, пожалуйста, поделиться своим кодом в качестве живой демонстрации ? Поскольку ваш код выглядит так, как в примере, это упростило бы отладку проблемы.

2. Исходный код работает, однако он не перемещается при движении устройства (акселерометр / гироскоп). Я хотел бы перемещать волны движением, а не касанием / перемещением мыши. Я попытался смешать пример waves и пример управления ориентацией устройства, но я получаю этот эффект. Вы можете увидеть ошибку здесь (liveforevr.webflow.com ) Спасибо!

3. TBH, я немного смущен предоставленной ссылкой. Я не вижу в вашем коде, где вы создаете экземпляр THREE.DeviceOrientationControls . Кроме того, кажется, вы предполагаете, что волны перемещаются при выполнении взаимодействия. Это неправильно, поскольку взаимодействие изменяет положение камеры (!). В этом контексте важно подчеркнуть, что DeviceOrientationControls изменяет только поворот камеры, а не ее положение. Таким образом, вы не можете добиться такого же поведения примера, просто используя DeviceOrientationControls .

4. Я решил это, проверьте обновление.

5. Как насчет того, чтобы опубликовать ваше решение в качестве ответа, а затем пометить его как решенное? Это лучше, чем публиковать свой ответ в рамках вашего вопроса.

Ответ №1:

Исправлено. Код работает, перемещение по оси Y отключено.

 console.log("Desktop");
if (WEBGL.isWebGLAvailable() === false) {
	document.body.appendChild(WEBGL.getWebGLErrorMessage());
}
var SEPARATION = 100,
	AMOUNTX = 50,
	AMOUNTY = 50;
var container;
var camera, scene, renderer, controls;
var particles, count = 0;
var mouseX = 0,
	mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

// var isMobile = isOSMobile();
var isMobile = true;

init();
animate();

function init() {
	container = document.createElement('div');
	document.body.appendChild(container);
	camera = new THREE.PerspectiveCamera(75, window.innerWidth /
		window.innerHeight, 1, 10000);
	if (isMobile) {
		controls = new THREE.DeviceOrientationControls(camera);
	}
	camera.position.z = 1000;
	scene = new THREE.Scene();
	//
	var numParticles = AMOUNTX * AMOUNTY;
	var positions = new Float32Array(numParticles * 3);
	var scales = new Float32Array(numParticles);
	var i = 0,
		j = 0;
	for (var ix = 0; ix < AMOUNTX; ix  ) {
		for (var iy = 0; iy < AMOUNTY; iy  ) {
			positions[i] = ix * SEPARATION - ((AMOUNTX *
				SEPARATION) / 2); // x
			positions[i   1] = 0; // y
			positions[i   2] = iy * SEPARATION - ((AMOUNTY *
				SEPARATION) / 2); // z
			scales[j] = 1;
			i  = 3;
			j  ;
		}
	}
	var geometry = new THREE.BufferGeometry();
	geometry.addAttribute('position', new THREE.BufferAttribute(
		positions, 3));
	geometry.addAttribute('scale', new THREE.BufferAttribute(scales,
		1));
	var material = new THREE.ShaderMaterial({
		uniforms: {
			color: {
				value: new THREE.Color(isMobile ? 0xffffff : 0x8D8D8F)
			},
		},
		vertexShader: document.getElementById(
			'vertexshader').textContent,
		fragmentShader: document.getElementById(
			'fragmentshader').textContent
	});

	particles = new THREE.Points(geometry, material);
	scene.add(particles);

	renderer = new THREE.WebGLRenderer({
		antialias: true
	});
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(window.innerWidth, window.innerHeight);
	container.appendChild(renderer.domElement);
	document.addEventListener('mousemove', onDocumentMouseMove,
		false);
	window.addEventListener('resize', onWindowResize, false);
	if (isMobile) {
		window.addEventListener("deviceorientation", handleOrientation, true);
	}
}

function handleOrientation(e) {
	var absolute = e.absolute;
	var alpha = e.alpha;// x -90 ... 90
	var beta = e.beta;// y 180 ... 0
	var gamma = e.gamma;// x -90 ... 90

	mouseX = -5 * windowHalfX * (gamma / 90);
	//mouseY = -windowHalfY * ((beta - 90) / 90);

	// console.log(mouseX.toFixed(2), ' x ', mouseY.toFixed(2));
}

function onWindowResize() {
	windowHalfX = window.innerWidth / 2;
	windowHalfY = window.innerHeight / 2;
	camera.aspect = window.innerWidth / window.innerHeight;
	camera.updateProjectionMatrix();
	renderer.setSize(window.innerWidth, window.innerHeight);
}
//
function onDocumentMouseMove(event) {
	mouseX = event.clientX - windowHalfX;
	mouseY = event.clientY - windowHalfY;
}

//
function animate() {
	requestAnimationFrame(animate);
	render();
	if (isMobile) {
		controls.update();
	}
}

function render() {
	camera.position.x  = (mouseX - camera.position.x) * .05;
	if(isMobile) {
		camera.position.y = 550;
	} else {
		camera.position.y  = (-mouseY - camera.position.y) * .05;
	}
	camera.lookAt(scene.position);
	var positions = particles.geometry.attributes.position.array;
	var scales = particles.geometry.attributes.scale.array;
	var i = 0,
		j = 0;
	for (var ix = 0; ix < AMOUNTX; ix  ) {
		for (var iy = 0; iy < AMOUNTY; iy  ) {
			positions[i   1] = (Math.sin((ix   count) * 0.3) * 50)  
				(Math.sin((iy   count) * 0.5) * 50);
			scales[j] = (Math.sin((ix   count) * 0.3)   1) * 8  
				(Math.sin((iy   count) * 0.5)   1) * 8;
			i  = 3;
			j  ;
		}
	}
	particles.geometry.attributes.position.needsUpdate = true;
	particles.geometry.attributes.scale.needsUpdate = true;
	renderer.render(scene, camera);
	count  = 0.1;
}

function isOSMobile() {
	var userAgent = navigator.userAgent || navigator.vendor || window.opera;

	if (/android/i.test(userAgent)) {
		return true;
	}
	if (/iPad|iPhone|iPod/.test(userAgent) amp;amp; !window.MSStream) {
		return true;
	}

	return false;
}