Three.js中的正交相机和光线投射

我正在做一个testing项目,将简单的游戏从iOS移植到Javascript。 我正在使用正交相机,它可以查看场景中的物体,并在场景中的网格塔上下移动。 现在我正在尝试使用raycasting来select网格物体,并且无法获取任何要注册的对象。 我做了大量的研究,花费了一些时间在debugging器上,尝试了许多不同的方式来configuration,但都没有成功。 我已经无数次地检查了我的代码,我想我正在按照我可以find的Three.js示例正确地做所有事情,但也许我错过了一些明显的东西。

首先我设置全局variables:

var container; var camera; var scene; var renderer; var projector; var mouseX = 0; var mouseY = 0; //implemented a fixed game size here var windowWidth = 800; var windowHeight = 600; var windowHalfX = windowWidth / 2; var windowHalfY = windowHeight / 2; var intersects = []; var entities = []; //this is the game model object var worldSize = new WorldSize(windowHalfX, windowHalfY); var tower = new Tower(worldSize); 

然后我初始化相机,渲染器和投影仪:

 var zoom = 1.0; camera = new THREE.OrthographicCamera(windowWidth * zoom / -2, windowWidth * zoom / 2, windowHeight * zoom / 2, windowHeight * zoom / -2, 0, 1000); //z value of position can be zero or positive but negative does not work camera.position = new THREE.Vector3(0,0,0); camera.lookAt(new THREE.Vector3(0,0,0)); camera.updateProjectionMatrix(); projector = new THREE.Projector(); renderer = new THREE.WebGLRenderer({clearColor: 0xff0000, clearAlpha: 1}); renderer.setClearColor(0xC0C0C0, 1); //this is light grey renderer.setSize(windowWidth, windowHeight); container.appendChild(renderer.domElement); 

然后我遍历我的游戏模型并将对象添加到场景中:

 var floorShape = THREE.SceneUtils.createMultiMaterialObject( new THREE.BoxGeometry(500, tower.towerFloors[obj].floorSize * 10, 1), multiMaterial); //negative values work for the Z for this, 0 looks strange, positive not in view floorShape.position.set(0, tower.towerFloors[obj].yposition * 10, -20); scene.add(floorShape); entities.push(floorShape); 

