Рисуем сферу с помощью двух треугольников

Сфера в трехмерной графике обычно состоит из сотни-другой треугольников, при этом половина из них не видна человеку, поскольку их отсекает face culling и/или zbuffer.

Сфера всегда кажется наблюдателю кругом. Поэтому можно схитрить и вместо сферы нарисовать плоскость, которая всегда повернута к наблюдателю одной стороной. А уже на этой плоскости с помощью математики и какой-то матери изобразить текстуру, освещение и блик.

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

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

Вычисление размера плоскости

Размер плоскости будем вычислять используя школьную геометрию, в частности, уроки про теорему Пифагора, касательные к окружности и определения подобных треугольников. Итак, взгляните на эти картинки (наблюдатель слева, сфера справа, вид сбоку, синим обозначена гипотенуза):

RLDHlCh

Поскольку L — это касательная к окружности, треугольник LDR является прямоугольным, а высота H проведенная из прямого угла делит этот треугольник на два ему подобных. Нас интересует треугольник hCl.

Нам известен радиус нашей сферы R и дистанция до камеры D. Обладая знаниями восьмого класса о теореме Пифагора, найдем катет L прямоугольного треугольника LDR:

L=\sqrt{D^2 - R^2}

Также нам известен катет C треугольника hCl:

C = D - R

Поскольку треугольники подобны, то соотношения сторон у них одинаковые:

\frac{h}{C}=\frac{R}{L}

Значит искомый катет h равен:

h=\frac{R*(D-R)}{L}

Итак, мы получили размер нашей плоскости в зависимости от расстояния до камеры и радиуса нашей сферы — с чем я нас и поздравляю!

Вычисление нормали для сферы спроецированной на плоскость

Осталась сущая мелочь — получить нормаль сферы в каждой точке нашей плоскости. Дальше пойдет магия, так что не удивляйтесь. Идея математически верная, но, возможно, есть способ гораздо проще. Если у вы его знаете — пишите в комментариях, не стесняйтесь.

Фактически, наша плоскость представляет собой экран для проецирования сферы. Поэтому построим матрицу перспективной проекции, чтобы знать трехмерные координаты каждой точки на этой сфере относительно камеры. Матрица проекции строится по четырем параметрам:

  • угол обзора (fov) — в нашем случае это угол между L и D умноженный на два, то есть 2.0 * acos(L / D)
  • соотношение сторон экрана (aspect ratio) — наша плоскость квадратная, поэтому единичка
  • ближняя плоскость отсечения (near plane) — расстояние от камеры до ближней точки сферы (DR)
  • дальняя плоскость отсечения (far plane) — расстояние от камеры до дальней точки сферы (D + R)

Напрямую эта матрица проекции нам не подходит, поскольку выполняет операцию приведения координат из пространства камеры в плоскость экрана. Для нашей задачи ее необходимо сперва инвертировать.

Умножив полученную матрицу на любой вектор (X, Y, -1) мы получим точку на ближней к наблюдателю плоскости в пространстве камеры. Затем мы построим луч к этой точке из камеры и найдем ближайшее пересечение луча со сферой. После этого можно найти нормаль, вычислив вектор от центра сферы до полученной точки пересечения.

Лирическое отступление — умножив инвертированную матрицу проекции на вектор (0, 1, -1), мы получим вектор, у которого значение Y будет равно размеру нашей плоскости, то есть h. Но мы ведь не ищем легких путей, правда? (:

Результат и исходники

Тестовая схема сделана при помощи библиотеки three.js. На одной половине экрана рисуется честная сфера, на другой имитация. Попробуйте догадаться без подглядывания в исходники — где истинная сфера, а где ложная. Подсказка: ложная сфера выглядит лучше истинной. Освещение сделано практически один в один с предыдущей заметкой об освещении куба.


Добавить комментарий

Ваш e-mail не будет опубликован.