久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

OpenGL選擇

 小乙vk 2015-01-04

無論是游戲還是VR,,三維世界總免不了與用戶的交互。而這其中常也免不了“用戶對(duì)場景中物件的選擇(也就是,,拾?。边@種需求。OpenGL本身就內(nèi)置有一套拾取機(jī)制,,這次就亂彈一下吧,。(亂彈不少了哦~)——ZwqXin.com

OpenGL內(nèi)置的拾取方法,應(yīng)該是第三次要接觸了,。頭次是課程作業(yè),套例子糊里糊涂弄成功了,;第二次是去年9月,,做一個(gè)DEMO,,實(shí)現(xiàn)起來終于遇到了諸多麻煩,,好不容易通過艱辛調(diào)試得到了正確的結(jié)果,,相關(guān)代碼被我供奉捧信至今,,但其實(shí)也不算真正理解了這套機(jī)制。這次則是跟老師做的一個(gè)項(xiàng)目需要,,雖說把之前的代碼弄進(jìn)來這個(gè)選取邏輯就差不多了,但我覺得嘛,還是趁機(jī)作一次更深入的了解吧,。

本文來源于 ZwqXin (http://www./), 轉(zhuǎn)載請注明
      原文地址:http://www./archives/opengl/opengl-picking-what.html

假設(shè)用戶是通過鼠標(biāo)左鍵單擊來選擇場景物件的。先說說一般游戲引擎里的“射線檢測”實(shí)現(xiàn)思路,,也就是在用戶拾取物件,點(diǎn)擊渲染窗口(屏幕)上的相應(yīng)的某一點(diǎn)的時(shí)候,,激發(fā)一條從相機(jī)位置(眼睛)過該點(diǎn)的射線,,這條射線反映在世界空間中,,就是“穿過”視像平面發(fā)射到三維世界空間的光束,。根據(jù)三維投影的知識(shí)(小孔成像原理)可知,,該光速必然可以“射中”用戶所點(diǎn)擊處的物件,,問題轉(zhuǎn)化為線面相交檢測——檢測通過則意味著“射中”該面所屬的三維物件,。

1.名字標(biāo)識(shí)

這“射線檢測”比較好理解,。好吧,,請先認(rèn)為OpenGL也遵循這種機(jī)制(可不要先入為主哦,,后面或許會(huì)否定這個(gè)假設(shè)的),,那么,,從工序的后端往前看,,要解決些什么問題呢?恩,,三維物件知道自己身體某一部分的面被一條線叉中了,,但它怎么通知應(yīng)用程序“我被叉叉了”呢,?簡單的就是給上頭打報(bào)告,,報(bào)告內(nèi)容首項(xiàng)寫上自己的姓名,。好吧,在三維世界里允許長一個(gè)樣的但不允許有同樣的識(shí)別碼,,換言之,你最好給場景中每樣你在意的物件分配一個(gè)唯一的ID,,好在它們打報(bào)告的時(shí)候不會(huì)弄混,。于是,在這層意義上,,ID就是名字,。

OpenGL拾取機(jī)制覺得ID這太俗了,于是用名字(Name)來唯一表示每個(gè)可拾取物件,。但是我們作為應(yīng)用者就該記住,這里的Name就是ID,,即使它真是一個(gè)名字"Allan",那背后肯定也有這么一句"#define Allan 13"之類的,。OpenGL拾取的名字機(jī)制也就是ID機(jī)制。當(dāng)然,,它里面沒有什么排斥不唯一性的,但你讓兩個(gè)物件擁有相同Name的時(shí)候,,你起碼得知道自己在干嘛(是為了實(shí)現(xiàn)某些奇怪的效果吧,略談~),。

2.名字棧機(jī)制

上頭....不,應(yīng)用程序怎么儲(chǔ)存這些資料呢,?啊,先說說應(yīng)用程序怎么與那些被射中而打受傷報(bào)告的物件溝通吧,,它怎么拿到報(bào)告呢,?答案是名字棧。

在OpenGL內(nèi)部,,有一種叫做HIT Record[擊中記錄]的數(shù)據(jù),保存在一個(gè)特別的自設(shè)定緩存區(qū)——Select Buffer[選擇緩存]里,。我們可以通過以下這個(gè)函數(shù)來設(shè)定SelectBuffer,,給予一個(gè)已經(jīng)分配好空間的INT數(shù)組作為參數(shù)(數(shù)組指針和具體大小):

glSelectBuffer (GLsizei size, GLuint *buffer);

至于我們到底應(yīng)該給它多大的空間,,下面講述HIT Record數(shù)據(jù)結(jié)構(gòu)的時(shí)候你就明白了。記住,,HIT Record數(shù)據(jù)的填充是應(yīng)用程序(上頭)的任務(wù),在拾取過程中我們無必要對(duì)它動(dòng)手腳,。我們更應(yīng)該關(guān)心的是之前所說的溝通問題,。首先看看幾個(gè)分配Name(名字)的函數(shù):

  1. //初始化名字棧
  2. void  glInitNames ();
  3.  
  4. //向名字棧壓入名字(ID)
  5. void glPushName(GLuint name); 
  6.  
  7. //從名字棧彈出名字(ID)
  8. void glPopName();
  9.  
  10. //直接把名字(ID)放在棧頭位置上
  11. void glLoadName(GLunit name); 
  12. //相當(dāng)于:
  13. //glPopName();
  14. //glPushName(name);
  15.  
//初始化名字棧void  glInitNames ();//向名字棧壓入名字(ID)void glPushName(GLuint name); //從名字棧彈出名字(ID)void glPopName();//直接把名字(ID)放在棧頭位置上void glLoadName(GLunit name); //相當(dāng)于://glPopName();//glPushName(name);

一般來說,棧這種數(shù)據(jù)結(jié)構(gòu),,是一種前入前出的形式。因此一般也只允許訪問棧首元素?,F(xiàn)在不妨就假設(shè)場景中有三個(gè)物件好了,回頭看看假設(shè)的OpenGL選擇拾取邏輯:用戶沒有點(diǎn)擊屏幕的時(shí)候一切如常,,一旦用戶進(jìn)行了點(diǎn)選則觸發(fā)拾取邏輯——在這里,我們給每個(gè)物件分配一個(gè)臨時(shí)的名字(如前述,,相當(dāng)于ID):

  1. const int OBJECT1 = 1;
  2. const int OBJECT2 = 2;
  3. const int OBJECT3 = 3;
  4.  
  5. //渲染待選擇物件的函數(shù)
  6. void RenderObjects(GLeum renderMode)
  7. {
  8.   if( renderMode == GL_SELECT )  //選擇模式
  9.   {
  10.      glPushName(OBJECT1);
  11.      RenderObject1();
  12.      glPopName();
  13.  
  14.      glPushName(OBJECT2);
  15.      RenderObject2();
  16.      glPopName();
  17.  
  18.      glPushName(OBJECT3);
  19.      RenderObject3();
  20.      glPopName();
  21.  
  22.     RenderOtherRelated();
  23.   }
  24.   else   // ( renderMode == GL_RENDER ) 即正常渲染模式
  25.   {
  26.      RenderObject1();
  27.      RenderObject2();
  28.      RenderObject3();
  29.      RenderOtherRelated();
  30.    }
  31. }
const int OBJECT1 = 1;const int OBJECT2 = 2;const int OBJECT3 = 3;//渲染待選擇物件的函數(shù)void RenderObjects(GLeum renderMode){  if( renderMode == GL_SELECT )  //選擇模式  {     glPushName(OBJECT1);     RenderObject1();     glPopName();     glPushName(OBJECT2);     RenderObject2();     glPopName();     glPushName(OBJECT3);     RenderObject3();     glPopName();    RenderOtherRelated();  }  else   // ( renderMode == GL_RENDER ) 即正常渲染模式  {     RenderObject1();     RenderObject2();     RenderObject3();     RenderOtherRelated();   }}

我們用一個(gè)renderMode枚舉值來分開兩種邏輯,即選擇模式和正常渲染模式,,事實(shí)上這是必須的,后面會(huì)詳述,。在選擇模式下,分配名字,。誒,?先PUSH后POP,,那不是在棧頭壓一個(gè)然后又彈出,,然后再壓再彈,,等于什么都沒有,?嘿,,這種無意義的事情誰也不會(huì)干啦,,關(guān)鍵是中間夾著的渲染過程,,后面等全部代碼亮出后你就能理解了,。

這里你要明白這種用名字棧分配名字的做法——它溝通了該名字所代表物件與應(yīng)用程序內(nèi)部的HIT Record,。當(dāng)某物件被“拾取”(被光束射中)的時(shí)候,,對(duì)應(yīng)的名字和相關(guān)信息便會(huì)被提交給HIT Record,,存儲(chǔ)在SelectBuffer里面,。相關(guān)信息包括該物件離光束發(fā)射處(相機(jī)/眼睛)最近的點(diǎn)的深度值和最遠(yuǎn)的點(diǎn)的深度值等等,,以下反映了當(dāng)一個(gè)物件被拾取后,,名字棧機(jī)制向HIT Record發(fā)送的信息(然后HIT Record把此信息存入SelectBuffer):

擊中的物件的名字的數(shù)目
這個(gè)物件中最近的點(diǎn)的深度值
這個(gè)物件中最遠(yuǎn)的點(diǎn)的深度值
擊中的物件的名字之一
擊中的物件的名字之二
(若有多個(gè)名字,則如此類推...)

 

上述為一般式,,因?yàn)橐粋€(gè)物件可以配兩個(gè)或多個(gè)名字(這是可能用于某些特殊應(yīng)用的,在參考文章Picking - LightHouse里提到,有興趣的可以去看看),。更一般的應(yīng)用,如上述例子,,則會(huì)產(chǎn)生如下信息:

擊中的物件的名字的數(shù)目(即:1)
這個(gè)物件中最近的點(diǎn)的深度值
這個(gè)物件中最遠(yuǎn)的點(diǎn)的深度值
這個(gè)物件的唯一的名字

例子代碼中共會(huì)產(chǎn)生1條,,2條,,或3條這樣的記錄(第一項(xiàng)都是一樣的,,為1,,因?yàn)樗鼈兊拿治ㄒ唬?,因?yàn)橛脩暨@一次點(diǎn)擊啊,,可能就擊中了3個(gè)物件中的其中一個(gè),,也可能一次過擊中兩個(gè)或三個(gè)(如果物件靠得近,,或者視點(diǎn)離物件很遠(yuǎn)導(dǎo)致物件在屏幕上顯得距離近),。如果是一次多條記錄的話,,它們會(huì)按檢測順序(例子中即是渲染順序)依次緊跟著存入SelectBuffer里,。這樣就完了嗎,?還沒呢,!還有可能出現(xiàn)第4條記錄會(huì)發(fā)送到HIT Record上(或者單獨(dú)發(fā)送或者一同):

