簡體   English   中英

使用定點算法以高精度和大數歸一化向量

[英]Normalizing a vector with great accuracy and big numbers using fixed-point arithmetic

為什么我需要這個?

我正在創造一個關於太空的游戲。 要使太空游戲發揮作用,它需要大而准確的數字。 浮點數對於這樣的應用來說並不是很好,因為如果你離世界很遠,精度會更差,所以物理就不一樣了,等等。

有什么問題

在太空中通常傾向於 pl.nets。 所以要創建一個我必須生成球體 (pl.nets) 網格。 問題是如果我想要一個像木星一樣的 pl.net(半徑為 ~69911km),當我標准化一個點並將它乘以 pl.net 的半徑,然后將它“放在”pl 的表面上.net,我沒有足夠的精度(網格不是很圓,大約有10-15m的誤差)。 這是該錯誤的一些鏡頭(pl.net 的閃爍是由於視頻壓縮,網格中最小正方形的邊長約為 10m)。 問題不在於數字,而在於我用來標准化點的方法。

在視頻中,我使用了 64 位定點數,精度為 20 位。 我只是通過將它們與快速平方根反比相乘來對這些點進行歸一化(我只使用了牛頓方法的一次迭代,兩次迭代更好但仍然不夠)。 其中,我將定點數轉換為雙精度浮點數,只是為了快速平方根倒數。

我的想法和嘗試

我認為獲得如此精確的向量歸一化的唯一解決方案是使用迭代方法:

  1. 正常計算點(將其歸一化並乘以 pl.nets 半徑)
  2. 將它越來越靠近半徑,直到誤差足夠小(我希望它是 0.01m)

2.步驟是困難的一步。 我沒有數學技能,也沒有計算機科學領域的任何經驗來解決這個問題。 我們可以迭代地增加或減少一個向量一定數量,但是數量是多少,我們怎么知道我們不會超過它。 我考慮過再次嘗試牛頓法,但使用的是實際坐標,而不是長度的平方根倒數。 其中包括 128 位除法以保持所需的精度,這是無法有效完成的(請記住,對於 pl.net 網格的每個頂點,我可能必須這樣做一百萬次)

對此有什么想法嗎?

而且比例不必停在那里,如果我想制造一顆恆星怎么辦,太陽的半徑是木星的 10 倍。 還有更大的星星。

我可能不是第一個思考這個問題的人,因為很多人都嘗試過按真實比例制作地球(請記住,地球半徑比木星小 10 倍)。 我可能不會是最后一個嘗試這個的人,所以遲早會有答案。

更新 1,經過一些評論

  1. 是的,我使用 3D 向量,其中包含 64 位數字和 20 位精度的數字。 而且我顯然正在使用 pl.net 的局部坐標系來創建點(我需要標准化才能根據需要工作)。
  2. 使用浮點數是我最后的希望。 由於不同尺度的精度差異,它們真的不是為這種應用而設計的。
  3. 我想讓天體(恆星,pl.nets)工作的尺度至少是太陽的大小。 對於 position 個對象,我使用了兩個 64 位整數(實際上是一個 128 位整數),因此如果我想要游戲中的世界可以比我們實際可觀察到的宇宙更大(多虧了定點數)。 但是我不需要 128 位整數來表示 pl.nets 的頂點位置,64 位就足夠了(盡管我可以在這個過程中使用 128 位整數來計算值)。 正如我提到的,網格頂點的誤差最好小於 0.01 米,其中 1 個坐標單位為 1 米(想象一下在 pl.net 的表面上行走,任何大於 0.01 米的東西都可能是顯)。

我如此推定點數的原因是因為物理原因我不能將對象的 position 用作浮點數。 假設我們有一個比木星還大的 pl.net。 因為我需要計算相對於木星 position 的頂點,然后在着色器中從它們中減去相機 position(主要限於 32 位浮點數),所以錯誤只會累加起來並且很明顯 有解決方法,但核心問題總是會出現在使用浮點數上。

長話短說

我需要一種方法來計算球體上的一個點,其半徑可以大到 1000000000 = 10^9,但誤差小於 0.01,給定點需要 rest 的角度。這個方法需要相對有效率。

我的理解是你是

  1. 制作一個 3D 向量,以米為單位表示為具有 20 位精度的 64 位定點數。
  2. 將向量歸一化為單位長度,通過快速平方根反比近似進行縮放。
  3. 按木星半徑 69911 公里縮放矢量。

挑戰在於,在按 pl.net 半徑縮放時,歸一化向量中 inv sqrt 近似或舍入的任何誤差都會被放大。 要達到 0.01 米的精度,單位矢量的長度需要精確到誤差小於

0.01 米 / 69911 公里 = 1.43×10 -10

為此,inv sqrt 需要精確到 11 位或更好,並且單位向量至少需要 32 個小數位(對於 2 -33 = 1.16×10 -10的舍入誤差)。

我建議計算局部坐標系中的表面點,原點位於 pl.net 的中心,用 64 位浮點數表示,然后將這些點轉換為宇宙坐標系。 64 位浮點數精確到 ~16 位,足以獲得所需的精度。 假設 64 位浮點數是一個選項,這似乎是最簡單和最有效的解決方案。

或者對於迭代“輕推”方法,您可以這樣做:

R = planet radius
x, y, z = surface point with correct angle but possibly wrong length
for 1, 2, ...
  scale = 0.5 * (R² / (x² + y² + z²) + 1)
  x *= scale
  y *= scale
  z *= scale

這是一個快速收斂的不動點迭代。 運行示例,從非常不准確的表面點 x、y、z = [6991.1, 55928.8, -6991.1] 開始:

#  x        y          z        Radius
-------------------------------------------------
0  6991.10  55928.80  -6991.10  56795.96489065047
1  8791.84  70334.70  -8791.84  71425.22857460588
2  8607.42  68859.40  -8607.42  69927.05096841766
3  8605.45  68843.60  -8605.45  69911.00184215968

進一步的想法:

請記住,對於 pl.net 網格的每個頂點,我必須執行此操作可能一百萬次

從視頻中可以看出,您已經在應用細節層次方案來減少遠處的頂點數量。 分層網格也可用於減少靠近表面時的頂點:基於平截頭體剔除,當它們的父母不在屏幕上時跳過更精細階段的頂點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM