Редакция LAN осваивает язык VRML в поисках ответа на вопрос: насколько создание виртуальных миров доступно неподготовленному пользователю?


C ЧЕГО НАЧАТЬ?
VRML 2.0: УЗЛЫ
И ВСЕ-ТАКИ ОН ВЕРТИТСЯ!
ЧТО ОСТАЛОСЬ ЗА КАДРОМ
ЗАКЛЮЧЕНИЕ

УЗЕЛОК ЗАВЯЖЕТСЯ...
Объектно-ориентированное программирование в VRML

ЧТО МЫ ВИДИМ В ВИРТУАЛЬНЫХ МИРАХ
Геометрические тела в VRML

РАСПЕЧАТКА 1


Технология создания виртуальных миров в Internet успешно развивается и совершенствуется, но как-то полуподпольно по сравнению с тем же Java. Если воспользоваться терминологией из мира музыки, то язык VRML имеет культовый статус, т.е. имеет устойчивую армию приверженцев, не имеющую ярко выраженной тенденции к изменению численности как в меньшую, так и в большую сторону. Вероятных причин можно назвать несколько, например повышенные требования к производительности компьютеров, на которых работает интерпретатор VRML. Но единственным, по-настоящему фатальным препятствием на пути распространения этой технологии может стать только невозможность ее освоения "с ходу". Сделать более-менее сносную страницу Web способен любой, кто знаком с текстовым редактором и готов потратить часок на изучение основ HTML, создать же что-либо привлекательное в виртуальном пространстве, очевидно, не так просто. Автор решил на себе испытать, насколько же сложно стать одним из Творцов виртуальных миров, где газонокосильщики охотятся на стаи непуганых газонокосилок, и влез в шкуру неискушенного пользователя (впрочем, до написания статьи сколь-либо искушенным в VRML он и не был), написав по итогам своего исследования некоторое подобие путеводителя по миру VRML.

C ЧЕГО НАЧАТЬ?

Поскольку автор, по роду своей деятельности, человек относительно информированный, он знал, что язык описания виртуальной реальности - VRML (Virtual Reality Markup Language) - разработан на основе более сложного и, в общем-то, не предназначенного для использования в Internet языка OpenInventor компании Silicon Graphics, являющейся, конечно же, одним из основных участников форума VRML и часто выступающей от его имени. Поэтому первым шагом автора в поисках информации о языке вместо напрашивающегося обращения к поисковому серверу был визит на узел компании, с которого по соответствующей ссылке он отправился на посвященный VRML сервер http://vrml.sgi.com. По этому адресу можно найти практически всю (по крайней мере на начальном этапе освоения языка) необходимую информацию о VRML. Давайте посмотрим, какие начальные сведения об интересующей технологии новичок получит.

Очевидно, первое, что придется узнать, - это ответ на вопрос, что же такое VRML. Казалось бы, и так все знают - это "то же самое, что HTML, только для описания трехмерных объектов". На самом деле ничего общего с HTML, кроме расшифровки двух последних букв названия и того, что описание объектов производится при помощи ASCII-текста, у VRML нет. HTML, грубо говоря, - некий набор "крючков" (вроде текстового формата RTF) для форматирования текста, и его область применения довольно жестко ограничена технологиями Web. В отличие от него, VRML относится, скорее, к объектно-ориентированным языкам программирования, и, говоря о нем, более уместным будет проводить параллели с Java. Как и Java, VRML платформенно-независим, и область его применения точно так же не ограничивается технологиями Internet/Intranet (для которых он изначально разрабатывался). Загонять объединяющий все платформы универсальный язык описания трехмерных объектов в тесные рамки домашних страниц было бы просто расточительством, поэтому рискну предположить, что VRML суждено стать промышленным стандартом.

