【问题标题】:ThreeJS update Camera position so Plane world position matches DIV screen positionThreeJS 更新相机位置,使平面世界位置与 DIV 屏幕位置匹配
【发布时间】:2023-04-02 02:04:01
【问题描述】:

目标

我想更新 Camera Position,以便 Plane World Position 与 DIV Screen Position 匹配。

最初的想法

我需要计算camera.position.z - 以便平面与DIV 的大小相匹配 - 即使在调整画布大小时也是如此。

this.computeZ = function(meshHandle, cameraHandle, faceHeight, targetHeight){
    var face = meshHandle.geometry.vertices[2]
    var vFOV = cameraHandle.fov * Math.PI / 180;  
    var vHeightPartial = 2 * Math.tan( vFOV / 2 );
    var p1 = faceHeight * window.innerHeight;
    var p2 = face.z * vHeightPartial;
    var p3 = targetHeight * vHeightPartial;
    var p4 = targetHeight * p2;
    var p5 = p1 + p4;
    var z = p5/p3;
    return z;
}

See computeZ in action here.

后续步骤

世界人脸大小现在与DIV 屏幕像素大小匹配。

接下来我们需要找到camera.position.xcamera.position.y——这样人脸就直接和DIV重叠了。

我学过...
How to Fit Camera to Object
Three.js - Width of view
THREE.JS: Get object size with respect to camera and object position on screen
Converting World coordinates to Screen coordinates in Three.js using Projection

...但一直在努力构建适用于 computeXcomputeY 的东西

请帮忙

看看我提供的the fiddle 中的computeXcomputeY 函数。 这些功能是我最好的尝试 - 但不起作用。

如何构建这些函数?

更新

Craig's post 的帮助下,我想出了一个solution。此类建立在他的方法之上,以涵盖调整大小事件。

【问题讨论】:

标签: javascript html three.js


【解决方案1】:

<!DOCTYPE html>

<html>

<head>
    <title>SO code</title>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script>
    <style>
          html,body{
		  height:100%;
		  width:100%;
		  padding:0px;
		  margin:0px;
		}
		#content{
		  width:100%;
		  height:100%;
		  position:relative;
		}
		#box{
		  position:absolute;
		  background:orange;
		  height:100px;
		  width:100px;
		  bottom:100px;
		  right:100px;
		}
    </style>
    </head>
	<body>
	<div id="content">
	  <div id="box"></div>
	</div>
