- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Написание скриптов для Blender 2.49 - Michel Anders
Шрифт:
Интервал:
Закладка:
scn = Scene.GetCurrent()
context = scn.getRenderingContext()
current_frame = context.currentFrame() #Текущий кадр
start_frame = context.startFrame() #Начальный кадр
end_frame = context.endFrame() #Конечный кадр
frames_per_second = context.fps #Частота
#кадров, fps
Теперь, с этой информацией, мы можем вычислить время, или абсолютное, или относительно стартового кадра:
absolute_time = current_frame/float(frames_per_second)
relative_time = (current_frame-start_frame)/
float(frames_per_second)
Заметьте преобразование во float (число с плавающей точкой) в знаменателе (выделено). Этим способом мы гарантируем, чтобы деление рассматривалось как операция с плавающей точкой. Не строго необходимо, поскольку fps возвращается с типом плавающей точки, но множество людей считают частоту кадров как некоторую целую величину, например, 25 или 30. Тем не менее, так бывает не всегда (например, кодировка NTSC использует дробную частоту кадров), так что мы лучше сделаем это явно. Также заметьте, что мы не можем покончить с этим делением, в противном случае, когда люди захотят изменить своё решение о выбранной частоте кадров, скорость анимации должна измениться.
Всё, что выглядит хорошо — это хорошоТочно имитировать то, как выглядят пульсации, вызванные падением капелек, может показаться трудным, но это просто, хотя и немного запутано. Читатели, интересующиеся базовой математикой, могут проверить какие-нибудь ссылки (например, http://en.wikipedia.org/wiki/Wave). Нашей целью, тем не менее, не является моделирование реального мира с максимально возможной точностью, а обеспечение художника текстурой, которая выглядит хорошо и управляется так, чтобы текстуру можно было применить даже в нереалистичных ситуациях.
Так, вместо определения скорости, с которой двигается волна в зависимости от чего-нибудь, например, вязкости воды, мы делаем скорость в виде регулируемого входа в наш Pynode. То же самое для высоты и ширины волн, и показателя, с которым высота волн уменьшается по мере расширения. В основном, мы аппроксимируем наш небольшой пакет пульсаций, его расхождение наружу из точки падения капельки, функцией косинуса, умноженной на экспоненциальную функцию и показатель торможения. Это снова может показаться опасным погружением в математику, но может легко быть визуализировано:
Для того, чтобы вычислить высоту в любой позиции x, y на нашей текстуре, вышеуказанное можно осуществить следующим образом:
position_of_maximum=speed*time
damping = 1.0/(1.0+dampf*position_of_maximum)
distance = sqrt((x-dropx)**2+(y-dropy)**2)
height = damping*a*exp(-(distance-
position_of_maximum)**2/c)*
cos(freq*(distance-position_of_maximum))
Здесь, dropx и dropy - позиция ударившей капли, a - наш регулируемый параметр высоты.
Эффекты от многих брошенных капель в разное время и в разных позициях можно просто вычислить суммированием результирующих высот.
Хранение дорогостоящих результатов для многократного использованияЕдинственная капля - это, конечно, не дождь, так что мы хотели бы видеть сложенные эффекты от множества случайных капель. Следовательно, мы должны выбирать произвольные позиции и время ударов для стольких капелек, сколько мы хотели бы сымитировать.
Мы должны были бы делать это каждый раз при вызове метода __call__() (то есть, для каждого видимого пикселя в нашей текстуре). Тем не менее, это было бы огромными тратами процессорных сил, поскольку вычисление множества случайных чисел и получение и возврат памяти для, возможно, многих капель дорого.
К счастью, мы можем сохранить эти результаты в качестве экземпляров переменных нашего Pynode. Конечно, мы должны быть достаточно осторожными, чтобы проверять, что никакие входные параметры не были изменены между вызовами __call__ () и предпринять соответствующие меры, если они изменились. Общая картина будет выглядеть следующим образом:
class MyNode(Node.Scripted):
def __init__(self, sockets):
sockets.input = [Node.Socket('InputParam',
val = 1.0)]
sockets.output = [Node.Socket('OutputVal' ,
val = 1.0)]
self.InputParam = None
self.Result = None
def __call__(self):
if self.InputParam == None or
self.InputParam != self.input.InputParam :
self.InputParam = self.input.InputParam
self.Result = интенсивные_вычисления ...
self.output.OutputVal = другие_вычисления …
Этот образец работает, только если входной параметр изменяется редко, например, только если его изменяет пользователь. Если вход изменяется с каждым пикселем, поскольку входной сокет подключен к выходу другого нода - схема с запоминанием, наоборот, будет дороже по времени вместо какой-либо экономии.
Вычисление нормалейНашей целью будет сгенерировать волновой узор, который можно использовать в качестве нормали. Так что нам нужен некий способ получить нормаль из рассчитанных высот. Блендер не предоставляет нам такого преобразующего нода для материалов, так что мы должны разработать схему самостоятельно.
В противовес нодам материалов, ноды текстур Блендера обеспечивают преобразующую функцию, называемую 'Value to Normal' (величина в нормаль), которая доступна в нодовом редакторе текстур из меню Add|Convertor|Value to Normal.
Теперь, как и в случае ряби, мы могли бы, в принципе, вычислить также точную нормаль для нашей капли дождя, но, вместо движения по математическому пути, мы снова применяем метод, используемый многими встроенными текстурами шума для вычисления нормалей, который работает независимо от основной функции.
Пока мы можем оценивать функцию в трех точках: f(x,y),f(x+nabla,y), и f(x,y+nabla), мы можем оценить направление нормали в x,y, изучая наклон нашей функции в направлениях x и y. Нормаль поверхности будет вектором, перпендикулярным к плоскости, определенной этими двумя наклонами. Мы можем взять любую малую величину для nabla, чтобы попробовать с ней, и если это не будет выглядеть хорошо, мы можем её уменьшить.
Собираем всё это вместеВзяв все эти идеи из предыдущих параграфов, мы можем приготовить следующую программу для нашего Pynode Raindrops (с опущенными операторами import):
class Raindrops(Node.Scripted):
def __init__(self, sockets):
sockets.input = [
Node.Socket('Drops_per_second' ,
val = 5.0, min = 0.01, max = 100.0),
Node.Socket('a',val=5.0,min=0.01,max=100.0),
Node.Socket('c',val=0.04,min=0.001,max=10.0),
Node.Socket('speed',val=1.0,min=0.001, max=10.0),
Node.Socket('freq',val=25.0,min=0.1, max=100.0),
Node.Socket('dampf',val=1.0,min=0.01, max=100.0),
Node.Socket('Coords', val = 3*[1.0])]
sockets.output = [
Node.Socket('Height', val = 1.0),
Node.Socket('Normal', val = 3 *[0.0])]
self.drops_per_second = None
self.ndrops = None
Код инициализации определяет множество входных сокетов помимо координатного. Drops_per_second (капель в секунду) должен быть самочитаемым. a и c - общая высота и ширина пульсаций, двигающихся наружу из точки удара. speed и freq определяют, как быстро наши пульсации двигаются и насколько близко волны друг к другу. То, как быстро высота волн уменьшается во время пути наружу, определяет dampf.
Мы также определяем два выходных сокета: Height будет содержать рассчитанную высоту и Normal будет содержать соответствующую нормаль в этой же точке. Normal - это то, что Вы должны обычно использовать для получения поверхностного эффекта распространения, но рассчитанная высота может быть полезной, например, чтобы смягчить величину отражательной способности поверхности.
Инициализация заканчивается с определением некоторых переменных экземпляра, которые будут использованы, чтобы определить, нужно ли нам вычислять позицию падения капли заново, как мы увидим в определении функции __call__().
Определение функции __call__() начинается с инициализации множества локальных переменных. Одно примечательное место - то, где мы установили произвольное семя, используемое функциями модуля Noise (выделено в следующем коде). Таким образом, мы убеждаемся, что всякий раз, когда мы пересчитываем точки удара, мы получаем повторяемые результаты, что если мы установили бы количество капель в секунду сначала на десять, а позже на двадцать, и, затем ввернулись к десяти, сгенерированный узор будет тем же. Если Вы хотели бы изменить это, Вы могли бы добавить дополнительный входной сокет, который нужно использовать как вход для функции setRandomSeed():