Возможно, кто-то уже приуныл - опять язык программирования... пока его выучишь, пока напишешь что-нибудь прилично... это же вечность пройдет! Да, описывать трехмерные объекты вручную - занятие неблагодарное, требующее большого объема рутинной работы: автор еще не забыл, как сам возился с курсовой работой по машинной графике. Для облегчения проектирования виртуальных миров создано и создается множество VRML-конструкторов, позволяющих работать в режиме WYSIWYG и часто предоставляющих пользователю набор готовых к использованию объектов: несложные трехмерные тела, символы, различные предметы. Если поставляемых вместе с редактором объектов не хватает, то можно поискать в довольно обширных, с открытым доступом через Internet библиотеках. И наконец, специальные программы конвертируют в VRML описания трехмерных объектов из форматов таких приложений, как, например, 3D Studio, при помощи которых уже создано столько всего... Доступ к этим ресурсам можно получить через все тот же сервер Silicon Graphics, а также через популярные серверы свободно распространяемого ПО: www.download.com, www.slaughterhouse.com и другие.

Обилие инструментария тем не менее не освобождает от изучения языка, поскольку "доводить до блеска" созданные миры придется, скорее всего, вручную. Во-первых, работая с "мышкой", достичь высокой точности сложно, а во-вторых, принятый недавно стандарт VRML 2.0 позволяет описывать сложное поведение объектов, что требует хотя бы минимального применения традиционного программирования.

VRML 2.0: УЗЛЫ

Стандарт VRML версии 2.0 (ISO/IEC CD 14772) был принят 4 августа 1996 года. Полную спецификацию языка можно найти на сервере Silicon Graphics, но научиться использовать VMRL по ней более чем затруднительно, что, впрочем, справедливо для любых спецификаций любых языков программирования. Учебная литература по VRML, по-видимому, еще не скоро появится в наших книжных магазинах, но, по счастью, для сообщества Internet характерно стремление обладающих каким-либо сокровенным знанием поделиться им с окружающими. Все та же страница Silicon Graphics предлагает на выбор воспользоваться несколькими такими пособиями, излагающими концепции языка в последовательной и доступной форме. Конечно, в них излагается лишь малая толика возможностей VRML, но, по мере их освоения, извлечь недостающую информацию из спецификации языка становится все легче. Итак, наш новичок имеет под рукой необходимый минимум информации и горит желанием получить первый практический результат. Традиционно первой программой на любом языке является код для вывода на экран текста: "Hello, world!". В нашем случае, это будет код, описывающий самый тривиальный объект - куб (см. Рисунок 1.).

Picture 1a Picture 1b

Рисунок 1.
Даже простейший пример языка VRML демонстрирует сложную объектную структуру.

#VRML V2.0 utf8 
Group{ 
children [ 
Shape { 
appearance Appearance { 
material Material { 
diffuseColor 0.8 0.15 0 
} 
} 
geometry Box {} 
} 
]}

Если вы хотите воспроизвести это и последующие изображения на своем ПК (набив приведенный текст вручную или воспользовавшись электронной версией нашего журнала), то для просмотра файлов VRML 2.0 потребуется соответствующий модуль расширения для вашего браузера. Найти необходимый модуль расширения можно там же, где и конструкторы VRML. Сам автор воспользовался программой Cosmo Player, разработанной Silicon Graphics. Как видим, описывается он не двумя-тремя словами, как можно было бы ожидать. Причина - строгий порядок описания объектов, которые в VRML называются узлами (nodes). Такое название выбрано далеко не случайно, поскольку объекты в языке объединяются в древовидную структуру. Каждый узел является родительским по отношению к нижележащим, а они соответственно дочерними по отношению к нему. Эта концепция именуется "instancing", что по-русски можно перевести как "организация иерархической структуры". Поскольку перевод получается довольно длинный, придется, рискуя навлечь на себя гнев пуристов, так и называть ее "инстансингом". Статус дочернего может иметь узел VRML любого типа, но родительским, а тем более корневым узлом может быть только объект одного из типов группирующих узлов. В нашем примере таковым стал узел Group. Объявив его, мы логически объединяем все входящие в него дочерние объекты, которые могут как иметь геометрическое представление, так и нет. В данном случае мы хотим создать группу из одного геометрического объекта. Подобные объекты создаются при помощи узла Shape, который имеет поля geometry и appearance, указывающие на узлы соответствующих типов. В примере поле geometry мы определили как box - параллелепипед, по умолчанию куб, но внутри фигурных скобок мы могли бы задать любую длину граней. В поле appearance мы задали цвет куба и то, как его поверхность отражает цвет. Таким образом, даже самый простой объект представляет собой ветвистое дерево узлов.

