Maya Python Terrain Generator - PullRequest
       31

Maya Python Terrain Generator

0 голосов
/ 05 декабря 2018

Для школьного проекта я пытаюсь кодировать генератор местности.Я полный нуб в Python, но много изучал о создании ландшафта.Это мой код на данный момент:

#imports
import maya.cmds as cmds
import random
#variables
landsub = 100 #number of subdivisions on the plane
landsize = 400 #size of the plane
maxheight = 100
land = cmds.polyPlane( sx=landsub, sy=landsub, w=landsize, h=landsize)
vtxCount = cmds.polyEvaluate(v=True)
print land
values = [random.triangular(0,1,0) for i in xrange(10)]

#code
for x in range(vtxCount):
    cmds.select(cl=True)
    cmds.select(land[0] + '.vtx[' + str(x) + ']')
    cmds.move(values[x] * maxheight, y=True, absolute=True) 

cmds.select(land[0])
#cmds.polySmooth(dv = 1)

Я пытаюсь (и изо всех сил) сделать переменные maxheight и vtxCount взаимозависимыми;количество вершин на максимальной высоте должно быть наименьшим, увеличиваясь до тех пор, пока большинство вершин не окажутся на минимальной высоте.В настоящее время мой код показывает, что большинство вершин находятся на средней высоте, а редко - на минимальной высоте.Я долго искал решение, но я не знаком с тем, что я могу сделать в Python, поэтому изо всех сил пытаюсь найти правильные команды для своего кода.

Я действительно надеюсь, что кто-то может помочьи направь меня в правильном направлении!Спасибо за чтение

Ответы [ 2 ]

0 голосов
/ 08 декабря 2018

Создание ландшафта - забавная проблема, к сожалению, для получения удовлетворительного результата часто требуется много усилий.

Часть проблемы '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) на другую формулу?* что вы могли бы сделать с растровым изображением для имитации уровня воды?

0 голосов
/ 05 декабря 2018

вы могли бы сделать что-то вроде этого

#imports
import maya.cmds as cmds
import random
#variables
landsub = 100 #number of subdivisions on the plane
landsize = 400 #size of the plane
maxheight = 100
land = cmds.polyPlane( sx=landsub, sy=landsub, w=landsize, h=landsize)
vtxCount = range(cmds.polyEvaluate(v=True))
random.shuffle(vtxCount)
values = [random.triangular(0,1,0) for i in xrange(10)]
values_count = len(values)
SEED = 448
random.seed(SEED)
optimize_setter = []

#code
for x in vtxCount:
    mod = x % values_count
    # cmds.move(values[mod-1] * maxheight, land[0] + '.vtx[' + str(x) + ']',y=True, absolute=True)
    optimize_setter += [float(0),values[mod-1]*maxheight,float(0)]
cmds.setAttr('pPlane1.vtx[:]', *optimize_setter)
cmds.select(land[0])

Если вы хотите, чтобы больше значений было около 0, вы можете изменить значения, чтобы иметь больше значений .1.Я не уверен, в чем ваша проблема.

values = []
high_values = [random.triangular(0,1,0) for i in xrange(3)]
low_values = [random.uniform(0, .2), for i in xrange(7)]
values = low_values + high_values

РЕДАКТИРОВАТЬ

#imports
import maya.cmds as cmds
import random
#variables
landsub = 100 #number of subdivisions on the plane
landsize = 400 #size of the plane
maxheight = 15
land = cmds.polyPlane( sx=landsub, sy=landsub, w=landsize, h=landsize)
vtxnb = cmds.polyEvaluate(v=True)
vtxCount = range(vtxnb)

values = []
high_values = [random.triangular(0,1,0) for i in xrange(vtxnb/10)]
low_values = [random.uniform(0, .3) for i in xrange(vtxnb/2)]
values = low_values + high_values
values_count = len(values)
SEED = 448
random.seed(SEED)
random.shuffle(vtxCount)
random.shuffle(values)
optimize_setter = []

#code
for x in vtxCount:
    mod = x % values_count
    # cmds.move(values[mod-1] * maxheight, land[0] + '.vtx[' + str(x) + ']',y=True, absolute=True)
    optimize_setter += [float(0),values[mod-1]*maxheight,float(0)]
cmds.setAttr('pPlane1.vtx[:]', *optimize_setter)
cmds.select(land[0])
...