three.js實現螢幕座標轉化為模型的世界座標
three.js實現螢幕座標轉化為模型的世界座標
基礎:three.js中座標系統.doc
方法.project
通過Vector3物件的方法project,方法的引數是相機物件,語句worldVector.project(camera);返回的結果是世界座標worldVector在camera相機物件矩陣變化下對應的標準裝置座標, 標準裝置座標xyz的範圍是[-1,1]。
因為canvas畫布是全屏狀態,完全填充瀏覽器視窗的客戶區,canvas畫布的寬高尺寸就是window.innerWidth、window.innerHeight。 畫布的中心從螢幕座標系的角度看是座標是(window.innerWidth/2,window.innerHeight/2),從WebGL標準裝置座標系的角度看是座標原點(0,0).
主要參考部落格:
平面座標轉場景座標
這種轉換由於是由2D轉換為3D,所以轉換過於以後,平面的座標在場景座標內的位置應該是從相機的near到far的一條直線。所以我們無法確定單個點的座標,一般都是使用當前2D平面的座標生成一條射線raycaster,來檢測當前射線是否和模型產生相交,而獲取到當前模型上的位置以及相關資訊。
Three.js也給我們封裝了轉換的方法,我們只要會使用,就能夠實現當前的效果,原理是:
首先,獲取到當前平面座標,平面的中心點是(0, 0),左上角的座標是(-1, 1),右下角的座標是(1, -1)。我們可以通過當前滑鼠距離瀏覽器視窗減去顯示區域距離視窗左上角計算出滑鼠距離顯示區域左上角的偏移量:
let left = wrap.getBoundingClientRect().left;
let top = wrap.getBoundingClientRect().top;
let clientX = event.clientX - left;
let clientY = event.clientY - top;
然後根據獲取到的偏移量計算出平面座標:
let mouse = new THREE.Vector2();
mouse.x = (clientX / wrap.offsetWidth) * 2 - 1;
mouse.y = -(clientY / wrap.offsetHeight) * 2 + 1;
使用射線通過二維座標和相機更新射線位置:
let raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
1
2
最後遍歷所有場景內的模型判斷是否和當前射線相交:
let intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length >= 1){
alert("有相交的模型");
}
intersectObjects方法如果傳第二個值為true,將遍歷陣列內的所有模型的子類,也就是深度遍歷。如果當前沒有模型相交,將返回一個空陣列,如果有模型,陣列內的模型將按照相交距離從近到遠排序,每一個物件裡面將有以下幾個屬性:
point:和模型相交的點的座標位置
face:相交的模型的當前的三角形面片
object:相交的模型物件
distance:相交的點距離相機的位置
由於這個之前也有案例,在這裡就發一下之前的案例吧:點選這裡
場景世界座標轉平面二維座標
場景世界座標轉平面二維座標相對來說就簡單了一些,首先,我們需要得到當前點在世界中的座標位置,如果是某個場景組Group裡面的模型的位置座標那種,我們可以通過模型的方法localToWorld方法獲取到世界座標:
let position = mesh.localToWorld();
1
然後通過THREE.Vector3的project方法,逆轉相機來求出二維座標:
let vec2 = position.project(camera);
1
返回的是一個二維向量,我們可以通過獲取的座標計算出當前點距離顯示平面左上角的畫素距離。
let halfWidth = window.innerWidth / 2;
let halfHeight = window.innerHeight / 2;
案例預設是顯示平面大小為顯示視窗的大小,所以直接使用視窗計算:
$(".one").css({
left:vec2.x * halfWidth + halfWidth,
top:-vec2.y * halfHeight + halfHeight
});
我們需要在每幀裡面呼叫,然後持續獲取當前的位置。
案例檢視地址:點選這裡