По умолчанию интерпретатор VRML поместит наш кубик в начало системы координат виртуального мира, сориентировав по ее осям оси локальной системы координат (ось X вправо по экрану, ось Y - вверх, ось Z - перпендикулярно плоскости экрана, наружу). Следующий, неизбежно возникающий вопрос - как ориентировать и располагать в пространстве создаваемые объекты. Решается он при помощи группирующего узла типа Transformer. От Group он отличается наличием нескольких новых полей вида exposedField: center, rotation, scale, scaleOrientation и translation. Center определяет сдвиг центра вращения тела (или группы тел) относительно начала локальной системы координат. Translation тоже определяет сдвиг, но уже начала локальной СК относительно CK родителя. Rotation определяет поворот локальной системы координат только относительно одной из осей, если же необходимая ориентация объекта требует сложного поворота, то следует добавить два внешних или внутренних трансформера. Scale и scaleOrientation позволяют растянуть или сжать объект, задавая коэффициент преобразования и ориентацию осей, вдоль которых оно происходит, относительно локальной системы координат. Таким образом, чтобы, например, поставить наш кубик на ребро, надо

"Group {" заменить на 
Transformer { 
rotation 1 0 0 0.785

Цифры 1 0 0 после слова rotation задают ось, вокруг которой производится поворот, в данном случае это орт оси X (0 1 0 соответствует Y, 0 0 1 - Z), а 0,785 - это PI/4 или 45 градусов. По замыслам автора, куб должен был быть сориентирован так, чтобы его главная диагональ располагалась параллельно оси Y абсолютной системы координат (скоро будет ясно зачем), для чего требовалось совершить второй поворот вокруг оси абсолютной системы координат на легко вычисляемый угол. Тут-то и начались первые серьезные трудности, поскольку из-за использования полем rotation локальной системы координат пришлось полученный угол поворота пересчитывать в углы поворотов вокруг двух ее осей. Из недр книжного шкафа извлекли книгу Ильина и Позняка "Аналитическая геометрия", и полдня было потрачено на реанимацию знаний о преобразовании систем координат, углах Эйлера, и прочем подобном, казалось бы, навсегда оставленном за порогом института. Впрочем, никаких зубодробительных формул в книге не содержится, и нужный при работе с трехмерной графикой минимум знаний вполне по силам освоить любому технически грамотному человеку. Необходимость этих знаний проявляется прежде всего при анимации созданных объектов.

И ВСЕ-ТАКИ ОН ВЕРТИТСЯ!

Главное отличие стандарта 2.0 от первой версии VRML - возможность задать объектам сколь угодно сложное поведение. Достигается это при помощи двух типов узлов: интерполяторов и сенсоров. Изменение параметров объектов происходит при получении им соответствующего сообщения. Вне зависимости от того, от какого узла он его получает, начало цепочке событий дает какой-нибудь сенсор. Существуют следующие типы сенсоров: CylinderSensor, PlaneSensor, ProximitySensor, SphereSensor, TimeSensor, Touch-Sensor, VisibilitySensor. Большая часть этих узлов реагирует на воздействие на объект при помощи указывающего устройства, в частности мыши. TouchSensor реагирует на "прикосновение", т.е. щелчок кнопки. CylinderSensor, PlaneSensor и SphereSensor срабатывают при попытке изменения положения объекта и носят обобщающее название Drag Sensor. CylinderSensor срабатывает при попытке повернуть объект вокруг оси; направление оси фиксируется заранее. Такой сенсор применим для моделирования колеса из "Поля чудес" - колесо будет крутиться только при поперечном движении. В отличие от CylinderSensor, SphereSensor реагирует на попытку повернуть объект в любом направлении. Как уже понятно, PlaneSensor активируется при попытке линейного перемещения объекта. Узлы ProximitySensor и VisibilitySensor генерируют сообщения о том, что виртуальная камера приблизилась к объекту на заданное расстояние или объект попал в ее поле зрения. TimeSensor представляет собой часовой механизм, отсчитывающий время до генерации события. С одним узлом может быть ассоциировано несколько сенсоров, сообщения которых обрабатываются по очереди, поэтому создателю трехмерных миров необходимо отслеживать возможность возникновения противоречивых ситуаций.

Однако не эти, вызывающие аналогии со взрывными механизмами, узлы непосредственно заставляют объект ожить. В роли детонаторов или, если отвлечься от нездоровой подрывной темы, кукловодов выступают интерполяторы. Такое название для этого класса узлов выбрано потому, что автор задает любое движение или изменение внешнего вида объекта в виде нескольких промежуточных стадий, а из дискретного плавным преобразование объекта становится при помощи интерполяции дополнительных промежуточных положений. Или автор может задать некоторое преобразование для отдельных участков объекта, а интерполятор проведет адекватное преобразование для объекта в целом. Как же можно измываться над бедным объектом? Так, что его родная мать не узнает! При помощи узла Position-Interpolator его можно переместить, а OrientatonInterpolator его повернет, но это еще цветочки. При помощи ScalarInterpolator можно как угодно увеличить или уменьшить любой его выражаемый численно параметр: радиус, длину ребра - все, что хотите. Объект можно перекрасить, как угнанную машину, при помощи ColorInterpolator. Если это многогранник, то CoordinateInterpolator перекособочит его до неузнаваемости, а если объект сферического происхождения, то NormalInterpolator превратит его хоть в мяч для регби, хоть в куриное яйцо. Как видим, возможности предоставляются самые широкие, но, чтобы ими воспользоваться, надо организовать передачу сообщений между объектами. Делается это при помощи директивы ROUTE - в конце файла помещаются строки следующего вида:

ROUTE Node1_Name.param1_changed TO Node2_Name.set_param2 , 

имена объектов должны быть заданы при помощи директивы DEF.

Пример простейшей анимации приведен на Рисунок 2 (текст программы приведен на Распечатке 1).

Picture 2a Picture 2b

Рисунок 2.
Обратите внимание, что, хотя концепция объектно-ориентированного программирования реализована в VRML в ограниченном варианте, порой она доведена до абсолюта. Узлы типа Transformer и TimeSensor в нашем примере функционально и логически являются операторами, но описываются они как объекты!

Теперь понятно, зачем автор ломал голову над углами и осями - вращение куба вокруг главной диагонали выглядит намного эффектней, чем вращение вокруг оси симметрии. Разберемся подробней в устройстве механизма вращения, начав с его "моторчика" - интерполятора. У узла Rotation-Interpolator, как и у других интерполяторов, есть два поля: key и keyValue, представляющих собой индекс массива возможных значений изменяемого (интерполируемого) параметра и собственно массив. В данном случае этот параметр - значение поля rotation для Transformer, поэтому он задается в виде ортов вращения и угла поворота; в приведенном случае поворот происходит вокруг оси Y. Использование интерполяции позволяет задать всего три положения объекта - начальное, конечное и промежуточное. Этого вполне достаточно, чтобы задать полный оборот вокруг своей оси и его направление. Добиться непрерывного вращения нам дает возможность TimeSensor, который будет циклически запускать интерполятор. (В принципе, параметры таймера и интерполятора можно варьировать так, чтобы объект поворачивался на любой заданный угол с требуемой скоростью, не обязательно циклически.) Проводками, связывающими все три узла игрушки, являются две директивы ROUTE. Первая посылает интерполятору импульс таймера, предписывающий сменить текущее значение key. Вторая передает от интерполятора трансформеру директиву сменить значение поля rotation на соответствующее keyValue.

В принципе, этот пример позволяет получить достаточно ясное представление о языке; единственнное, чего он не объясняет, - как привязать к объекту ссылку на какой-нибудь URL. Для этого надо поместить объект внутрь узла типа Anchor:

Anchor { 
children 
какой-то объект 
url [ "адрес URL" ] 
description "Описание ссылки" 
} 

Собственно, так я и поступил в своем третьем примере (Рисунок 3), текст которого я не привожу из-за его размера. Щелкнув мышкой на вращающееся лого нашего издательского дома, вы попадете на сервер: www.osp.ru (конический постамент находится вне узла Anchor).

Picture 3

Рисунок 3.
Когда-нибудь мы вынесем это на первую страницу сервера... Вполне законченное (и осмысленное) произведение VRML. Объем файла - всего около 2,8 Кбайт.

ЧТО ОСТАЛОСЬ ЗА КАДРОМ

Сконцентрировавшись на основах языка, мы обошли вниманием несколько принципиально важных при профессиональном использовании возможностей языка.

Звук. Картину можно озвучить при помощи узла AddSound, позволяющего ассоциировать с объектами аудиоклипы. Продвинутые браузеры используют стереоэффекты и изменяют громкость звучания в зависимости от того, как объект и виртуальная камера расположены по отношению друг к другу.

Свет. Несколько узлов позволяют задавать тип освещения в виртуальном мире, располагая в нем источники света - направленного или рассеянного, точечные или фронтальные. Интенсивность, степень рассеяния и цвет исходящего от них света можно варьировать. При желании в созданный мир можно напустить тумана, затеняющего удаленные объекты и рассеивающегося по мере приближения к ним.

Выполнение сценариев. Интерполяторы и сенсоры предоставляют широкие возможности по анимации объектов, но сделать поведение более сложным, чем у заводных игрушек, можно только при помощи сценариев, производящих сложные вычисления и интеллектуальную обработку событий. VRML позволяет применять Java/JavaScript и свой собственный язык VRMLScript. Интерфейс взаимодействия со сценариями VRML 2.0 также динамически меняет структуру маршрутизации сообщений.

ЗАКЛЮЧЕНИЕ

На поиск информации и освоение общих концепций языка автор потратил около недели, занимаясь попутно другими делами. Этот факт говорит о том, что упоминаемое в начале статьи фатальное препятствие на пути распространения VRML не стоит, и ему несомненно предстоит перерасти культовый статус. Кстати, я вовсе не намерен забросить VRML после написания статьи. Поскольку я намерен в ближайшем будущем обзавестись собственной страницей в Internet, то предполагаю продолжить изучение VRML, с целью применить его на практике. Со сложностями вычислительно-геометрического плана я как-нибудь справлюсь, а язык вполне поддается изучению между делом. Надеюсь, что, прочитав статью, вы тоже попробуете себя в создании виртуальных миров.


С Александром Авдуевским можно связаться через Internet по адресу: shura@osp.ru.

УЗЕЛОК ЗАВЯЖЕТСЯ...

Объектно-ориентированное программирование в VRML

Объект в языке VRML называется узлом (node); определяют узел следующие характеристики.

  • Тип узла: Group, Shape, Box и т. д.
  • Поля. Узел может иметь любое количество полей, содержащих параметры, отличающие его от других узлов того же типа. Каждое поле имеет имя, тип и значение по умолчанию. Поля содержат в себе данные различных типов, в том числе и ссылки на другие узлы. Поля бывают двух видов: field и exposedField. Первые не могут изменять своего значения, заданного при инициализации объекта, вторые могут быть изменены как "изнутри", так и другими узлами. В какой-то степени это некоторый аналог понятиям C++ private и public.
  • Набор ассоциированых сообщений (событий), которые узел может принимать и отправлять. Узел получает сообщения типа eventIn, которые обычно влекут за собой изменения состояния узла и обозначаются set_хххх (set_color, set_position). Отправляемые узлом сообщения обычно содержат информацию об изменении его состояния (color_changed, position_changed) и имеют тип eventOut. Объявление в узле поля типа exposedField равноценно ассоциированию с объектом двух событий. Т.е., строчка
exposedField SomeParameter 

эквиалентна следующему:

eventIn set_ SomeParameter 
field SomeParameter 
eventOut SomeParameter _changed. 

Инициализируется объект строчкой:

тип_объекта { поля }. 

Наличие значений по умолчанию позволяет не задавать значения отдельных полей и, при желании, оставить скобки пустыми. При необходимости объекту можно присвоить идентификатор при помощи директивы DEF:

DEF имя_объекта тип_объекта { поля }




ЧТО МЫ ВИДИМ В ВИРТУАЛЬНЫХ МИРАХ

Геометрические тела в VRML

Поле geometry узла Shape помимо Box может принимать целый ряд значений. Во-первых, это Cone, Sphere и Cylinder - конус, сферу и цилиндр, соответственно. Во-вторых, Extrusion - объект, являющийся произвольным телом вращения, и IndexedFaceSet - произвольный многогранник, буквально - набор многоугольников. В-третьих, объекты, так сказать, низкого уровня: прямоугольная решетка ElevationGrid, IndexedLineSet - 3-мерная структура из ломаных линий, и PointSet - набор точек в 3-мерном пространстве. Завершает список стоящий особняком узел типа Text, поскольку он описывает текст, написанный на воображаемой плоскости, и не является полноценным трехмерным объектом, так как сам двумерен (хотя плоскость можно сориентировать как угодно).

Подробно описывать узел типа Appearance вряд ли стоит, поскольку он ссылается на богатые различными параметрами узлы трех типов. Мы ограничимся обобщенным описанием вариаций внешнего вида геометрических тел. Поверхность заданного в поле geometry объекта можно украсить текстурой: растровым изображением (GIF, JPEG, PNG), видеоклипом (MPEG-1) или логическим набором пикселов. При желании текстуру можно растянуть/сжать или повернуть относительно поверхности тела. Поля узла Material позволяют задать цвет поверхности тела и ее способность поглощать, отражать и пропускать свет, что даст возможность имитировать различные эффекты - от матовой шероховатости бетона до бликов на стекле или боку яблока.


РАСПЕЧАТКА 1

#VRML V2.0 utf8

WorldInfo {
 info ""
}
DEF Cube_Transform Transform {
 children       [
  Group {
   children     [
        DEF Spin_Timer TimeSensor {
          cycleInterval 2
          loop  TRUE
          startTime     1
        }
        DEF Spin_Interpolator OrientationInterpolator {
         key    [ 0, 0.5, 1.0 ]
         keyValue       [       0 1 0 0,
                                0 1 0 3.14159,
                                0 1 0 6.2831
                        ]
        }
   ]
  }
 Transform {
   center 0 0 0 #
   rotation -1 0 1 0.955 # 
   children [ Shape {
   appearance   Appearance {
        material        Material {
         diffuseColor   0.8 0.15 0
        }
   }
   geometry     Box {}
  }
]}
]}
ROUTE Spin_Timer.fraction_changed TO Spin_Interpolator.set_fraction
ROUTE Spin_Interpolator.value_changed TO Cube_Transform.set_rotation