Чтение онлайн

ЖАНРЫ

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

Anders Michel

Шрифт:
Индексирование текстуры вектором

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

Мы не можем просто подключить цветную текстуру к цветовым входам, так как это приведёт, может быть, к интересной модели, но каждая плитка не будет иметь однородной окраски. Решением будет модифицировать наш Pynode, чтобы производить уникальный вектор, который станет однородным в пределах любой данной плитки. Этот вектор затем может быть подключен к любой шумовой текстуре, которая принимает вектор на входе, так же, как делают все текстуры Блендера. Этот вектор используется нодом текстуры шума, чтобы указывать на единственную точку в произвольной текстуре, и таким способом мы можем произвести произвольно окрашенные, но однородные элементы.

Чтобы обеспечить такую функциональность, мы модифицируем наш код, удалив цветовые входы, и заменяя цветовой выход векторным выходом (не показано). Код в функции __call__ теперь должен будет производить вектор вместо цвета. Здесь мы покажем модифицированную функцию triangle (полный код доступен как tilingsv.py в файле tilingsv.blend):

def triangle(self,x,y):

y *= self.stretch

x,y = self.cos45*x - self.sin45*y,

self.sin45*x + self.cos45*y

if int(floor(x%2)) ^int(floor(y%2)) ^ \

int(y%2>x%2) :

return [floor(x),floor(y),0.0]

return [floor(x)+0.5,floor(y),0.0]

Логика в основном та же, но, как показано на выделенной строке, мы возвращаем вектор, который зависит от позиции. Тем не менее, из-за операции floor, он постоянен в пределах треугольника. Заметьте, что для альтернативного треугольника мы добавляем незначительное смещение; не имеет значения какое именно смещение мы выберем до тех пор, пока оно постоянно и производит вектор, отличающийся от других треугольников.

Результаты показывают произвольный узор из треугольников, которые следуют за большими корреляциями шума оставляя каждый индивидуальный треугольник с однородным цветом. Образец справа имеет больший размер шума в использованной текстуре cloud:

Возможная настройка нодов показана на следующем скриншоте:

Свежий бриз - текстуры с нормалями

Текстура может иметь больше, чем просто геометрический вход. Если Вам нужна текстура, изменяющая свое поведение в зависимости от другой текстуры, этого не получится достигнуть простой настройкой нодов, которую Вы можете обеспечить дополнительными входными сокетами. Мы разработаем Pynode, генерирующий карту нормалей, которая имитирует небольшие пятна всплесков (wavelets) в пруду во время почти безветренного дня.

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

Длина волн ряби управляется еще одним входом с названием wavelength, и наш нод Ripples (Пульсации) будет также иметь входной сокет для координат.

Четвертый и последний вход, называемый direction– вектор, который контролирует ориентацию наших элементарных волн. Может быть установлено вручную пользователем, но при желании, может быть соединён с нодом normal, который предоставляет простой способ манипулировать направлением с помощью мыши.

Окончательная настройка нодов, которая объединяет все это, показана на скриншоте редактора нодов:

Скрипт для нода прост; после нескольких необходимых операций импорта мы определим многочисленные входные сокеты и наш единственный выходной сокет.

from Blender import Node

from math import cos

from Blender.Mathutils import Vector as vec

class Ripples(Node.Scripted):

def __init__(self, sockets):

sockets.input = [

Node.Socket('amplitude' , val= 1.0,

min = 0.001, max = 1.0),

Node.Socket('wavelength', val= 1.0,

min = 0.01, max = 1000.0),

Node.Socket('direction' , val= [1.0,0.0,0.0]),

Node.Socket('Coords' , val= 3*[1.0])]

sockets.output = [Node.Socket('Normal',

val = [0.0,0.0,1.0])]

def __call__(self):

norm = vec(0.0,0.0,1.0)

p = vec(self.input.Coords)

d = vec(self.input.direction)

x = p.dot(d)*self.input.wavelength

norm.x=-self.input.amplitude*cos(x)

n = norm.normalize

self.output.Normal = n*.01

__node__ = Ripples

Снова, вся реальная работа выполняется в функции __call__ (выделено в предыдущем куске коде). Мы сначала определяем сокращения p и d для векторов координат и направления соответственно. Наши элементарные волны - функции синуса и позиция на этой синусоиде определяется проекцией позиции на вектора направления. Эта проекция вычисляется скалярным произведением - операция предоставлена методом dot объекта Vector.

Затем, проекция умножается на длину волны. Если бы мы вычислили синус, у нас была бы высота нашей волны. Но нас, тем не менее, интересует не высота, а нормаль. Нормаль всегда направлена вверх и перемещается вместе с нашей сунусоидальной волной (смотри следующую диаграмму). Можно показать, что эта нормаль - вектор с z-компонентой 1.0 и x-компонентой, равной отрицательной производной функции синуса, то есть, минус косинус. Скрипт (ripples.py) и пример настройки нодов доступны как файл ripples.blend.

Поделиться с друзьями: