
VPM矩陣
1、V 表示攝像機(jī)的觀察矩陣(View Matrix),它的作用是把對象從世界坐標(biāo)系變換到攝像機(jī)坐標(biāo)系。因此,對于世界坐標(biāo)系下的坐標(biāo)值 worldCoord(x0, y0, z0),如果希望使用觀察矩陣 VM 將其變換為攝像機(jī)相對坐標(biāo)系下的坐標(biāo)值 localCoord(x’, y’, z’),則有:
localCoord = worldCoord * VM
此外,觀察矩陣可以理解為“攝像機(jī)在世界坐標(biāo)系下的變換矩陣的逆矩陣”,因此 Camera類也專門提供了 getInverseViewMatrix 這樣一個函數(shù),它的實(shí)際意義是表示攝像機(jī)在世界坐標(biāo)系下的位置。
2、P 表示投影矩陣(Projection Matrix),當(dāng)我們使用 setProjectionMatrixAsPerspective之類的函數(shù)設(shè)置攝像機(jī)的投影矩陣時,我們相當(dāng)于創(chuàng)建了一個視截錐體,并嘗試把包含在其中的場景對象投影到鏡頭平面上來。如果投影矩陣為 PM,而得到的投影坐標(biāo)為 projCoord(x”,y”, 0)的話,那么:
projCoord = localCoord * PM
3、W 表示視口矩陣(Window Matrix),它負(fù)責(zé)把投影坐標(biāo)變換到指定的二維視口中去,對于視口矩陣 WM,通過下面的公式可以得到最終的窗口坐標(biāo) windowCoord(x, y, 0):
windowCoord = projCoord * WM
將所有的公式整合之后,得到:
windowCoord = worldCoord * VM * PM * WM
而這個所謂的窗口坐標(biāo) windowCoord,實(shí)際上也就是世界坐標(biāo)系下的坐標(biāo)值 worldCoord在指定的攝像機(jī)視口中(也就是我們的屏幕上)對應(yīng)的平面位置。怎么樣,不知不覺中,我們已經(jīng)實(shí)現(xiàn)了 gluProject 函數(shù)所完成的功能了,而反轉(zhuǎn)這三個步驟就可以得到視口中指定位置所對應(yīng)的世界坐標(biāo)了(也就是 gluUnProject 的工作)。
CheckEvent與takeEvents
上一節(jié)我們遺漏了GraphicsWindowWin32::checkEvents和osgGA::EventQueue::takeEvents的關(guān)系。我們現(xiàn)在來講解一下。先看一下checkEvents函數(shù),這個函數(shù)的內(nèi)容對于熟悉 Win32 SDK 編程的朋友一定非常熟悉,其中的TranslateMessage,DispatchMessage都是windows的消息傳遞函數(shù),而它們的工作就是:通知 Windows 執(zhí)行窗口的消息回調(diào)函數(shù),進(jìn)而執(zhí)行用戶交互和系統(tǒng)消息的檢查函數(shù)GraphicsWindowWin32::handleNativeWindowingEvent。而這個函數(shù)的作用是把Win32 SDK 編程中常見的窗口消息(WM_*)轉(zhuǎn)化并傳遞給osgGA::EventQueue 消息隊(duì)列。而osgGA::EventQueue 消息隊(duì)列通過takeEvents得到所有的windows窗口消息,并進(jìn)行處理,以及清空EventQueue。
switch(event->getEventType())
????????????????{
????????????????????case(osgGA::GUIEventAdapter::PUSH):
????????????????????case(osgGA::GUIEventAdapter::RELEASE):
????????????????????case(osgGA::GUIEventAdapter::DOUBLECLICK):
????????????????????case(osgGA::GUIEventAdapter::MOVE):
????????????????????case(osgGA::GUIEventAdapter::DRAG):
????????????????????{
????????????????????????if(event->getEventType()!=osgGA::GUIEventAdapter::DRAG ||
????????????????????????????eventState->getGraphicsContext()!=event->getGraphicsContext() ||
????????????????????????????eventState->getNumPointerData()<2)
????????????????????????{
????????????????????????????generatePointerData(*event);
????????????????????????}
????????????????????????else
????????????????????????{
????????????????????????????reprojectPointerData(*eventState, *event);
????????????????????????}
????????????????????????eventState->copyPointerDataFrom(*event);
????????????????????????break;
????????????????????}
????????????????????default:
????????????????????????event->copyPointerDataFrom(*eventState);
????????????????????????break;
????????????????}
回到osgViewer:: Viewer::eventTraversal()中,我們繼續(xù)向下else也就是事件中的鼠標(biāo)位置多于兩個就會調(diào)用reprojectPointerData函數(shù),它也是用來把鼠標(biāo)從window屏幕坐標(biāo)轉(zhuǎn)換到主相機(jī)視口內(nèi)坐標(biāo),和上一節(jié)內(nèi)容基本相同。大家可以參照上一節(jié)內(nèi)容進(jìn)行理解。
for(itr = gw_events.begin();
????????????????itr != gw_events.end();
????????????????++itr)
????????????{
????????????????osgGA::GUIEventAdapter* event = (*itr)->asGUIEventAdapter();
????????????????if(!event) continue;
????????????????switch(event->getEventType())
????????????????{
????????????????????case(osgGA::GUIEventAdapter::CLOSE_WINDOW):
????????????????????{
????????????????????????boolwasThreading = areThreadsRunning();
????????????????????????if(wasThreading) stopThreading();
????????????????????????gw->close();
????????????????????????_currentContext = NULL;
????????????????????????if(wasThreading) startThreading();
????????????????????????break;
????????????????????}
????????????????????default:
????????????????????????break;
????????????????}
????????????}
模模糊糊朦朦朧朧,我們也算是跳出了處理所有事件中鼠標(biāo)坐標(biāo)的for循環(huán)。我們只能繼續(xù)向下前行。我們又遇到了一個for循環(huán),這個for循環(huán)簡單來說就是處理當(dāng)窗口關(guān)閉消息osgGA::GUIEventAdapter::CLOSE_WINDOW發(fā)生時,osg會做什么樣的工作,使其更加體面的離開。當(dāng)我們選擇關(guān)閉一個 GraphicsWindow 窗口 gw 時,OSG 系統(tǒng)必須首先嘗試終止所有的渲染線程,然后關(guān)閉窗口,之后再打開所有的渲染線程。事實(shí)上,當(dāng)我們試圖在運(yùn)行時開啟一個新的 OSG 圖形窗口時,也必須使用相同的線程控制步驟,即,關(guān)閉線程,創(chuàng)建新渲染窗口,開啟線程。否則很可能造成系統(tǒng)的崩潰。
再往下我們也要針對目前幀的狀態(tài)新建一個幀事件(也就是每一幀都會調(diào)用的事件),并添加到事件隊(duì)列_evnetQuene中,然后同樣得把這個幀事件中的鼠標(biāo)坐標(biāo)轉(zhuǎn)化到主相機(jī)的視口坐標(biāo)。再遍歷一遍windows消息事件,添加到events中,并清空eventQuene隊(duì)列。這樣我們的events中就把所有來自圖形窗口和視景器的事件都添加到一個 std::list 鏈表(event)當(dāng)中, 下一步我們可以統(tǒng)一處理這些交互事件了.
歡迎大家來我的新家看一看?3wwang個人博客-記錄走過的技術(shù)之路