18.1. Готовим сцену

18.1. Готовим сцену

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

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

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

Добавление объектов к сцене, их перемещение, игра с источниками освещения и геометрией – работа компьютерного художника. Наша задача – предоставить интерактивную поддержку для манипуляций с графом сцены на экране. Предположим, что в текущей версии своего инструмента мы решили воспользоваться каркасом приложений Open Inventor для C++ (см. [WERNECKE94]), но с помощью подтипизации расширили его, создав собственные абстракции нужных нам классов. Например, Open Inventor располагает тремя встроенными источниками освещения, производными от абстрактного базового класса SoLight:

class SoSpotLight : public SoLight { ... }

class SoPointLight : public SoLight { ... }

class SoDirectionalLight : public SoLight { ... }

Префикс So служит для того, чтобы дать уникальные имена сущностям, которые в области компьютерной графики весьма распространены (данный каркас приложений проектировался еще до появления пространств имен). Точечный источник (point light) – это источник света, излучающий, как солнце, во всех направлениях. Направленный источник (directional light) – источник света, излучающий в одном направлении. Прожектор (spotlight) – источник, испускающий узконаправленный конический пучок, как обычный театральный прожектор.

По умолчанию Open Inventor осуществляет рендеринг графа сцены на экране с помощью библиотеки OpenGL (см. [NEIDER93]). Для интерактивного отображения этого достаточно, но почти все изображения, сгенерированные для киноиндустрии, сделаны с помощью средства RenderMan (см. [UPSTILL90]). Чтобы добавить поддержку такого алгоритма рендеринга мы, в частности, должны реализовать собственные специальные подтипы источников освещения:

class RiSpotLight : public SoSpotLight { ... }

class RiPointLight : public SoPointLight { ... }

class RiDirectionalLight : public SoDirectionalLight { ... }

Новые подтипы содержат дополнительную информацию, необходимую для рендеринга с помощью RenderMan. При этом базовые классы Open Inventor по-прежнему позволяют выполнять рендеринг с помощью OpenGL. Неприятности начинаются, когда возникает необходимость расширить поддержку теней.

В RenderMan направленный источник и прожектор поддерживают отбрасывание тени (поэтому мы называем их источниками освещения, дающими тень, – SCLS), а точечный – нет. Общий алгоритм требует, чтобы мы обошли все источники освещения на сцене и составили карту теней для каждого включенного SCLS. Проблема в том, что источники освещения хранятся в графе сцены как полиморфные объекты класса SoLight. Хотя мы можем инкапсулировать общие данные и необходимые операции в класс SCLS, непонятно, как включить его в существующую иерархию классов Open Inventor.

В поддереве с корнем SoLight в иерархии Open Inventor нет такого класса, из которого можно было бы произвести с помощью одиночного наследования класс SCLS так, чтобы в дальнейшем уже от него произвести SdRiSpotLight и SdRiDirectionalLight. Если не пользоваться множественным наследованием, лучшее, что можно сделать, – это сравнить член класса SCLS с каждым возможным типом SCLS-источника и вызвать соответствующую операцию:

SoLight *plight = next_scene_light();

if ( RiDirectionalLight *pdilite =

dynamic_castRiDirectionalLight*( plight ))

pdilite-scls.cast_shadow_map();

else

if ( RiSpotLight *pslite =

dynamic_castRiSpotLight*( plight ))

pslite-scls.cast_shadow_map();

// и так далее

(Оператор dynamic_cast – это часть механизма идентификации типов во время выполнения (RTTI). Он позволяет опросить тип объекта, адресованного полиморфным указателем или ссылкой. Подробно RTTI будет обсуждаться в главе 19.)

Пользуясь множественным наследованием, мы можем инкапсулировать подтипы SCLS, защитив наш код от изменений при добавлении или удалении источника освещения (см. рис. 18.1).

RiDirectionalLight :

public SoDirectionalLight, public SCLS { ... };

class RiSpotLight :

public SoSpotLight, public SCLS { ... };

// ...

SoLight *plight = next_scene_light();

if ( SCLS *pscls = dynamic_castSCLS*(plight))

pscls-cast_shadow_map();

Это решение несовершенно. Если бы у нас был доступ к исходным текстам Open Inventor, то можно было бы избежать множественного наследования, добавив к SoLight член-указатель на SCLS и поддержку операции cast_shadow_map():

class SoLight : public SoNode {

public:

void cast_shadow_map()

{ if ( _scls ) _scls-cast_shadow_map(); }

// ...

protected:

SCLS *_scls;

};

// ...

SdSoLight *plight = next_scene_light();

plight- cast_shadow_map();

* Самое распространенное приложение, где используется множественное (и виртуальное) наследование, – это потоковая библиотека ввода/вывода в стандартном C++. Два основных видимых пользователю класса этой библиотеки – istream (для ввода) и ostream (для вывода). В число их общих атрибутов входят: информация о форматировании (представляется ли целое число в десятичной, восьмеричной или шестнадцатеричной системе счисления, число с плавающей точкой – в нотации с фиксированной точкой или в научной нотации и т.д.);

* информация о состоянии (находится ли потоковый объект в нормальном или ошибочном состоянии и т.д.);

* информация о параметрах локализации (отображается ли в начале даты день или месяц и т.д.);

* буфер, где хранятся данные, которые нужно прочитать или записать.