0
這個(gè)物件中最近的點(diǎn)的深度值
這個(gè)物件中最遠(yuǎn)的點(diǎn)的深度值

沒錯(cuò),,它就是RenderOtherRelated()所代表的物件,。從上面的記錄也可看出,,顯然我們沒有給它分配名字,,但它確實(shí)參與了拾取過程(更準(zhǔn)確地說,,因?yàn)樗苍趓enderMode == GL_SELECT 這個(gè)選擇項(xiàng)里),。為什么允許這樣的情況出現(xiàn)呢,?根據(jù)參考文章Picking - LightHouse所說,,譬如我們遇上了這種情況:場景由多個(gè)房間組成(并且不進(jìn)行場景劃分),每個(gè)房間里有不少魔法石待玩家拾取,。那么如果我們單單就讓這些魔法石都在選擇模式里分配名字,然后拾取,,“射線光束”就很容易穿過墻壁,,“不小心地”把隔壁房間的魔法石給撿了- -。如果墻壁也作為待拾取物,,則射線在碰到墻壁的時(shí)候就停止了,,但是墻壁沒有分配名字,,因此只有一條首項(xiàng)為0的記錄被發(fā)送,,這樣很好,后期判斷處理很簡捷,。但是缺點(diǎn)是……

缺點(diǎn)是它只有三項(xiàng),。同樣,,具有兩個(gè)名字的物件的HIT Record記錄有五項(xiàng),,然后擁有多個(gè)名字的物件則是更多項(xiàng)……問題在于我們在SelectBuffer里讀取的時(shí)候怎么辦呢,?(譬如一次拾取過程中獲得含幾條記錄的HIT Record,,讀取各名字來確定物件時(shí),。)我們最怕不規(guī)則的數(shù)組……

