光線追蹤中的透視畸變如何矯正?
更新=======================================================
改了下鏡頭的位置以及鏡頭遠近,調整了物體的大小以及相對位置,效果好了很多,如下圖,不過當兩個球離得稍遠時還是會有明顯的畸變。原問題=======================================================
剛開始寫簡單的光線追蹤,如圖,相機是看向世界中心坐標的,藍色的球體因為位於中心坐標附近,所以變化不明顯,而右邊綠色的球體明顯有了透視畸變,請問這種問題該如何解決?是不是我的castray的代碼有問題?代碼如下castRay相關代碼:
Vec3f Y(0, 1, 0);
Vec3f lookAt(0, 0, 0);
//camara model
Vec3f camOri(0, 4, 20);
Vec3f camDir((lookAt - camOri).normalize());
Vec3f camRight(camDir.cross(Y).normalize());
Vec3f camUp(camRight.cross(camDir).normalize());
float aspect = width * 1.0 / height;
for (unsigned int x = 0; x &< width; ++x) { fprintf(stderr, " Rendering %3.2f%%", (x + 1) * 1.0 / width * 100); for (unsigned int y = 0; y &< height; ++y) { unsigned int k = y * width + x; float xx = 0, yy = 0; //adjust if (aspect &> 1) {
xx = ((2 * (x + 0.5) / width) - 1) * aspect;
yy = 1 - (2 * (y + 0.5) / height);
}
if (aspect &< 1) { xx = ((2 * (x + 0.5) / width) - 1); yy = 1 - (2 * (y + 0.5) / height) / aspect; } if (aspect == 1) { xx = (2 * (x + 0.5) / width) - 1; yy = 1 - (2 * (y + 0.5) / height); } Vec3f raydir = (camDir + camRight * xx + camUp * yy).normalize(); pixel[k] = trace(camOri, raydir, objects, 0); } }
這跟光線跟蹤沒關係,跟攝像機設置有關係。不論你用光線跟蹤還是光柵化,都會這樣。調fov吧。
一般的攝像機模型使用透視投影,可以說是模仿理想的直線性鏡頭 (rectilinear lens)。這是指,場景中的直線經過投影后,在投影平面上仍然是直線。基於這個約束,必然會導致題主所說,畫面中心以外區域的透視變形 (perspective distortion) 現象。(題主用立方體代替球體就能明白。)
另一種投影是使用魚眼投影 (fisheye projection),例如 equidistant fisheye。但它們不再是直線性鏡頭,直線投影后會變成曲線。用光線追蹤是很容易實現的,光柵化就只能以後處理實現。
正如把地球畫成平面世界地圖,不同的平面投影只會有不同的變形,不可能有完美的選擇。只有放棄投影至平面才可解決這問題,例如用地球儀。而在圖形學上,可以用穹頂投影機,或更常見的 VR HMD,就可避免平面投影的變形。畸變這麼明顯應該是你的視錐角度設置的不好導致的。不過畸變這種事情,如果你是用這種投影方法的話,是無法避免的。照相機也是這樣。
至於人眼的投影比較複雜。你經過觀察就會發現,如果你直視一條很長的橫樑的中間,兩邊其實是彎曲的。如果要近似這種效果,可以在人的面前放一個巨大的球體,然後光線首先平行於「前」方向射到球體,計算那一點的法線,然後改為按照法線的方向射出去。
這是一道幾何題:
無畸變=等距映射,等距(isometric)字面的意思就是等度量,即保持像的每一個局部等尺度並且保角(亦稱共形). 對於題主的問題而言,二維曲面局部的度量由一個張量(數組)決定:.
有度量就能定義面內的夾角——查一查你的高中課本是怎麼定義向量之間的夾角的。
舉個例子,我想知道平面上很相近兩個像素點之間的距離的平方, 它是這麼算的:
, 其中
只要取圖像上各個點, 就是勾股定理,就是平面歐氏幾何。
球面幾何是怎樣的呢,如果距離很近的兩個像素點方位角分別是,那麼
,其中
這裡, 這就是球面幾何。
任何攝影都有一個光學中心,相機就是以這個光學中心為球心,將球面上的影像映射到平面的ccd/屏幕上。也就是說,從「真實」的影像到你屏幕上的影像,是一個這樣的映射:
伴隨的度規矩陣映射是。
這個所謂的的對角元很可能不是1(圖像的伸縮),甚至還有非0的非對角元(圖像的剪切形變),取決於你的. 這就是你照片邊緣的球被扯成了蛋的原因,不過一般來講,圖像中心的度量矩陣,就剛好是, 所以圖片中間的球還是比較端正的。
那題主你想問的問題,用上面的話說,就是:
有沒有辦法通過知道是什麼並且修正它,使得?
答案是做不到的,因為球面有曲率,而平面(屏幕)沒有,曲率是一階導數和二階導數的函數,我們不可能選擇一個新的產生一個新的使得一片區域內曲率處處為0
有一款軟體叫PTGui的,做全景的同學可能比較熟悉,其中的小行星效果也僅僅是達成一個比較妥協的投影:, 是隨著位置變化的一個因子,我們稱為共形(conformal)映射.
這裡由於對角而且對角元相等,可以發現圖像各點是沒有剪切畸變的,只是各點放大率不一樣。
值得一提的是,共形映射保圓*,雖然圖像各點放大率不一樣,但一個端正的圓,投影之後肯定還會是一個端正的圓,雖然各點放大率不一樣,但是至少不會是個蛋。這一點可以保證題主的綠球還是端正的。
*註:直線是半徑無窮大的圓,圖中任何直線段都變成了圓的一部分.
看起來像fov過大,調小試試
真正解決的唯一方法只能用球面顯示器,且人眼在球心位置(現在的vr是這個方法吧),不然總存在一個角度觀察正確,另外一個角度(位置)觀察畸變
推薦閱讀:
※為什麼會有那麼多類型的 BxDF?
※高斯模糊和高斯有什麼關係?
※為何遊戲里的3d渲染速度非常快,而三維軟體實時渲染非常慢?
※為什麼計算機圖形不用四面體做基本圖元?
※如何學習計算機圖形學?