3D對象鼠標像素拾取解決方案

        標題的功能是公司以前項目中的新加的功能,之前的鼠標物體拾取是通過cpu計算射線和物體AABB相交來判斷,但這種方式存在很大弊端,如果物體成長條形,那麼在某些角度下AABB就比物體大的多,再者像一些鏤空很多的物體,如蜘蛛、柵欄等,會遮擋後面物體的拾取,要能夠精確的判斷的話,就需要做射線和模型三角網格的相交檢測,由於可選擇的物體可能很多,模型也可能會很複雜,所以在沒有簡化網格的情況下這個計算量可能非常大,耗時長。討論解決辦法時同事建議在像素級別來做這個操作,於是眼前一亮,想了下覺得可行,然後下來經過幾番思考,最後結合項目得到如下解決方案,可以作爲3D遊戲拾取方案的參考。

       在涉及具體方案前先說下幾個項目涉及到的問題

       1、物體拾取時有類型優先級,比如:npc要優先選擇,如果npc被周圍玩家完全遮擋,那麼npc要優先能被選中。

       2、拾取邏輯和後續事件處理邏輯的協調。

       3、效率及優化


先說下像素拾取方法,原理很簡單,通過額爲的渲染將物體的指針或者id號作爲顏色傳給pixel shader輸出到texture,cpu回讀這個texture,然後通過獲取像素點顏色值,確定被選中的物體。

        完整的解決方案分如下幾步:

1、視錐體裁剪不可見物體(一般渲染步驟會包含這個操作)

2、按物體類型,進行射線和AABB相交,進一步裁剪,並記錄哪些物體和類型需要做拾取判斷

3、按類型進行拾取渲染,每種類型用一個RTT,跳過不需要拾取的類型的渲染

4、回讀需要判斷類型的texture到內存,按類型優先級判斷是否有物體被拾取到,如果有選中則可以跳過後續類型的判斷,沒有則繼續判斷下一類型

5、如果最終有選中物體,則進行鼠標事件處理

上述步驟中,第五步涉及到前面提到的問題2和3,原因是:按照一般的幀循環邏輯,是先framemove後render,而像素拾取必須要render後才能得到結果。當然等像素拾取後再處理鼠標事件也可行,但是這樣會讓cpu和gpu相互等待,破環工作的異步性,更優化的辦法是把獲取結果和鼠標事件處理放到下一幀的開始去做。根據具體情況,可能還需要和UI鼠標事件協調,如果有UI鼠標事件,則可以忽略結果獲取和處理。

        在整個解決方案中還有個非常需要優化的地方,就是texture的回讀,因爲cpu回讀gpu數據是一個非常耗時的操作,所以進行了兩個處理:

1、爲了提高cpu和gpu並行性,每種有拾取判斷類型的texture都會完整回讀到內存,而不是直接到gpu中去取像素點的值,同時爲了減少回讀數據,每個texture大小爲3x3的9個像素(理論上1個像素點就夠了,但覺得有點不踏實)。

2、爲了只記錄以鼠標點爲中心的9個像素值,追加了一次2D的二次投影。


總的來說像素拾取可以很精確的的進行拾取操作,對幀率的影響也很小,經測試項目以70幀的話會有1、2幀的影響,當然像素拾取也並非完美,比如:對渲染出來很小或很細的物體很難拾取,對蛇這種帶動畫的物體也有同樣問題,可以考慮渲染拾取像素時加個矩形等等,這個問題還沒有想到完美的解決辦法。就目前來說還有個問題就是由於沒有進行射線三角形相交測試,所以不能確定鼠標點中點的世界座標,要解決這個問題只要在拾取渲染同時將像素3d座標也輸出就可以實現,項目沒有這個需求,解決方案就沒加這點。

       從這個解決方案也可以看出,其實用像素來進行物體拾取判斷的技術本身非常簡單,但是要作爲一個功能模塊集成到項目中或引擎中則還需要做很多工作,這也是ce、ue等通用引擎難得的地方:把很多簡單的技術或方法集成到一起相互協調並高效的工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章