Генерация случайных точек на любой трехмерной поверхности
Мне было интересно, как вы, математически говоря, могли бы генерировать x точек в случайных положениях на трехмерной поверхности, зная количество треугольников, составляющих поверхность (их размеры, положения, нормали и т. Д.)? За сколько шагов вы бы продолжили?
Я пытаюсь создать "рассеиватель" в Maya (с Python и API), но я даже не знаю, с чего начать с точки зрения концепции. Должен ли я сначала создать точки, а затем проверить, принадлежат ли они поверхности? Должен ли я создавать точки непосредственно на поверхности (и как, в этом случае)?
Изменить: я хочу достичь этого без использования 2D-проекции или UV, насколько это возможно.
4 ответа
Вы должны вычислить площадь каждого треугольника и использовать их в качестве весов, чтобы определить пункт назначения каждой случайной точки. Вероятно, проще всего сделать это как пакетную операцию:
def sample_areas(triangles, samples):
# compute and sum triangle areas
totalA = 0.0
areas = []
for t in triangles:
a = t.area()
areas.append(a)
totalA += a
# compute and sort random numbers from [0,1)
rands = sorted([random.random() for x in range(samples)])
# sample based on area
area_limit = 0.0
rand_index = 0
rand_value = rands[rand_index]
for i in range(len(areas)):
area_limit += areas[i]
while rand_value * totalA < area_limit:
# sample randomly over current triangle
triangles[i].add_random_sample()
# advance to next sorted random number
rand_index += 1;
if rand_index >= samples:
return
rand_value = rands[rand_index]
Обратите внимание, что ребристые или морщинистые области могут иметь более высокую плотность точек просто потому, что они имеют большую площадь поверхности в меньшем пространстве.
- Выберите 2 случайных ребра из случайного треугольника.
- Создайте 2 случайные точки по краям.
- Создать новую случайную точку между ними.
Мой уродливый мел сценарий:
//Select poly and target object
{
$sel = `ls -sl -fl`; select $sel[0];
polyTriangulate -ch 0;
$poly_s = `polyListComponentConversion -toFace`;$poly_s = `ls -fl $poly_s`;//poly flat list
int $numPoly[] = `polyEvaluate -fc`;//max random from number of poly
int $Rand = rand($numPoly[0]);//random number
$vtx_s =`polyListComponentConversion -tv $poly_s[$Rand]`;$vtx_s=`ls- fl $vtx_s`;//3 vertex from random poly flat list
undo; //for polyTriangulate
vector $A = `pointPosition $vtx_s[0]`;
vector $B = `pointPosition $vtx_s[1]`;
vector $C = `pointPosition $vtx_s[2]`;
vector $AB = $B-$A; $AB = $AB/mag($AB); //direction vector and normalize
vector $AC = $A-$C; $AC = $AC/mag($AC); //direction vector and normalize
$R_AB = mag($B-$A) - rand(mag($B-$A)); vector $AB = $A + ($R_AB * $AB);//new position
$R_AC = mag($A-$C) - rand(mag($A-$C)); vector $AC = $C + ($R_AC * $AC);//new position
vector $ABC = $AB-$AC; $ABC = $ABC/mag($ABC); //direction vector and normalize
$R_ABC = mag($AB-$AC) - rand(mag($AB-$AC)); //random
vector $ABC = $AC + ($R_ABC * $ABC);
float $newP2[] = {$ABC.x,$ABC.y,$ABC.z};//back to float
move $newP2[0] $newP2[1] $newP2[2] $sel[1];
select -add $sel[1];
}
PS УФ метод лучше
Если ограничением является то, что все выходные точки находятся на поверхности, вам нужен согласованный метод обращения к самой поверхности, а не беспокоиться о 3D-преобразовании поверхности для ваших точек.
Хакетический способ сделать это - создать UV-карту для вашего 3d-объекта, а затем разбросать точки случайным образом в двух измерениях (отбрасывая точки, которые не попали внутрь действительной UV-оболочки). Как только ваши УФ-оболочки заполнены настолько, насколько вы хотите, вы можете преобразовать свои УФ-точки в барицентрические координаты, чтобы преобразовать эти 2-d точки обратно в 3-d точки: фактически вы говорите: "Я 30%, вершина A, 30". % вершины B и 40% вершины C, поэтому моя позиция (.3A + .3B + .4C)
Помимо простоты, еще одним преимуществом использования УФ-карты является то, что она позволяет настраивать плотность и относительную важность различных частей сетки: на большей УФ-поверхности будет много рассеянных точек, а на меньшем - меньше. если это не соответствует физическим размерам или граням.
Переход к 2D приведет к появлению некоторых артефактов, потому что вы, вероятно, не сможете придумать УФ-карту, которая бы не была растянутой и не имела швов, поэтому из-за этого вы получите изменения в плотности рассеяния. Однако для многих приложений это будет хорошо, поскольку алгоритм действительно прост, а результаты легко настраиваются вручную.
Я не использовал этот, но похоже, что он основан на этом общем подходе: http://www.shanemarks.co.za/uncategorized/uv-scatter-script/
Если вам нужен более математически строгий метод, вам понадобится более изящный метод параметризации сетки: способ превратить вашу трехмерную коллекцию треугольников в единое пространство. В этой области много интересной работы, но было бы сложно выбрать конкретный путь, не зная приложения.
Вот псевдокод, который может быть хорошей отправной точкой:
- Пусть N = нет вершин трехмерного лица, с которым вы работаете.
- Просто сгенерируйте N случайных чисел, вычислите их сумму, разделите каждое на сумму. Теперь у вас есть N случайных чисел, сумма которых равна = 1,0.
- Используя приведенные выше случайные числа, возьмите линейную комбинацию трехмерных вершин 3D-грани, которая вас интересует. Это должно дать вам случайную 3D-точку на грани.
- Повторяйте, пока не получите достаточно нет. случайных точек на трехмерном лице.