Радиус проектируемой сферы
Я хочу уточнить предыдущий вопрос:
Как проецировать сферу на экран?
(2) дает простое решение:
приблизительный радиус на экране [ПРОСТРАНСТВО КЛИПА ] = мировой радиус * кроватка (fov / 2) / Z с: fov = угол поля зрения Z = z расстояние от камеры до сферы результат находится в пространстве клипа, умножьте на размер области просмотра, чтобы получить размер в пикселях
Теперь моя проблема в том, что у меня нет поля зрения. Известны только матрицы вида и проекции. (И размер области просмотра, если это поможет)
Кто-нибудь знает, как извлечь FOV из проекционной матрицы?
Обновить:
Это приближение работает лучше в моем случае:
float radius = glm::atan(radius/distance);
radius *= glm::max(viewPort.width, viewPort.height) / glm::radians(fov);
4 ответа
Обновление: см. Ниже.
Поскольку у вас есть матрицы вида и проекции, вот один из способов сделать это, хотя, вероятно, он не самый короткий:
- преобразовать центр сферы в пространство вида, используя матрицу вида: вызвать точку результата C
- преобразовать точку на поверхности сферы, например, C+(r, 0, 0) в мировые координаты, где r - мировой радиус сферы, в пространство обзора; назвать точку результата S
- вычислить rv = расстояние от C до S (в пространстве обзора)
- пусть точка S1 в координатах вида будет C + (rv, 0, 0) - то есть другой точкой на поверхности сферы в пространстве вида, для которой линия C -> S1 перпендикулярна вектору "взгляда"
- спроецируйте C и S1 в экранные координаты, используя матрицу проекции как Cs и S1s
- вычислить радиус экрана = расстояние между Cs и S1s
Но да, как сказал Брандорф, если вы сможете сохранить переменные камеры, такие как FOVy, это будет намного проще.:-)
Обновление: вот более эффективный вариант вышеупомянутого: сделайте обратную матрицу проекции. Используйте его, чтобы преобразовать края области просмотра обратно в область просмотра. Тогда вам не придется проецировать каждый блок в координаты экрана.
Более того, сделайте то же самое с матрицей вида и превратите усеченную камеру обратно в мировое пространство. Это было бы более эффективно для сравнения многих блоков с; но сложнее понять математику.
Я немного опоздал на эту вечеринку. Но я наткнулся на эту тему, когда смотрел на ту же проблему. Я провел целый день, изучая это, и работал над некоторыми прекрасными статьями, которые я нашел здесь: http://www.antongerdelan.net/opengl/virtualcamera.html
В итоге я начал с матрицы проекции и работал в обратном направлении. Я получил ту же формулу, которую вы упомянули в своем посте выше. (где кроватка (х) = 1/ загар (х))
radius_pixels = (radius_worldspace / {tan(fovy/2) * D}) * (screen_height_pixels / 2)
(где D - расстояние от камеры до ограничивающей сферы цели)
Я использую этот подход, чтобы определить радиус воображаемого трекбола, который я использую, чтобы вращать мой объект.
Кстати, Флориан, вы можете извлечь фови из матрицы проекции следующим образом:
Если вы возьмете компонент Sy из матрицы проекции, как показано здесь:
Sx 0 0 0
0 Sy 0 0
0 0 Sz Pz
0 0 -1 0
where Sy = near / range
and where range = tan(fovy/2) x near
(вы можете найти эти определения на странице, на которую я ссылался выше)
если вы подставите диапазон в приведенном выше уравнении, вы получите:
Sy = 1 / tan(fovy/2) = cot(fovy/2)
перестановки:
tan(fovy/2) = 1 / Sy
взяв арктан (обратный загар) с обеих сторон, получим:
fovy/2 = arctan(1/Sy)
так,
fovy = 2 x arctan(1/Sy)
Не уверен, если вы все еще заботитесь - это было какое-то время! - но, возможно, это поможет кому-то еще.
Ответ размещен по вашей ссылке radiusClipSpace = radius * cot(fov / 2) / Z
где fov - угол поля зрения, а Z - расстояние по оси z, определенно работает. Однако имейте в виду, что radiusClipSpace
должен быть умножен на ширину области просмотра, чтобы получить меру в пикселях. Значение, измеренное в radiusClipSpace, будет значением от 0 до 1, если объект помещается на экране.
Альтернативным решением может быть использование телесного угла сферы. Сплошной угол, образованный сферой в небе, - это, в основном, область, которую он охватывает, проецируясь на сферу объекта.
Формулы приведены по этой ссылке, но примерно то, что я делаю, это:
if( (!radius && !distance) || fabsf(radius) > fabsf(distance) )
; // NAN conditions. do something special.
theta=arcsin( radius/distance )
sphereSolidAngle = ( 1 - cosf( theta ) ) ; // not multiplying by 2PI since below ratio used only
frustumSolidAngle = ( 1 - cosf( fovy / 2 ) ) / M_PI ; // I cheated here. I assumed
// the solid angle of a frustum is (conical), then divided by PI
// to turn it into a square (area unit square=area unit circle/PI)
numPxCovered = 768.f*768.f * sphereSolidAngle / frustumSolidAngle ; // 768x768 screen
radiusEstimate = sqrtf( numPxCovered/M_PI ) ; // area=pi*r*r
Это работает примерно до тех же чисел, что и radius * cot(fov / 2) / Z
, Если вам нужна только оценка площади, покрытой проекцией сферы в пикселях, это может быть простой способ.
Я не уверен, что можно легко найти лучшую оценку телесного угла усеченного конуса. Этот метод включает в себя больше соревнований, чем radius * cot(fov / 2) / Z
,
FOV не сохраняется непосредственно в матрице проекции, а используется, когда вы вызываете gluPerspective для построения результирующей матрицы.
Наилучшим подходом было бы просто сохранить все переменные вашей камеры в их собственном классе, таком как класс frustum, чьи переменные-члены используются, когда вы вызываете gluPerspective или подобное.
Может быть возможно вернуть FOVy обратно из матрицы, но математика требует ускользнуть от меня.