Широкоугольный объектив не должен вести себя иначе, чем другие обычные модели объективов. Они просто имеют больший FOV (в D3DXMatrixPerspectiveFovLH
смысле - я предполагаю, что вы используете DirectX) или большие левые / правые и нижние / верхние значения (в glFrustum
смысле OpenGL ).
Я считаю, что действительно интересная часть заключается в моделировании объектива «рыбий глаз». Есть « Рыбий глаз», который вы можете изучать, он поставляется с источником.
Истинная рыбий глаз
Однако проекция объектива типа «рыбий глаз» весьма нелинейна. В более распространенном (насколько мне известно, видеонаблюдении) виде объектива точка 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
Существуют и другие сопоставления типа «рыбий глаз», которые могут дать более интересные эффекты. Тебе решать.
Я вижу два способа реализации эффекта «рыбий глаз» в HLSL.
Способ 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);
...
Недостатки : поскольку весь конвейер рендеринга был задуман для линейных преобразований, результирующая проекция является точной для вершин, но все вариации будут неправильными, а также координаты текстуры, и треугольники все равно будут отображаться в виде треугольников, даже если они должны выглядеть искаженными.
Обходные пути : было бы приемлемо получить лучшее приближение, отправив уточненную геометрию в графический процессор с большим количеством подразделений треугольника. Это также может быть выполнено в геометрическом шейдере, но поскольку этот шаг выполняется после вершинного шейдера, геометрический шейдер будет довольно сложным, поскольку ему придется выполнять собственные дополнительные проекции.
Способ 2: выполнить проекцию на фрагмент шейдера
Другой метод - визуализация сцены с использованием широкоугольной проекции, а затем искажение изображения для достижения эффекта «рыбий глаз» с использованием полноэкранного фрагментного шейдера.
Если точка M
имеет координаты (x,y)
на экране «рыбий глаз», это означает, что она имеет координаты (x,y,z)
на поверхности полушария, с z = sqrt(1-x*x-y*y)
. Это означает, что у нас были координаты (ax,ay)
в нашей сцене, отрисованные с помощью поля зрения 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);
}
Преимущество : вы получаете идеальную проекцию без искажений, кроме тех, что из-за точности пикселей.
Недостаток : вы не можете физически просматривать всю сцену, поскольку поле обзора не может достигать 180 градусов. Кроме того, чем больше FOV, тем хуже точность в центре изображения ... именно там, где вам нужна максимальная точность.
Обходные пути : потерю точности можно улучшить, выполнив несколько проходов рендеринга, например, 5, и выполните проекцию в виде карты куба. Другой очень простой обходной путь - просто обрезать конечное изображение до требуемого поля зрения - даже если объектив имеет угол обзора 180 градусов, вы можете захотеть отрендерить только его часть. Это называется «полнокадровым» «рыбьим глазом» (что довольно иронично, поскольку создается впечатление, что вы получаете «полное» что-то, пока оно фактически обрезает изображение).
(Примечание: если вы сочли это полезным, но недостаточно ясным, скажите, пожалуйста, я хочу написать более подробную статью об этом).
Должно быть
z = sqrt(1.0 - in_Point.x * in_Point.x - in_Point.y * in_Point.y)
, верно?Моя реализация GLSL:
источник