最后在一个鼠标点击我打电话这个function:

 function onDocumentMouseDown(event) { event.preventDefault(); var vectorx = (event.clientX / windowWidth) * 2 - 1; var vectory = -(event.clintY / windowHeight) * 2 + 1; var vectorz = camera.position.z; var vector = new THREE.Vector3(vectorx, vectory, vectorz); //quick hack to make sure the x is not the problem vector.x = camera.position.x; projector.unprojectVector(vector, camera); var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position), 0, -100); // use picking ray since it's an orthographic camera raycaster = projector.pickingRay(vector, camera); intersects = raycaster.intersectObjects(entities); alert(vector.x + ' , ' + vector.y + ',' + vector.z); if ( intersects.length > 0 ) { console.log( intersects[ 0 ] ); alert('did it'); } } 

当我单击屏幕时,警报中显示的值是(0, theCorrectValueForY, 0) 。 当我检查scene.children或实体数组中的对象时,它们位于正确的xyz位置。

debugging时,我注意到当我第一次调用var raycaster = ... ,near被设置为0 ,far被设置为-100就像我说的那样。 但是在调用raycaster = projector.pickingRay ,远值被设置为空值。 我已经尝试了raycaster的近距离和远距离的所有不同的值,而且他们都没有工作。 光线的来源在被点击的位置是正确的,在raycaster = projector.pickingRay之后它的移动方向总是(0, 0, -1)

我可以想到一些可能的问题,例如可能我不移动物体的实际网格来匹配它们的位置? 或者,也许我正在向错误的方向发射光线? 但我无法弄清楚。 我不知道我是否做错了什么,或者如果相机位置,物体位置和光线投射值的正确值的某些组合会按预期工作。 我非常感谢任何帮助!

编辑:这是完整的代码,以防万一任何人想要尝试它。 的index.html

 <!DOCTYPE html> <html> <head lang="en"> <style> body { background-color: #ffffff; margin: 50px; overflow: hidden; font-family:Monospace; font-size:13px; text-align:center; font-weight: bold; text-align:center; } div{ background: #ffffff; } </style> <meta charset="UTF-8"> <title></title> </head> <body> <script src="js/jquery.min.js"></script> <script src="js/three.min.js"></script> <script src = "js/three2dtest.js"></script> </body> </html> 

three2dtest.js

 var container; var camera; var scene; var renderer; var projector; var mouseX = 0; var mouseY = 0; //implemented a fixed game size here var windowWidth = 800; var windowHeight = 600; var windowHalfX = windowWidth / 2; var windowHalfY = windowHeight / 2; var particleMaterial; var intersects = []; var entities = []; //this is the game model object var worldSize = new WorldSize(windowHalfX, windowHalfY); var tower = new Tower(worldSize); init(); function init() { container = document.createElement( 'div' ); document.body.appendChild( container ); //test text on screen var info = document.createElement('div'); info.style.position = 'absolute'; info.style.top = '2px'; info.style.width = '100%'; info.style.textAlign = 'center'; info.innerHTML = '<p>clickable tower floors</p>'; container.appendChild(info); var zoom = 1.0; camera = new THREE.OrthographicCamera(windowWidth * zoom / -2, windowWidth * zoom / 2, windowHeight * zoom / 2, windowHeight * zoom / -2, 0, 1000); //z value of position can be zero or positive but negative does not work camera.position = new THREE.Vector3(0,0,0); camera.lookAt(new THREE.Vector3(0,0,0)); camera.updateProjectionMatrix(); scene = new THREE.Scene(); //scene.fog = new THREE.FogExp2( 0x000000, 0.0025 ); console.log(scene); projector = new THREE.Projector(); //particle material var PI2 = Math.PI * 2; particleMaterial = new THREE.SpriteCanvasMaterial( { color:0x000000, program: function(context) { context.beginPath(); context.arc(0,0,0.5,0,PI2,true); context.fill(); } }); for (obj in tower.towerFloors) { console.log(tower.towerFloors[obj].yposition); // Using wireframe materials to illustrate shape details. var darkMaterial = new THREE.MeshBasicMaterial({color: 0xffffcc}); var lightMaterial = new THREE.MeshBasicMaterial({color: 0xffffff}); var wireframeMaterial = new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true, transparent: true}); var localFloor = tower.towerFloors[obj]; if (localFloor.hasBeenClicked === true) { var multiMaterial = [lightMaterial, wireframeMaterial]; } else { var multiMaterial = [darkMaterial, wireframeMaterial]; } // create a cube for each of the floors on the tower var floorShape = THREE.SceneUtils.createMultiMaterialObject( new THREE.BoxGeometry(500, tower.towerFloors[obj].floorSize * 10, 1), multiMaterial); //negative values work for the Z for this, 0 looks strange, positive not in view floorShape.position.set(0, tower.towerFloors[obj].yposition * 10, -20); scene.add(floorShape); entities.push(floorShape); } renderer = new THREE.WebGLRenderer({clearColor: 0xff0000, clearAlpha: 1}); //renderer = new THREE.CanvasRenderer(); renderer.setClearColor(0xC0C0C0, 1); //this is light grey renderer.setSize(windowWidth, windowHeight); container.appendChild(renderer.domElement); document.addEventListener( 'mousemove', onDocumentMouseMove, false ); window.addEventListener( 'resize', onWindowResize, false ); document.addEventListener('mousedown', onDocumentMouseDown, false); //should be better to start animate inside here rather than outside animate(); } //new function from documentation but not the raycasting is not working function onDocumentMouseDown(event) { event.preventDefault(); var vectorx = (event.clientX / windowWidth) * 2 - 1; var vectory = (event.clintY / windowHeight) * 2 + 1; var vectorz = camera.position.z; var vector = new THREE.Vector3(vectorx, vectory, vectorz); //quick hack to make sure the x is not the problem vector.x = camera.position.x; projector.unprojectVector(vector, camera); var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize(), 0, -10000); // use picking ray since it's an orthographic camera raycaster = projector.pickingRay(vector, camera); intersects = raycaster.intersectObjects(entities); alert(vector.x + ' , ' + vector.y + ',' + vector.z); if ( intersects.length > 0 ) { console.log( intersects[ 0 ] ); alert('did it'); } } function onDocumentMouseMove( event ) { mouseX = event.clientX - windowHalfX; mouseY = event.clientY - windowHalfY; } function onWindowResize() { windowHalfX = windowWidth/ 2; windowHalfY = windowHeight / 2; camera.aspect = windowWidth / windowHeight; camera.updateProjectionMatrix(); renderer.setSize(windowWidth, windowHeight); } function animate() { requestAnimationFrame( animate ); render(); } function render() { //move only up and down //camera.position.x += ( mouseX - camera.position.x ) * 0.05; camera.position.y += ( - mouseY - camera.position.y / 10 ) * 0.15; //the camera wont move above the top floor so no bounds check needed if (camera.position.y < 0) { camera.position.y = 0; } camera.updateProjectionMatrix(); renderer.render(scene, camera); } /////////////////////////////////////////////// /* Tower Objects */ /////////////////////////////////////////////// function Tower (worldSize) { this.towerFloors = {}; var floorNumber = 0; for (floorNumber = 0; floorNumber < 50; floorNumber++) { var floor = new Floor(floorNumber, worldSize); this.towerFloors[floorNumber] = floor; } for (obj in this.towerFloors) { //var thing = this.towerFloors[obj]; console.log(this.towerFloors[obj].floorNumber); } } function Floor (floorNumber, worldSize) { this.floorNumber = floorNumber; this.floorSize = worldSize.ysize / 50; this.yposition = this.floorSize * floorNumber; this.hasBeenClicked = false; } function WorldSize (xsize, ysize) { this.xsize = xsize; this.ysize = ysize; } 

不知道这是怎么回事。 vector的全部点是将鼠标位置转换为相机前面的3d点。 当参考框架当前聚焦在相机上时(摄像机为0,0,0),将Z设置为camera.position.z,然后将该值无法投影到世界空间中是没有意义的。

 var vectorx = (event.clientX / windowWidth) * 2 - 1; var vectory = (event.clintY / windowHeight) * 2 + 1; var vectorz = camera.position.z; var vector = new THREE.Vector3(vectorx, vectory, vectorz); 

试试下面。 它只是根据点击坐标在相机前创建一个点,并将其投影到位于世界空间的适当位置,然后从这两点创建一个光线。

 var vector = new THREE.Vector3(event.clientX, event.clintY, 1); projector.unprojectVector(vector, camera); raycaster.set(camera.position, vector.sub(camera.position).normalize()); //No point in creating a new one each time