那么,,用glSelectBuffer設(shè)定SelectBuffer的時(shí)候,,你現(xiàn)在應(yīng)該知道了,,其大小應(yīng)該根據(jù)你認(rèn)為一次點(diǎn)選可能擊中的物件數(shù)的最大值,,結(jié)合那些在GL_SELECT下的三項(xiàng),,多項(xiàng)的非正常物件數(shù)來決定,。

本篇最后說一下那兩個(gè)深度值:

1. 它們是物件對(duì)應(yīng)Z-BUFFER中的深度值,,乘以2^32 -1后取整而得到,;

2. 它們的值不是線性連續(xù)的,,因?yàn)閆-BUFFER中的深度值本來就不是線性連續(xù)的(倒是它們的倒數(shù)是線性連續(xù)的,,并符合插值規(guī)則,,具體的自己去找本3D圖形學(xué)數(shù)學(xué)原理的書看去~)

3.它們是在投影變換之后得到的,經(jīng)過的視截體裁減過程(what? ),,因此針對(duì)的是物件在視錐體內(nèi)的部分的離眼最近最遠(yuǎn)點(diǎn),,而不一定就是實(shí)際世界空間中完整物件的最近最遠(yuǎn)點(diǎn)。

好了,,接下來的事項(xiàng)我留在了下篇日志中:亂彈OpenGL 選擇- 拾取機(jī)制Ⅱ

那么,開頭的假設(shè)——OpenGL拾取機(jī)制基于射線檢測——哈,,是錯(cuò)的。的確它們有相同處,,至少目前探討的名字棧機(jī)制與這個(gè)假設(shè)是否成立沒有半點(diǎn)關(guān)系。注意,,在真正的OpenGL拾取機(jī)制中,,從相機(jī)(眼睛)發(fā)出的不是光束射線,,而是“目光”~!

呃……呃,,哼,,哼……嘛,,諸君,!請留意下篇:亂彈OpenGL 選擇- 拾取機(jī)制Ⅱ

本文來源于 ZwqXin (http://www./), 轉(zhuǎn)載請注明.
原文地址:http://www./archives/opengl/opengl-picking-what.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多