現在根據鼠標點擊的屏幕位置能夠得到屏幕的坐標event.clientX
和event.clientY
,然后我的annotation就初始化在這個屏幕坐標的位置,那么如何綁定annotation和三維物體,使得物體旋轉的時候可以讓annotation跟隨物體一起旋轉呢?
問題一:我知道的一種方法如下:
/* 修改注解屏幕位置函數體 實時更新,實際是三維坐標向屏幕坐標的映射*/ function updateScreenPosition() { var canvas = renderer.domElement; var vector = new THREE.Vector3(50, 0, 250); // 控制annotation的位置 vector.project(camera); vector.x = Math.round((0.5 + vector.x / 2) * (canvas.width / window.devicePixelRatio)); // 控制annotation跟隨物體一起旋轉 vector.y = Math.round((0.5 - vector.y / 2) * (canvas.height / window.devicePixelRatio)); annotation.style.top = vector.y + "px"; annotation.style.left = vector.x + "px"; annotation.style.opacity = spriteBehindObject ? 0.25 : 1; }
上面的方法可以控制annotation和物體一起旋轉,但是annotation的位置確實預先設定的:
var vector = new THREE.Vector3(50, 0, 250); // 控制annotation的位置
對於上面這行代碼,Vector3
是個什么坐標系下的向量呢?當我在修改Vector3
中的三個坐標的時候,發現它並不是控制annotation
的位置的向量,比如我把鼠標點擊位置的參數傳遞過來,然后觸發鼠標點擊事件,然后發現鼠標點擊的位置並不是annotation
的位置。如何把它和鼠標點擊的屏幕坐標掛鈎呢?
問題二:如果不預先設定好這個Vector3
而是使用鼠標,而是直接傳遞event.clientX
和event.clientY
function updateAnnosPosition(){ var canvas = renderer.domElement; var vector = new THREE.Vector3(clientX,clientY,-1); vector.project(camera); //這個位置的寫法有問題 annos.style.left = clientX + "px"; annos.style.top = clientY + "px"; annos.style.opacity = spriteBehindObject ? 0.25 : 1; }
這樣是可以在鼠標點擊位置觸發annotation的,但是annotation卻不跟隨物體旋轉。
網上查了一下,大概是要進行屏幕坐標與三維坐標之間的轉換:
_mouse.x = ( event.clientX / _domElement.width ) * 2 - 1;
_mouse.y = - ( event.clientY / _domElement.height ) * 2 + 1;
但是還是不行,求教該如何處理。
(1)Vector3是個什么坐標系下的向量呢?
Vector3是 three.js 定義的三維空間坐標系里的向量
(2)如何把它和鼠標點擊的屏幕坐標掛鈎呢?
這個問題本質是問:如何將three.js 三維坐標轉換成屏幕二維坐標
其實updateScreenPosition()
函數就是將三維坐標轉換成屏幕坐標的過程,我們可以改一下這個函數,使之更通用:
/** * 將three.js三維坐標轉換成屏幕上的二維坐標 * @param THREE.Vector3 vector three.js三維坐標 * @return {x:int,y:int} 屏幕坐標 */ function transToScreenCoord(vector) { var screenCoord = {}; vector.project(camera); //1 screenCoord.x = Math.round((0.5 + vector.x / 2) * (canvas.width / window.devicePixelRatio)); //2 screenCoord.y = Math.round((0.5 - vector.y / 2) * (canvas.height / window.devicePixelRatio)); return screenCoord; }
有了這個函數,就可以把任意three.js三維坐標轉換成屏幕坐標。也就是實現了3維坐標和屏幕坐標掛鈎
接下來解釋一下這個轉換的過程:vector.project(camera)
這句的意思是,將一個三維坐標,投影到相機平面上,使之變成一個二維坐標。需要注意的是,投影得到的結果是一個標准向量(或者叫單位向量),其值是限定在[-1,1]范圍內的。並且,這個向量是定義在以屏幕中心為原點的坐標系里的,這個坐標系和屏幕坐標系的關系,就像下圖一樣:
假如經過投影之后的點就是上圖中的點A(0.3,0.5),屏幕坐標系是sx-s0-sy,相機平面坐標系是tx-t0-ty,坐標系的各項參數已經標在圖上,試着求A點在屏幕坐標系中的坐標。你求一遍的話,就會理解上面這個函數的意思。
(3)如果不預先設定好這個Vector3
而是使用鼠標,而是直接傳遞event.clientX
和event.clientY
,該怎么處理?
處理方法就是將這個屏幕二維坐標轉three.js三維坐標,按照下面這個方法來處理:
var vector = new THREE.Vector3(); vector.x = ( event.clientX / _domElement.width ) * 2 - 1; vector.y = - ( event.clientY / _domElement.height ) * 2 + 1;
把上面這段替換掉updateScreenPosition()
函數中的:
var vector = new THREE.Vector3(50, 0, 250); // 控制annotation的位置
應該就可以了