Равномерное случайное (Монте-Карло) распределение на единичной сфере

Мне нужно уточнение с алгоритмом, генерирующим случайные значения для моего луча-трассировщика.
Я испускаю лучи из одной точки. И у меня есть проблема с распределением этих лучей: мне нужно, чтобы распределение было равномерным, но это не...

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

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

Теперь моя идея: я хочу создать тетраэдр, нормализовать его вершины, разделить каждую грань (треугольник) с точкой посередине, нормализовать ее и повторить рекурсивно, пока у меня не будет достаточно очков. Затем я немного "искажаю" эти точки. Затем я нормализую их снова. Это.

Я понимаю, что этот метод не является чисто математическим методом Монте-Карло, потому что я не использую случайное распределение ни на одном шаге, кроме последнего. И мне не нравится это решение для этой сложности.

Может ли кто-нибудь предложить что-нибудь более простое, но все же

  • случайное
  • равномерная
  • быстро
  • просто

Спасибо!

EDIT:
Мне нужен быстрый метод, а не только правильный. Вот почему я спрашиваю о Монте-Карло. Предоставленные ответы являются правильными, но не быстрыми. Метод с тетраэдром является быстрым, но не очень "случайным" = > неверным.
Мне действительно нужно что-то более подходящее.

Ответ 2

Здесь реализация Java, которую я использовал в прошлом:

public static double[] randomPointOnSphere(Random rnd)
{
    double x, y, z, d2;
    do {
        x = rnd.nextGaussian();
        y = rnd.nextGaussian();
        z = rnd.nextGaussian();
        d2 = x*x + y*y + z*z;
    } while (d2 <= Double.MIN_NORMAL);
    double s = Math.sqrt(1.0 / d2);
    return new double[] {x*s, y*s, z*s};
}

Ответ 3

Вам действительно нужно случайное распределение или равномерное распределение по сфере?

Тогда я бы предложил углы ZCW, которые равномерно распределены по всей сфере и быстро вычисляются. Другие методы - TheSydneyOperaHouse (SOPHE) и Отталкивание. (поиск отталкивания .c) Метод отталкивания довольно неплохой, но медленный: он итеративно распределяет точки равномерно по сфере. К счастью, это нужно сделать только один раз.

Это используется в кристаллографии и ЯМР, потому что для образцов порошков быстрее использовать равномерное распределение по сравнению с случайным распределением (вам нужно меньше очков).

Здесь - реализация Python для ZCW.

Подробнее в этих статьях:

Ответ 5

Если вы не трассируете только тривиальные сцены, будет ли ваше время рендеринга на самом деле доминировать на выборке? Если нет, то, вероятно, еще не стоит оптимизировать, хотя стоит прочитать и понять методы единообразной выборки, приведенные в других ответах.

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

В любом случае, вы должны проконсультироваться на форумах на ompf.org. Там есть супер-хардкор-рейтрейзеры.

Ответ 6

Для сферических секций ваш угол равномерно распределяется в phi (полярный угол) и cos(theta) (для theta азимутальный угол) между вашими пределами.

В псевдокоде:

phi = phi_low_limit        + rand()*(phi_high_limit       - phi_low_limit)
ct = cos(theta_high_limit) + rand()*(cos(theta_low_limit) - cos(theta_high_limit))
// The order is inverted here because cos heads down for increasing theta
theta = arccos(ct)

Это частный случай правила, который говорит инвертировать Jacobian и генерировать равномерно в этом пространстве этих координат.

Примечание. Обратите внимание, что я использую противоположное соглашение для phi и theta из линии Дэвида Нормана.

Обратите внимание: это не самый быстрый метод, а скорее тот, который иллюстрирует общий принцип.