Эти общие атрибуты вынесены в абстрактный базовый класс ios, для которого istream и ostream являются производными.

Класс iostream – наш второй пример множественного наследования. Он предоставляет поддержку для чтения и записи в один и тот же файл; его предками являются классы istream и ostream. К сожалению, по умолчанию он также унаследует два различных экземпляра базового класса ios, а нам это не нужно.

Виртуальное наследование решает проблему наследования нескольких экземпляров базового класса, когда нужен только один разделяемый экземпляр. Упрощенная иерархия iostream изображена на рис. 18.2.

Рис. 18.2. Иерархия виртуального наследования iostream (упрощенная)

Еще один реальный пример виртуального и множественного наследования дают распределенные объектные вычисления. Подробное рассмотрение этой темы см. в серии статей Дугласа Шмидта (Douglas Schmidt) и Стива Виноски (Steve Vinoski) в [LIPPMAN96b].

В данной главе мы рассмотрим использование и поведение механизмов виртуального и множественного наследования. В другой нашей книге, "Inside the C++ Object Model", описаны более сложные вопросы производительности и дизайна этого аспекта языка.

Для последующего обсуждения мы выбрали иерархию животных в зоопарке. Наши животные существуют на разных уровнях абстракции. Есть, конечно, особи, имеющие свои имена: Линь-Линь, Маугли или Балу. Каждое животное принадлежит к какому-то виду; скажем, Линь-Линь – это гигантская панда. Виды в свою очередь входят в семейства. Так, гигантская панда – член семейства медведей, хотя, как мы увидим в разделе 18.5, по этому поводу в зоологии долго велись бурные дискуссии. Каждое семейство – член животного мира, в нашем случае ограниченного территорией зоопарка.

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

Помимо классов, описывающих животных, есть и вспомогательные классы, инкапсулирующие различные абстракции иного рода, например "животные, находящиеся под угрозой вымирания". Наша реализация класса Panda множественно наследует от Bear (медведь) и Endangered (вымирающие).

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Глава 4. Готовим новый раздел

Из книги Linux From Scratch автора Бикманс Герард

Глава 4. Готовим новый раздел Вступление В этой главе речь пойдет о создании раздела для системы LFS. Мы создадим сам раздел, файловую систему на нем, и монтируем


Готовим «снаряжение»

Из книги Знакомства и общение в Интернете автора Леонтьев Виталий Петрович

Готовим «снаряжение» Прежде чем заходить на страничку знакомств, запускать чат или ICQ, нам необходимо обзавестись несколькими остро необходимыми вещами. Как известно, лучшая импровизация – та, которая подготовлена заранее…«Резюме». Объявления и ответы на нихКакую бы


Храним и готовим

Из книги Эффективное делопроизводство автора Пташинский Владимир Сергеевич

Храним и готовим Купить хорошую бумагу недостаточно. Ее необходимо правильно хранить и подготовить к использованию, от чего работоспособность техники зависит не меньше, чем от производственных параметров бумаги.Как и любой качественный продукт, офисная бумага капризна


Упражнение 2. Собираем сцену

Из книги 3ds Max 2008 для дизайна интерьеров автора Семак Рита

Упражнение 2. Собираем сцену В этом упражнении описано, как собрать несколько объектов в один файл. В данном случае закончим создание дизайна помещения.1. Откройте файл Стандартное освещение.max. Сначала соберем гостиную. Там будут располагаться диван, два кресла, столик и


nextScene - Переход на следующую сцену

Из книги Справочник по Flash автора Коллектив авторов

nextScene - Переход на следующую сцену nextScene(Глобальная функция)Перемещает воспроизводящую головку в первый кадр следующей сценыСинтаксис:nextScene()Описание:Функция nextScene() перемещает главную воспроизводящую головку фильма в первый кадр следующей сцены и останавливает


prevScene - Переход на предыдущую сцену

Из книги Главный рубильник. Расцвет и гибель информационных империй от радио до интернета автора Ву Тим

prevScene - Переход на предыдущую сцену prevScene(Глобальная функция)Перемещает воспроизводящую головку в первый кадр предыдущей сценыСинтаксис:prevScene()Описание:Функция prevScene() перемещает главную воспроизводящую головку фильма в первый кадр предыдущей сцены и останавливает


На сцену выходит Вейл

Из книги Социальные сети. ВКонтакте, Facebook и другие… автора Леонтьев Виталий Петрович

На сцену выходит Вейл В 1878 г. Теодор Вейл был амбициозным и целеустремленным 33-летним сотрудником Почтового ведомства США. Работа шла в гору: он организовал более эффективную форму железнодорожной почтовой связи, и под его началом были 3,5 тыс. человек. Однако он явно


Готовим «снаряжение»

Из книги Вопросы истории: UNIX, Linux, BSD и другие автора Федорчук Алексей Викторович

Готовим «снаряжение» Вы уже рветесь в бой? Понимаю – и, собственно говоря, ничего не имею против…За одним лишь исключением. Прежде чем заходить на страничку знакомств, запускать чат или ICQ, нам необходимо обзавестись несколькими остро необходимыми вещами.Как известно,


Герой выходит на сцену

Из книги автора

Герой выходит на сцену Итак, толчком для написания Линусом собственного ядра послужила MINIX – «студенческая» операционка Энди Танненбаума, с помощью патчей приспособленная для выполнения практической работы.Однако сам Линус не занимался «доведением MINIX до ума». Не