簡單實現
魚眼模式(Fisheye)和普通的透視投影(Perspective projection),一個很大的區別就是魚眼的投影算法是非線性的(non-linear),實際照相機的情況是在鏡頭外面包圍一個半球體,將場景通過半球體投影到畫布上。大致如下:
M
x
\
\ ______
M'_x' `-.
,' |\ `.
/ | \ \
/ | \ \
| | \ |
__________|_____|____\_________|_________
M" O 1
M: world position
M': projection of M on the unit hemisphere
M": projection of M' on the unit disc (= the screen)
上面這個示意圖來自stack exchange 的回答,是Sam Hocever畫的
vertex shader 繪制一個長寬均為1的正方形,作為遠處的物體,紋理實際貼在這個正方形上面。
在 fragement shader 內將上面的這個算法實現即可。
設我們在屏幕上的點為(x0, y0)
直接得到外面球面上的點是
(x0, y0, z0) and z0 = sqrt(1 - x0^2 - y0^2)
遠處平面上的點和球面上的點共線所以坐標成比例關系,直接設x1 = a * x0
然后假設我們的遠處的物體就是一個平面,處於 z1 = -1
的位置,直接得到:
因為:
abs(z1) = 1 = z0 * a
z0 = sqrt(1 - x0^2 - y0^2)
所以:
a = 1.0 / z0 = 1.0 / (sqrt(1 - x0^2 - y0^2))
當然遠處平面的位置可以調整,離的近就大,離的遠就小,但是大小是不變的,仍然是一個邊長為 1 的正方形。所以可以調整的就是這個距離,也是 z1 的值。Sam Hocever 提供的思路是改變這個投影的 fov 的值。當 fov = PI / 2 時,和我剛剛設定的值一樣,z1 = -1。兩者關系為
z1 = 0.5 / tan(theta / 2)
總結:a = z1 / z0 = 0.5 / ((sqrt(1 - x0^2 - y0^2)) * tan(theta / 2))
其他實現方式
主要參考 Computer Generated Angular Fisheye Projections
上文介紹了兩種投影的方式,第一種前文已經解釋,后一種方式我正在學習中。
另外還有一個經典的項目,blinky
提供另外一種解決的辦法,先弄出一個包圍盒,紋理貼在包圍盒上,然后在轉換成魚眼效果。
具體的實現看源碼。
本文的主要參考鏈接
[How do I create a wide-angle / fisheye lens with HLSL?](http://
gamedev.stackexchange.com/questions/20626/how-do-i-create-a-wide-angle-fisheye-lens-with-hlsl)