Three.js 選擇對象的本質是從點擊位置發射光線
,但屏幕座標系與webgl座標系是不同的,而把屏幕的二維座標轉化爲三維座標
就是關鍵,做一步換算後交由Raycaster的setFromCamera方法即可。
所以思路如下:
1.獲取屏幕座標(x, y)
2.換算至webgl座標中的(x2,y2),此時長度仍爲像素單位下的長度
3.由於webgl座標軸的範圍爲(-1,1),做個比值,稱爲標準化
4.Raycaster.setFromCamera(mouse, camera)發射射線
5.Raycaster.intersectObjects獲取射得的所有對象,再按需要操作
先對比下各座標軸,再按代碼分析步驟
代碼中使用了我封裝的方法,threeConf對象包含three.js的基本組件,但不影響拾取對象的邏輯,拾取的代碼於此
一、座標軸
1.屏幕座標軸
即我們的屏幕的二維座標,矩形爲我們的屏幕,左上角就是(0, 0)
2.世界座標系
即webgl中的座標系,屏幕中心爲(0, 0, 0),第三個座標爲從屏幕指向我們的z軸。三個座標的大小範圍
爲(-1,1),如圖
這裏其實還有個相機座標系,因爲還沒有學習webgl或者opengl怕胡說八道,我覺得一篇很棒的blog貼於文末
但是我們實現選擇拾取功能時,但單純從three.js角度來看可以不需要相關知識
二、代碼思路
代碼較長,這裏截取部分代碼按思路來,全部代碼請看GitHub
1.座標標準化
//1.獲取屏幕座標(x, y)
//2.換算至webgl座標中的(x2,y2),此時長度單位仍爲像素
//3.由於webgl座標軸的範圍爲(-1,1),做個比值,稱爲標準化
//這三步後化簡的式子如下,這裏我們three.js的窗口爲整個瀏覽器
let rayRaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
function onMouseMove(event) {
mouse.x = (event.clientX/window.innerWidth) * 2 - 1;
mouse.y = 1 -(event.clientY/window.innerHeight) * 2 ;
}
window.addEventListener('mousemove', onMouseMove, false);
x1 = event.clientX,y1 = event.clientY,即點擊位置
,換算過程如下(這裏默認畫布寬高爲瀏覽器寬高)
根據式子帶入x1與y1即可得到代碼中相同的式子
2.發射射線,獲取對象
這裏截取射線代碼
function render() {
//射線射出
rayRaster.setFromCamera(mouse, threeConf.camera);
//射線上的物體
let intersects = rayRaster.intersectObjects(threeConf.scene.children);
//選中另一物體的情況,仍爲同一物體則不更新,這裏我們只拿第一個物體
if(selectedObj !== intersects[0]){
selectedObj && selectedObj.object.material.color.set(0x87CEEB); //復原
intersects.length && intersects[0].object.material.color.set(0xff0000);
selectedObj = intersects[0];
document.body.style.cursor = "pointer";
}
//未選中物體情況
if(intersects[0] === undefined){
document.body.style.cursor = "auto";
}
threeConf.stats.update();
threeConf.renderer.render(threeConf.scene, threeConf.camera);
requestAnimationFrame(render);
}
即可
三、參考資料
ThreeJS中的點擊與交互——Raycaster的用法
https://segmentfault.com/a/11...
OpenGL.座標系統的介紹與座標變換的實現(此篇blog包含相機座標軸)
https://blog.csdn.net/stringN...
Three.js Raycaster官方文檔
https://threejs.org/docs/#api...