<script>
	function Terrain(){

	  this.container = document.getElementById('content');
	  this.camera;
	  this.scene; 
	  this.renderer;
	  this.light;

	  this.computeZ = function(meshHandle, cameraHandle, faceHeight, targetHeight){
	  	var face = meshHandle.geometry.vertices[2]
			var vFOV = cameraHandle.fov * Math.PI / 180;  
			var vHeightPartial = 2 * Math.tan( vFOV / 2 );
			var p1 = faceHeight * window.innerHeight;
			var p2 = face.z * vHeightPartial;
			var p3 = targetHeight * vHeightPartial;
			var p4 = targetHeight * p2;
			var p5 = p1 + p4;
			var z = p5/p3;
      
			//calculate dom element center coordinate
			var screenPositionX = 0;
			var screenPositionY = 0;
			var div = document.getElementById('box');
			var divDim = div.getBoundingClientRect();
			screenPositionX = (divDim.left + divDim.right) / 2;
			screenPositionY = (divDim.bottom + divDim.top) / 2;
			var vector = new THREE.Vector3((screenPositionX / window.innerWidth) * 2 -1, (screenPositionY / window.innerHeight) * 2 -1, 0.5);
    //unproject camera
			vector = vector.unproject(this.camera);
			var distanceZ = this.camera.position.z - vector.z ;
			var offsetX = vector.x * (z-10) / distanceZ;
			var offsetY = vector.y * (z-10) / distanceZ;
			var cameraPosition = new THREE.Vector3(offsetX,offsetY,z);
			return cameraPosition;
		}
	  
		this.computeX = function(meshHandle, cameraHandle, faceHeight, targetWidth){
	  	var div = document.getElementById('box');
			var divDim = div.getBoundingClientRect();
			var y =  ((divDim.left + (targetWidth/2)) / window.innerHeight ) * 2 + 1;
			return y;
	  }
	  
	  this.computeY = function(meshHandle, cameraHandle, faceHeight, targetHeight){
			var div = document.getElementById('box');
			var divDim = div.getBoundingClientRect();
			var y =  ((divDim.top + (targetHeight/2)) / window.innerHeight ) * 2 + 1;
			return y;
	  }
	  this.onDocumentClick = function(event)
	  {
	  			var vector = new THREE.Vector3(( event.clientX / (window.innerWidth) ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
                vector = vector.unproject(this.camera);

                console.log(vector);
	  }
	  this.init();
	}

	Terrain.prototype.init = function () {

		this.camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
		this.scene = new THREE.Scene();

		this.geometry = new THREE.BoxGeometry(20, 20, 20);
		this.material = new THREE.MeshPhongMaterial();
		this.mesh = new THREE.Mesh( this.geometry, this.material );
		this.scene.add( this.mesh );
		
		var ambient = new THREE.AmbientLight( 0x00ff00, 0.5 );
		this.scene.add( ambient );

		this.renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
		this.renderer.setPixelRatio( window.devicePixelRatio );
		this.renderer.setSize( window.innerWidth, window.innerHeight );
		this.container.appendChild(this.renderer.domElement );
	  
		window.animations['terrain'] = this.animate.bind(this);
		window.addEventListener( 'resize', this.onWindowResize.bind(this), false );
		document.addEventListener('click',this.onDocumentClick.bind(this), false);
	}

	Terrain.prototype.onWindowResize = function(){
		this.renderer.setSize( window.innerWidth, window.innerHeight );
	  this.camera.aspect = window.innerWidth / window.innerHeight;
	  this.camera.updateProjectionMatrix();
	}

	Terrain.prototype.animate = function(){
	  	//this.camera.position.x = this.computeX(this.mesh, this.camera, 20, 100);
	  //this.camera.position.y = this.computeY(this.mesh, this.camera, 20, 100);
	  this.renderer.render( this.scene, this.camera );
	}

	function animate(){
	  for(var i in window.animations){
	    window.animations[i]();
	  };
	  window.requestAnimationFrame(animate);
	}

	window.animations = {};
	var terrain = new Terrain();
	window.requestAnimationFrame(animate);
	var newPosition = terrain.computeZ(terrain.mesh,terrain.camera,20,100);
	terrain.camera.position.x -= newPosition.x;
	terrain.camera.position.y += newPosition.y;
	terrain.camera.position.z += newPosition.z;
</script>
</body>
</html>

为了解决这个问题,我分了这两个步骤,1:获取3D空间中的dom元素坐标。 2:使用相似三角形计算x和y的偏移量。我将向您展示如何使用相似三角形。

因为相机是透视相机。现在 dom 元素、相机和网格可以组成一个三角形,如果我们忽略 Y 维度,三角形会像这张图片。

现在,我们知道了 3D 空间中的 dom 元素坐标,并且你得到了正确的“z”,我们也知道了绿色立方体的深度。我们需要计算偏移量x(图中的紫色线)。很明显,图中的这些三角形是相似的三角形。 vector.x / offsetX = vector.z / z - mesh.geometry.parameters.depth。我们可以做同样的事情来获取offsetY

【讨论】:

  • 感谢您花费这么多时间和精力来进行解释。我能够基于您的解决方案来调整事件大小。请随时咨询jsfiddle.net/gigablox/gryeejyj 以供参考:)当赏金可用时,我会奖励你的努力。
  • @DanKanze 谢谢你,你的问题很有趣。
  • 另一种解决方案,使用固定相机位置在场景中移动对象而不是相机。 jsfiddle.net/gigablox/e28eotue/4
猜你喜欢
  • 2013-07-31
  • 2018-07-04
  • 1970-01-01
  • 1970-01-01
  • 2018-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-20
相关资源
最近更新 更多