這里只是記錄一下坑,方便查閱,內(nèi)容主要援引自:three.js Raycaster 射線拾取 canvas不占滿整屏?xí)r射線拾取存在偏差
1. 世界坐標(biāo)系: 世界坐標(biāo)系位于屏幕的中心(0,0,0),往右側(cè)是x軸,往上是y軸,垂直屏幕朝向的是z軸.所以屏幕的左下角是(-1,-1),右上角是(1,1);
2. 屏幕坐標(biāo)系: webgl會將三維的坐標(biāo)經(jīng)過計算,在屏幕里正常顯示.
在根據(jù)模型或者網(wǎng)格去進(jìn)行碰撞測試時,我們選擇的點一般就是攝像機(jī)的位置(相當(dāng)于人眼的位置,在屏幕上點擊的位置,組成一條射線)
初始化,比如:
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera(mouse,camera) // 也可以給構(gòu)造函數(shù)傳參的形式寫
var intersects = raycaster.intersectObjects( scene.children ); // 這里的children,如果你是引入的模型,那么模型加載parse的時候,可以使用一個變量存儲你需要檢測的對象,這里必須傳入
但是當(dāng)你的瀏覽器屏幕的縮放比不一致的時候,你就會看到一個很奇葩的現(xiàn)象,你的模型偏了,你的鼠標(biāo)永遠(yuǎn)都選不到目標(biāo),然后審查一下元素,可能會出現(xiàn)winner.innerHeight,window.innerWidth 遠(yuǎn)大于或者小于屏幕視口實際尺寸的情況(我當(dāng)時還以為是無限鼠標(biāo)的鍋,后來才想到可能是屏幕的縮放導(dǎo)致的).這時需要重新去定義射線的初始化.我在開始部分原因的那段代碼就可以很好的解決:
functioninitRay(){
// 屏幕坐標(biāo)轉(zhuǎn)標(biāo)準(zhǔn)設(shè)備坐標(biāo)
let x = ((event.clientX - mainCanvas.getBoundingClientRect().left) / mainCanvas.offsetWidth) *2-1;// 標(biāo)準(zhǔn)設(shè)備橫坐標(biāo)
// 這里的mainCanvas是個dom元素,getBoundingClientRectangle會返回當(dāng)前元素的視口大小.
let y = -((event.clientY - mainCanvas.getBoundingClientRect().top) / mainCanvas.offsetHeight) *2+1;// 標(biāo)準(zhǔn)設(shè)備縱坐標(biāo)
let standardVector =newTHREE.Vector3(x, y,1);// 標(biāo)準(zhǔn)設(shè)備坐標(biāo)
// 標(biāo)準(zhǔn)設(shè)備坐標(biāo)轉(zhuǎn)世界坐標(biāo)
let worldVector = standardVector.unproject(camera);
// 射線投射方向單位向量(worldVector坐標(biāo)減相機(jī)位置坐標(biāo))
let ray = worldVector.sub(camera.position).normalize();
// 創(chuàng)建射線投射器對象
letrayCaster =newTHREE.Raycaster(camera.position, ray);
// 返回射線選中的對象 第二個參數(shù)如果不填 默認(rèn)是false
letintersects = rayCaster.intersectObjects(scene.children,true);
if(intersects.length >0) {
// console.log(intersects[0].object);
? }
}