Равномерное случайное (монте-карло) распределение на единичной сфере
Мне нужно разъяснение с алгоритмом генерации случайных значений для моего любимого трассировщика лучей.
Я излучаю лучи из одной точки. И у меня есть проблема с распределением этих лучей: мне нужно, чтобы распределение было равномерным, но это не так...
Проблема, с которой я сталкиваюсь сейчас, состоит в том, что распределение, которое изначально является однородным, не является равномерным после моих искажений пространства результатов.
Так, например, я генерирую r и t углы, если полярная система координат. Распределение не является равномерным и не может быть равномерным: пространство вблизи каждого полюса имеет гораздо большую плотность результатов, чем, скажем, близко к экватору. Причина довольно ясна: я преобразовываю равномерно распределенные точки из цилиндрического пространства в сферические. И я искажаю результаты. Та же проблема, если я нормализую точки, сгенерированные случайным образом в кубе.
Теперь моя идея такова: я хочу создать тетраэдр, нормализовать его вершины, разделить каждую грань (треугольник) точкой в середине, нормализовать ее и повторять рекурсивно, пока у меня не будет достаточно точек. Затем я немного "искажаю" эти моменты. Затем я снова их нормализую. Вот и все.
Я понимаю, что этот метод не является чисто математическим методом Монте-Карло, потому что я не использую случайное распределение ни на одном этапе, кроме последнего. И мне не нравится это решение для этой сложности.
Может кто-нибудь предложить что-нибудь более простое, но все же
- случайный
- единообразный
- быстро
- просто
Спасибо!
РЕДАКТИРОВАТЬ:
Мне нужен быстрый метод, а не только правильный. Вот почему я спрашиваю о Монте-Карло. Ответы предоставлены правильные, но не быстрые. Метод с тетраэдром быстрый, но не очень "случайный" => неверный.
Мне действительно нужно что-то более подходящее.
6 ответов
Вот алгоритм, который позволяет вам генерировать точки, случайно распределенные на единичной сфере.
Вот реализация 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};
}
Вам действительно нужно случайное распределение или равномерное распределение по сфере?
Тогда я бы предложил углы ZCW, которые равномерно распределены по всей сфере и быстро рассчитываются. Другими методами являются TheSydneyOperaHouse(SOPHE) и Repulsion. (ищите repulsion.c) Метод отталкивания довольно хорош, но медленен: он итеративно распределяет точки равномерно по сфере. К счастью, это нужно сделать только один раз.
Это используется в кристаллографии и ЯМР, потому что для порошковых структур быстрее использовать равномерное распределение по сравнению со случайным распределением (вам нужно меньше точек).
Вот реализация Python для ZCW.
Подробнее в этих статьях:
Исследования неслучайного численного метода для многомерного интегрирования, Ченг, Вера Б. и Генри Х. Сузукава-младший и Вольфсберг, Макс
Компьютерное моделирование в твердотельном ЯМР. III. Усреднение порошка, Матиас Эден
Если вы не трассируете лучи только тривиальными сценами, будет ли время вашего рендеринга действительно зависеть от времени выборки? Если нет, то, вероятно, это еще не стоит оптимизировать, хотя стоит прочитать и понять методы единообразной выборки, приведенные в других ответах.
Кроме того, ваши выборки не обязательно должны быть очень случайными, чтобы получить хорошую оценку любой функции, которую вы выбираете. Возможно, вы захотите исследовать, используя последовательность квазислучайных чисел, такую как последовательность Хэлтона. Ваша идея о подразделении тетраэдра неплохая. Это должно привести к хорошим хорошо распределенным точкам, которые должны быть лучше, чем однородные псевдослучайные выборки для большинства сцен, хотя в некоторых случаях могут привести к ужасным артефактам.
В любом случае, на самом деле вам следует обратиться к форумам на ompf.org Там есть несколько супер хардкорных ботаников с лучевой трассировкой.
Для сферических сечений сформируйте свой угол равномерно в phi
(полярный угол) и cos(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)
Это частный случай правила, которое гласит: инвертируйте якобиан и генерируйте равномерно в этом пространстве этих координат.
Примечание: обратите внимание, что я использую противоположное соглашение для phi и theta из линии Дэвида Нормана.
Обратите внимание: это на самом деле не самый быстрый метод, а скорее тот, который иллюстрирует общий принцип.
Я не уверен, имеет ли это вообще какой-либо смысл, но здесь вы идете:
Равномерная дробная часть: простой быстрый метод генерации непрерывных случайных величин