Ширококутний об'єктив не повинен вести себе інакше, ніж інші звичайні моделі лінз. Вони просто мають більший FOV (в D3DXMatrixPerspectiveFovLH
сенсі - я припускаю, що ви використовуєте DirectX) або більший лівий / правий і нижній / верхній значення (у glFrustum
сенсі OpenGL ).
Я вважаю, що дійсно цікава частина полягає в моделюванні лінзи риб'ячого ока. Існує Fisheye Quake, який можна вивчити, він постачається з джерелом.
Справжня проекція риб'ячого ока
Проте проекція лінзи з риб'ячим оком вкрай нелінійна. У більш поширеному (наскільки мені відомо, який обмежується камерами спостереження) об'єктиві, точка M
простору проектується на поверхню одиничної півкулі, то ця поверхня проходить паралельну проекцію на одиничний диск:
M
x M: world position
\ M': projection of M on the unit hemisphere
\ ______ M": projection of M' on the unit disc (= the screen)
M'_x' `-.
,' |\ `.
/ | \ \
/ | \ \
| | \ |
__________|_____|____\_________|_________
M" O 1
Існують і інші відображення риб'ячого ока, які можуть дати більш цікаві ефекти. Тобі вирішувати.
Я бачу два способи реалізації ефекту «риб'яче око» при ЛПНЩ.
Спосіб 1: виконати проекцію на вершинній шейдері
Перевага : у коді майже нічого не потрібно змінювати. Фрагментний шейдер надзвичайно простий. Замість:
...
float4 screenPoint = mul(worldPoint, worldViewProjMatrix);
...
Ви робите щось подібне (можливо, це може бути багато спрощено):
...
// This is optional, but it computes the z coordinate we will
// need later for Z sorting.
float4 out_Point = mul(in_Point, worldViewProjMatrix);
// This retrieves the world position so that we can project on
// the hemisphere. Normalise this vector and you get M'
float4 tmpPoint = mul(in_Point, worldViewMatrix);
// This computes the xy coordinates of M", which happen to
// be the same as M'.
out_Point.xy = tmpPoint.xy / length(tmpPoint.xyz);
...
Недоліки : оскільки весь конвеєр рендерингу вважався лінійним перетворенням, отримана проекція є точною для вершин, але всі зміни будуть помилковими, як і текстурні координати, і трикутники все одно будуть виглядати як трикутники, хоча вони повинні виглядати спотвореними.
Обхідні шляхи : може бути прийнятним отримати краще наближення, надіславши вишукану геометрію до GPU, що має більше трикутних підрозділів. Це також може бути виконано в геометричній шейдері, але оскільки цей крок відбувається після вершинного шейдера, шейдер геометрії був би досить складним, оскільки він повинен був би виконати власні додаткові прогнози.
Спосіб 2: виконати проекцію на фрагменті шейдера
Іншим методом було б візуалізацію сцени за допомогою проекції широкого кута, а потім спотворення зображення для досягнення ефекту риб'ячого ока за допомогою шейдера на весь екран.
Якщо точка M
має координати (x,y)
на екрані риб'ячого ока, це означає, що вона мала координати (x,y,z)
на поверхні півкулі, з z = sqrt(1-x*x-y*y)
. А це означає, що (ax,ay)
в нашій сцені були координати, представлені FOV theta
такого a = 1/(z*tan(theta/2))
. (Не на 100% впевнений у моїй математиці тут, я перевіряю ще сьогодні сьогодні).
Таким чином, шейдер фрагмента буде приблизно таким:
void main(in float4 in_Point : POSITION,
uniform float u_Theta,
uniform sampler2D u_RenderBuffer,
out float4 out_FragColor : COLOR)
{
z = sqrt(1.0 - in_Point.x * in_Point.x - in_Point.y * in_Point.y);
float a = 1.0 / (z * tan(u_Theta * 0.5));
out_FragColor = tex2D(u_RenderBuffer, (in_Point.xy - 0.5) * 2.0 * a);
}
Перевага : ви отримуєте ідеальну проекцію, без перекосів, крім тих, завдяки точності пікселів.
Недолік : фізично неможливо переглянути всю сцену, оскільки FOV не може досягти 180 градусів. Крім того, чим більше FOV, тим гірша точність в центрі зображення ... саме там, де ви хочете максимальної точності.
Обхідні шляхи : втрату точності можна поліпшити, виконавши кілька пропусків візуалізації, наприклад 5, та виконати проекцію за допомогою кубової карти. Ще одне дуже просте вирішення полягає в тому, щоб просто обрізати кінцеве зображення до потрібного FOV - навіть якщо в самому об’єктиві є 180-градусний FOV, ви можете відтворити лише його частину. Це називається "повним кадром" риб'ячим оком (що начебто іронічно, оскільки створюється враження, що ви отримуєте щось "повне", тоді як воно насправді обробляє зображення).
(Примітка. Якщо ви вважаєте це корисним, але недостатньо зрозумілим, скажіть, будь ласка, мені здається, що я напишу більш детальну статтю про це).