Книга: Написание скриптов для Blender 2.49

Регулярное заполнение

Регулярное заполнение

Текстура шахматной доски является, возможно, самой простой текстурой, которую Вы можете себе представить и, следовательно, часто используется в качестве примера при программировании текстур. Поскольку Блендер уже имеет встроенную клетчатую текстуру (начиная с версии 2.49, в текстурном контексте окна нодов), мы хотим пройти на один шаг дальше и создать текстурный нод, который отображает не только текстуру шахматной доски, но может заполнять (tilings) также треугольниками и шестиугольниками.

from Blender import Node,Noise,Scene
from math import sqrt,sin,cos,pi,exp,floor
from Blender.Mathutils import Vector as vec
# создаёт регулярное заполнение для использования в
качестве цветовой карты
class Tilings(Node.Scripted):
   def __init__(self, sockets):
      sockets.input = [Node.Socket('type' ,
                       val= 2.0, min = 1.0, max = 3.0),
                       Node.Socket('scale' ,
                       val= 2.0, min = 0.1, max = 10.0),
                       Node.Socket('color1',
                          val= [1.0,0.0,0.0,1.0]),
                       Node.Socket('color2',
                          val= [0.0,1.0,0.0,1.0]),
                       Node.Socket('color3',
                          val= [0.0,0.0,1.0,1.0]),
                       Node.Socket('Coords',
                          val= 3*[1.0])]
      sockets.output = [Node.Socket('Color',
                                    val = 4*[1.0])]

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

Мы также определяем вход Coords. Этот входной сокет может перехватывать любой выход сокета геометрии. Таким образом у нас есть множество возможностей отобразить нашу цветную текстуру на объект, который мы текстурируем. Сокет Scale определяется также, чтобы управлять размером нашей текстуры.

Наконец, мы определяем сокет Type, чтобы выбирать узор, который мы хотим генерировать. Так как API для Pynode не обеспечивает выпадающих меню или любого другого простого управляющего элемента для выбора, мы делаем сокет с одиночным значением и произвольно выбираем величины, представляющие наш выбор: 1.0 для треугольников, 2.0 для шахматного поля, и 3.0 для шестиугольников.

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

      self.cos45 = cos(pi/4)
      self.sin45 = sin(pi/4)
      self.stretch = 1/sqrt(3.0)
      self.cmap = { (0,0):None,(0,1):2,   (0,2):0,
                    (1,0):0,   (1,1):1,   (1,2):None,
                    (2,0):2,   (2,1):None,(2,2):1 }

Следующим шагом будет определение функции __call__():

   def __call__(self):
      tex_coord = self.input.Coords
      # мы игнорируем любую z-координату
      x = tex_coord[0]*self.input.scale
      y = tex_coord[1]*self.input.scale
      c1 = self.input.color1
      c2 = self.input.color2
      c3 = self.input.color3
      col= c1

Функция __call__() начинается с определения нескольких сокращений для входных величин и умножения координатного входа на выбранный масштаб, чтобы растянуть или уменьшить сгенерированный узор. Следующий шаг должен установить тип желательного узора и вызвать подходящую функцию для вычисления выходного цвета для данных координат. Результирующий цвет назначается в наш единственный выходной сокет:

      if self.input.type<= 1.0:
         col = self.triangle(x,y,c1,c2)
      elif self.input.type <= 2.0:
         col = self.checker(x,y,c1,c2)
      else:
         col = self.hexagon(x,y,c1,c2,c3)
      self.output.Color = col

Все различные функции генерации узоров очень похожи; они берут координаты x и y и два или три цвета в качестве аргументов и возвращают единственный цвет. Так как это функции-члены класса, они также принимают дополнительный первый аргумент self.

   def checker(self,x,y,c1,c2):
      if int(floor(x%2)) ?nt(floor(y%2)):
         return c1
      return c2

Функция checker проверяет, в какой строке и колонке мы находимся, и если номер строки и номер колонки - оба нечетные или четные (что устанавливает оператор исключающее или), она возвращает один цвет, если нет, то возвращает другой цвет.

   def triangle(self,x,y,c1,c2):
      y *= self.stretch
      x,y = self.cos45*x - self.sin45*y,
            self.sin45*x + self.cos45*y
      if int(floor(x%2)) ?nt(floor(y%2)) ^
         int(y%2>x%2) : return c1
      return c2

Функция triangle сначала одновременно вращает как x, так и y координаты на угол 45 градусов (превращение квадратов в вертикальные ромбы). Затем она определяет цвет, основываясь на номерах строки и колонки в точности подобно функции checker, но с уловкой: третье условие (выделено) проверяет, слева ли мы от диагонали, пересекающей квадрат, и поскольку мы вращали нашу сетку, на самом деле мы проверяем действительно ли координаты выше горизонтальной линии, делящей наш ромб. Это может звучать немного сложным, но Вы можете посмотреть на следующую иллюстрацию, чтобы понять идею:


   def hexagon(self,x,y,c1,c2,c3):
      y *= self.stretch
      x,y = self.cos45*x - self.sin45*y,
            self.sin45*x + self.cos45*y
      xf = int(floor(x%3))
      yf = int(floor(y%3))
      top = int((y%1)>(x%1))
      c = self.cmap[(xf,yf)]
      if c == None:
         if top :
            c = self.cmap[(xf,(yf+1)%3)]
         else :
            c = self.cmap[(xf,(yf+2)%3)]
      return (c1,c2,c3)[c]

Функция  hexagon (шестиугольник) во многих отношениях похожа на функцию triangle (в конце концов шестиугольник - шесть треугольников,   склеенных   вместе). Следовательно, в ней применяется та же хитрость с вращением, но, вместо выбора цвета с использованием простой формулы, здесь   всё   несколько   сложнее,   и, следовательно, мы используем цветовую карту (выделено в предыдущем фрагменте кода). В основном, мы делим экран на горизонтальные и вертикальные полосы, и выбираем цвет, основываясь на том, в какую полосу мы попали.

Последняя часть магии - на последней строке нашего скрипта:

__node__ = Tilings

В текущей реализации Pynodes, Блендеру нужно это присвоение, чтобы идентифицировать класс в качестве нода. Наш нод появится в выпадающем меню скриптовых нодов как Tilings. Полный код доступен как tilings.py в файле tilings.blend вместе с примером нодовой сети. Некоторые возможные узоры показаны на следующем скриншоте:


Соответствующая нодовая сеть показана на следующем скриншоте. Заметьте, что мы не подключили никаких нодов к цветовым входам, но можно создать даже более сложные узоры, если мы это сделаем.


Оглавление книги


Генерация: 2.023. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз