Unity開放大世界座標精度導致抖動的解決方案

2022-06-22 20:51:08 字數 2116 閱讀 5448

做開放大世界的小夥伴肯定都被過大的世界座標導致的抖動問題頭疼。過大的世界座標導致浮點精度無法準確的表示位置,在表現上看就是物體的抖動和物體之間的穿插問題。要解決這個問題,首先就要分析問題是怎麼來的。

我們的位置資訊是在cpu以vector3的形式儲存的,裡面全部是float型別的值。float型別在cpu有23位尾數,而過大的座標在cpu已經導致精度沒有小座標高,因為大座標占用了更多的尾數在小數點前的數字上。而在gpu,對於opengl es的api會被編譯成highp型別,highp型別的位數具體多少位是不確定的,根據各個廠商的實現各有不同。很多都是24位浮點,這就導致了再一次浮點精度丟失。再者當過大的座標和過小的座標計算時,比如localtoworldmatrix矩陣變換到世界座標時,會有一次本地座標和矩陣第四列的加法,float型別大值+小值是常見的導致浮點誤差變大的原因。綜合以上幾個因素,發現比較適合使用相機相對渲染的方案。

什麼是相機相對渲染?

就是把相機當做世界座標原點,所有的計算相對於相機的位置,這樣就能把座標大小控制在相機相對座標系下,而相機周圍的物體正好是我們容易看到的,遠處的在相機相對座標系的座標很大的位置也不會被相機看到。這樣,我們就從大值的float計算變成了小值的計算。

怎麼實現呢?

主要思路是現在cpu算出localtoworld矩陣,但是這個world指的是相機相對座標系的world,即矩陣的第四列是在cpu用高精度的浮點減法減去相機的座標。將這個矩陣傳遞到gpu進行變換,變換出來的結果不是世界座標,而是相機相對座標(positionrs),然後將這個相對座標變換到相機空間,最後按原來的方式變換到投影空間就完成了。

相機相對渲染是怎麼做到減小浮點誤差的?

首先採用相機相對座標縮小了浮點的值的大小,使得小數點後的有效位數更多,精度更高。其次是避免了在gpu直接做小座標(localpos)加大座標的操作,而是變成了小座標(localpos)+相機相對座標(值比較小)。然後在cpu計算相機相對座標的時候採用了double型別,更高精度的減法,最後再轉成float,最大程度降低了浮點的誤差。

實現細節:

首先需要在想要支援的相機相對渲染的shader中定義自己的localtoworld矩陣,然後在每乙個renderer上掛指令碼每幀傳給gpu計算完成的矩陣。計算方法很簡單,就是取到transform的localtoworldmatrix矩陣,然後將第四列的前三個元素減去相機座標就ok。

_localtoworldmatrix =gettransform().localtoworldmatrix;

_localtoworldmatrix.m03 =(float)(_localtoworldmatrix.m03 - (double

)camera.transform.position.x);

_localtoworldmatrix.m13 = (float)(_localtoworldmatrix.m13 - (double

)camera.transform.position.y);

_localtoworldmatrix.m23 =(float)(_localtoworldmatrix.m23 - (double)camera.transform.position.z);

然後就是傳到gpu,先變換到相機相對座標系,然後變換到相機座標。如何變換到相機座標系也很簡單:

inline float3 crr_getviewposition(float4 positionrs)

最後變換到投影空間:

//

相機相對空間

float4 positionrs =crr_getworldpositionrelative(positionos);

//正確的相機空間

float3 positionvs =crr_getviewposition(positionrs);

//裁減空間

return mul(unity_matrix_p,float4(positionvs,1));

如果在shader中要支援世界座標的訪問,只需要將相機相對座標+相機座標的位置即可。

本文思路主要參考unity hdrp camera-relative render的文件。有興趣的小夥伴可以去看看。hdrp預設支援相機相對渲染,所以如果用hdrp的小夥伴就不用為這個困擾了。

Unity 世界座標與本地座標

你在物體元件上看到的就是本地座標,是相對于父物體的座標。在 中用transform.position獲得的是世界座標,所以不一定等於你在元件上看到的值。transform.rotation的旋轉值範圍為 1 1 對應著 180 180 transform.rotation值是float型別的,當你想...

Unity世界座標區域性座標轉換

世界座標轉ui區域性座標比較常用,也比較簡單。vector3 worldposition 3dtransform.position vector3 screenposition 3dcamera.worldtoscreenpoint worldposition vector2 localpostio...

Unity 本地座標到世界座標,世界座標到本地座標

世界 本地 public gameobject mtarget public gameobject mpar 這個注意一定要是mtarget的第一父物體。use this for initialization void start world mtarget.transform.localposit...