Радиус проектируемой сферы - PullRequest
       37

Радиус проектируемой сферы

4 голосов
/ 15 сентября 2010

я хочу уточнить предыдущий вопрос :

Как проецировать сферу на экран?

(2) дает простое решение:

approximate radius on screen<sub>[CLIP SPACE]</sub> = world radius * cot(fov / 2) / Z

with:
fov = field of view angle
Z   = z distance from camera to sphere

<b>result is in clipspace</b>, multiply by viewport size to get size in pixels

Теперь моя проблема в том, что у меня нет поля зрения. Известны только матрицы вида и проекции. (И размер области просмотра, если это поможет)

Кто-нибудь знает, как извлечь FOV из проекционной матрицы?

Обновление:

В моем случае это приближение работает лучше:

float radius = glm::atan(radius/distance);
radius *= glm::max(viewPort.width, viewPort.height) / glm::radians(fov);

Ответы [ 4 ]

3 голосов
/ 07 ноября 2013

Я немного опоздал на эту вечеринку.Но я наткнулся на эту тему, когда смотрел на ту же проблему.Я потратил целый день на изучение этого вопроса и работал над некоторыми прекрасными статьями, которые я нашел здесь: http://www.antongerdelan.net/opengl/virtualcamera.html

В итоге я начал с матрицы проекции и работал в обратном направлении.Я получил ту же формулу, которую вы упомянули в своем посте выше.(где cot (x) = 1 / tan (x))

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, вы получите:

Sy = 1 / tan(fovy/2) = cot(fovy/2)

перестановка:

tan(fovy/2) = 1 / Sy

взяв арктан (обратный загар) с обеих сторон, мы получим:

fovy/2 = arctan(1/Sy)

так,

fovy = 2 x arctan(1/Sy)

Не уверен, если вы все еще заботитесь - это было какое-то время!- но, может быть, это поможет кому-то еще.

2 голосов
/ 15 сентября 2010

Обновление: см. Ниже.

Поскольку у вас есть матрицы вида и проекции, вот один из способов сделать это, хотя, вероятно, он не самый короткий:

  • преобразовать центр сферы в пространство вида с использованием матрицы вида: вызвать точку результата C
  • преобразовать точку на поверхности сферы, например, C + (r, 0, 0) в мировых координатах, где rмировой радиус сферы, в пространство зрения;назовите точку результата S
  • compute rv = расстояние от C до S (в пространстве вида)
  • пусть точка S1 в координатах вида будет C + (rv, 0, 0) - т.е. другая точкана поверхности сферы в пространстве вида, для которой линия C -> S1 перпендикулярна вектору «взгляда»
  • проецирует C и S1 в координаты экрана, используя матрицу проекции как Cs и S1s
  • вычислить радиус экрана = расстояние между Cs и S1s

Но да, как сказал Брандорф, если вы сможете сохранить переменные камеры, такие как FOVy, это будет намного проще.: -)

Обновление: Вот более эффективный вариант вышеупомянутого: сделать инверсию матрицы проекции.Используйте его, чтобы преобразовать края области просмотра обратно в область просмотра.Тогда вам не нужно будет проецировать каждый блок в координаты экрана.

Еще лучше, сделайте то же самое с матрицей вида и трансформируйте усеченную камеру обратно в мировое пространство.Это было бы более эффективно для сравнения многих блоков с;но сложнее понять математику.

1 голос
/ 05 сентября 2013

Ответ, отправленный по вашей ссылке radiusClipSpace = radius * cot(fov / 2) / Z, где fov - угол поля зрения, а Z - расстояние по оси z, определенно работает. Однако имейте в виду, что radiusClipSpace необходимо умножить на ширину области просмотра, чтобы получить меру в пикселях. Значение, измеренное в radiusClipSpace, будет значением от 0 до 1, если объект помещается на экране.

Альтернативным решением может быть использование телесного угла сферы. Телесный угол, образованный сферой в небе, - это, в основном, область , которую он покрывает при проекции на сферу единицы.

enter image description here

Формулы даны на по этой ссылке , но примерно то, что я делаю, это:

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.

0 голосов
/ 15 сентября 2010

Поле зрения непосредственно не сохраняется в матрице проекции, а используется, когда вы вызываете gluPerspective для построения результирующей матрицы.

Наилучшим подходом было бы просто сохранить все переменные камеры в своем собственном классе, например, в классе frustum, чьи переменные-члены используются при вызове gluPerspective или аналогичных.

Может быть возможно вернуть FOVy обратно из матрицы, но математика, которая мне нужна, ускользает от меня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...