Создание ландшафта - забавная проблема, к сожалению, для получения удовлетворительного результата часто требуется много усилий.
Часть проблемы 'maya' хорошо решена в ответе @ drweeny: вам просто нужно массово установить атрибуты .vtx
для polyPlane.Вот аналогичный способ сделать это, главное отличие состоит в том, что эта функция принимает массив позиций, который работает как растровое изображение: это список, содержимое которого представляет собой списки, представляющие строки и столбцы в карте высот, то есть: [ [0,1], [2,3] ]
представляет
[0, 1]
[2, 3]
в виде сетки.Превратить это в полиплан довольно легко:
def array_to_plane (array):
# this expects that array is an list-of-lists
# arranged as [ [0,1,2], [3,4,5], ... etc ]
h = len(array)
w = len(array[0])
# this makes a plane with the same number of samples as your array
plane, _ = cmds.polyPlane(width = 1, height = 1, sx = w -1 , sy=h-1)
# the zeros are here just so the final argument list is a set of xyz
# positions
setter = []
for row in array:
for column in row:
setter.append (0.0)
setter.append (column)
setter.append (0.0)
cmds.setAttr(plane + '.vtx[:]', *setter, type='double3')
return plane
Интересная и хитрая часть фактически заполняет эту сетку, чтобы создать ландшафт, который не выглядит слишком похожим на случайный шум.Существует множество техник, но основные из них:
Рандомизация
Вы хотите применить случайные числа к вашей карте высоты.К счастью, случайный модуль Python имеет множество отличных опций для шумов с различным распределением.Вот простая функция, которая вставляет случайные числа в сетку карты:
def randomize(map, scale):
rows = len(map)
columns = len(map[0]
# loop over the map and add random values
for r in range(rows):
for c in range(columns):
map[r][c] += (random.gauss(0,1) * scale)
Scales
Сетка случайных чисел не похожа на ландшафт - она слишком хаотична.Очень распространенным методом является применение рандомизаций с различными физическими размерами для имитации сочетания больших и маленьких объектов.Самый простой способ сделать это - начать с маленькой карты, а затем постепенно увеличивать ее - это даст вам набор функций, которые различаются по размеру.
Вот дешевый способ взять сетку карты высот и простоудвоить его содержание в каждом измерении.Если вы сделаете это несколько раз и добавите некоторую рандомизацию сверху, вы получите сочетание функций от очень больших до очень маленьких.
def upsize(heightmap):
output = []
for row in heightmap:
new_row = []
for each_value in row:
# first double the entries in the horizontal direction
new_row.append(each_value)
new_row.append(each_value)
# then add two copies of the row to the output.
# don't add it twice -- add a copy to keep
# the data independent
output.append(new_row)
output.append(new_row[:])
return output
Запуск этого на карте, такой как [ [0,1], [2,3] ]
, приведет к созданию карты, подобной
[ [0,0,1,1],
[0,0,1,1],
[2,2,3,3],
[2,2,3,3]]
Фильтрация
Вы не хотите, чтобы каждый образец в вашей карте высот был независимым от своих соседей: это редко встречается в природе и выглядит довольно плохо.В таких программах, как photoshop, вы генерируете эффект размытия, используя 'kernel' , которое в основном является средневзвешенным значением пикселя и его соседей.Здесь есть много вариантов (это хорошая область для изучения вашего проекта).Вот очень простое ядро размытия, которое берет одну сетку нашей карты и возвращает новое размытое;вы увидите, что после сложения различных весов пикселей мы также используем линейную интерполяцию для управления силой размытия:
def blur(map):
output = []
for row in map:
output.append(row[:])
width = len(map[0])
height = len(map)
for row in range (1, height-1):
for column in range (1, height-1):
v = 0
v += map [row-1][column - 1]
v += map [row-1][column] * 2
v += map [row-1][column + 1]
v += map [row][column - 1] * 2
v += map [row][column] * 4
v += map [row][column + 1] * 2
v += map [row+1][column - 1]
v += map [row+1][column] * 2
v += map [row+1][column + 1]
v = v / 16.0
# lerp the final output based on amount; 0 = no blur, 1 = full blur)
output[row][column] = v
return output
Для этого шага существует множество параметров, которые влияют на то, как будет получен ваш конечный результат.выглядит.Вы можете очень легко поэкспериментировать с ядрами фильтра, используя пользовательский фильтр Photoshop , который на самом деле является простым редактором ядра.
Соберите его вместе
С рандомизацией, масштабированием и фильтрацией у вас естьдостаточно, чтобы сделать интересный инструментарий поколения местности.Обычная нить заключается в том, что вы выполняете всю тяжелую работу над «растровым изображением» «пикселей», организованным в виде списка-списков, и когда вы закончите, вы передаете его Maya для создания конечного объекта.
Вот очень простой способ собрать их вместе.Я бы посоветовал вам разобрать это и собрать вместе таким образом, чтобы удовлетворить ваши творческие идеи.Запись фрагментов в виде отдельных функций, которые работают со списками карты высот, позволяет легко добавлять или изменять поведение, не нарушая целиком.
def fractalize(iterations, scale=1.0, seed = None):
# this lets you make it repeatable if you want
if seed:
random.seed(seed)
# make a 2-2 starter map
map = [ [0.0, 0.0], [0.0, 0.0] ]
randomize(map, scale)
for n in range(1, iterations):
map = upsize(map)
map = blur(map)
# this turns down the randomization
# in each successive step... you could
# use many formulas here for different effects
randomize(map, scale / 2**n)
return array_to_plane(map)
fractalize(7, 0.125, seed=1234)
Будьте осторожны с этим числом итераций: каждая итерация в четыре раза количество вершин, поэтому слишком большое значение этого параметра приведет к потере майи.Значение 8 создает плоскость с 255 x 255 вершинами;используйте с осторожностью.
Несколько вещей, которые можно попробовать сделать самостоятельно: * когда и как часто вы хотите добавить размытие?* что произойдет, если вы используете другое ядро размытия?* что произойдет, если вы измените randomize(map, scale / 2**n)
на другую формулу?* что вы могли бы сделать с растровым изображением для имитации уровня воды?