netlib.narod.ru< Назад | Оглавление | Далее >

Параллельно-точечный свет

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

Приложение SpaceDonut

В приложении SpaceDonut параллельно-точечный источник света размещен между двумя пончиками с малиновой (а может быть черничной) глазурью. Окно приложения SpaceDonut показано на рис. 6.7.


Рис. 6.7. Приложение SpaceDonut

Рис. 6.7. Приложение SpaceDonut


Приложение SpaceDonut демонстрирует применение следующих технологий:

Класс SpaceDonutWin

Основная функциональность приложения SpaceDonut сосредоточена в классе SpaceDonutWin, объявление которого выглядит следующим образом:

class SpaceDonutWin : public RMWin
{
public:
    SpaceDonutWin();
    BOOL CreateScene();
protected:
    //{{AFX_MSG(SpaceDonutWin)
    afx_msg void OnRenderWireframe();
    afx_msg void OnRenderFlat();
    afx_msg void OnRenderGouraud();
    afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI);
    afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI);
    afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
private:
    LPDIRECT3DRMMESHBUILDER meshbuilder;
};

Подобно всем демонстрационным приложениям на CD-ROM, программа SpaceDonut наследует свой класс окна от класса RMWin. Класс SpaceDonutWin предоставляет две открытых функции: конструктор и функцию CreateScene(). Конструктор отвечает за инициализацию любых членов данных, определенных в классе (в нашем случае, единственной переменной). Функция CreateScene() создает сцену приложения.

Шесть защищенных функций реализуют поддержку приложением меню Render. Для изменения параметров сетки эти функции используют переменную meshbuilder.

Функция SpaceDonutWin::CreateScene()

Сцена приложения SpaceDonut создается функцией CreateScene(), код которой приведен в листинге 6.4.

Листинг 6.4. Функция SpaceDonutWin::CreateScene()

BOOL SpaceDonutWin::CreateScene()
{
    // ------- СЕТКА ПОНЧИКА --------
    D3DRMLOADRESOURCE resinfo;
    resinfo.hModule = NULL;
    resinfo.lpName = MAKEINTRESOURCE(IDR_DONUTMESH);
    resinfo.lpType = "MESH";
    d3drm->CreateMeshBuilder(&meshbuilder);
    meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE,
            NULL, NULL);
    meshbuilder->SetPerspective(TRUE);
    meshbuilder->SetColorRGB(D3DVALUE(1), D3DVALUE(1), D3DVALUE(1));
    ScaleMesh(meshbuilder, D3DVALUE(20));

    //------ ТЕКСТУРА ГЛАЗУРИ --------
    LPDIRECT3DRMTEXTURE texture;
    HRSRC texture_id = FindResource(NULL,
            MAKEINTRESOURCE(IDR_FROSTINGTEXTURE),
            "TEXTURE");
    d3drm->LoadTextureFromResource(texture_id, &texture);
    meshbuilder->SetTexture(texture);
    texture->Release();
    texture = 0;

    //------- НАЛОЖЕНИЕ ----------
    D3DRMBOX box;
    meshbuilder->GetBox(&box);
    D3DVALUE w = box.max.x - box.min.x;
    D3DVALUE h = box.max.y - box.min.y;

    LPDIRECT3DRMWRAP wrap;
    d3drm->CreateWrap(D3DRMWRAP_FLAT, scene,
        D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(0.0), // начало координат наложения
        D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0), // ось Z наложения
        D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(1.0), // ось Y наложения
        D3DVALUE(0.5), D3DVALUE(0.5),                // начало координат текстуры
        D3DDivide(1,w), D3DDivide(1,h),              // масштаб текстуры
        &wrap);
    wrap->Apply(meshbuilder);
    wrap->Release();
    wrap = 0;

    //------- ФРЕЙМЫ ДЛЯ ПОНЧИКА --------
    LPDIRECT3DRMFRAME leftframe;
    d3drm->CreateFrame(scene, &leftframe);
    leftframe->SetPosition(scene,
            D3DVALUE(-12), D3DVALUE(0), D3DVALUE(0));
    leftframe->SetOrientation(scene,
            D3DVALUE(0), D3DVALUE(1), D3DVALUE(0),
            D3DVALUE(0), D3DVALUE(0), D3DVALUE(1));
    leftframe->SetRotation(scene,
            D3DVALUE(0), D3DVALUE(1), D3DVALUE(0),
            D3DVALUE(0.1));
    leftframe->AddVisual(meshbuilder);
    leftframe->Release();
    leftframe = 0;

    LPDIRECT3DRMFRAME rightframe;
    d3drm->CreateFrame(scene, &rightframe);
    rightframe->SetPosition(scene,
            D3DVALUE(12), D3DVALUE(0), D3DVALUE(0));
    rightframe->SetOrientation(scene,
            D3DVALUE(0), D3DVALUE(1), D3DVALUE(0),
            D3DVALUE(0), D3DVALUE(0), D3DVALUE(1));
    rightframe->SetRotation(scene,
            D3DVALUE(0), D3DVALUE(1), D3DVALUE(0),
            D3DVALUE(-0.1));
    rightframe->AddVisual(meshbuilder);
    rightframe->Release();
    rightframe = 0;

    // --------- ПАРАЛЛЕЛЬНО-ТОЧЕЧНЫЙ СВЕТ --------
    LPDIRECT3DRMLIGHT light;
    d3drm->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT,
            D3DVALUE(1.0), D3DVALUE(1.0), D3DVALUE(1.0),
            &light);

    LPDIRECT3DRMFRAME lightframe;
    d3drm->CreateFrame(scene, &lightframe);
    lightframe->AddLight(light);

    lightframe->Release();
    lightframe = 0;
    light->Release();
    light = 0;

    //------ КАМЕРА ----------
    d3drm->CreateFrame(scene, &camera);
    camera->SetPosition(scene,
        D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50));
    d3drm->CreateViewport(device, camera, 0, 0,
            device->GetWidth(), device->GetHeight(),
            &viewport);

    return TRUE;
}

Функция CreateScene() выполняет следующие действия:

  1. Создает сетку пончика.
  2. Создает и загружает текстуру, изображающую глазурь на пончике.
  3. Накладывает текстуру глазури на сетку пончика.
  4. Создает два фрейма и размещает их с двух сторон от начала координат, присоединяя к каждому фрейму сетку пончика.
  5. Создает параллельно точечный источник света и фрейм для него.
  6. Создает порт просмотра.

Давайте рассмотрим код, выполняющий каждое из перечисленных действий. Мы начнем с первого этапа: создания и загрузки сетки пончика:

D3DRMLOADRESOURCE resinfo;
resinfo.hModule = NULL;
resinfo.lpName = MAKEINTRESOURCE(IDR_DONUTMESH);
resinfo.lpType = "MESH";
d3drm->CreateMeshBuilder(&meshbuilder);
meshbuilder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE,
        NULL, NULL);
meshbuilder->SetPerspective(TRUE);
meshbuilder->SetColorRGB(D3DVALUE(1), D3DVALUE(1), D3DVALUE(1));
ScaleMesh(meshbuilder, D3DVALUE(20));

Приведенный код аналогичен коду для объекта meshbuilder в других демонстрационных программах. Загружаемая сетка идентифицируется с помощью структуры resinfo. Константа IDR_DONUTMESH — это идентификатор ресурса для импортированного в проект файла сетки. Строка "MESH" определяет категорию ресурса. Указатель meshbuilder инициализируется с помощью функции CreateMeshBuilder() интерфейса Direct3DRM, после чего, для загрузки сетки применяется функция Load(). Вызов функции SetPerspective() разрешает перспективную коррекцию текстур. Последнее действие является необязательным, и выполняется только для того, чтобы улучшить вид сетки после наложения текстуры.

Затем вызывается функция SetColorRGB() интерфейса Direct3DRMMeshBuilder, чтобы окрасить сетку в белый цвет. Это сделано потому, что используемая в данном примере сетка пончика имеет грани различных цветов. Чтобы одновременно установить цвет для всех граней сетки и используется функция SetColorRGB(). После этого вызывается функция ScaleMesh() чтобы задать размер сетки.

Код следующего этапа — создания и загрузка текстуры, изображающей глазурь на пончике — выглядит так:

LPDIRECT3DRMTEXTURE texture;
HRSRC texture_id = FindResource(NULL,
        MAKEINTRESOURCE(IDR_FROSTINGTEXTURE),
        "TEXTURE");
d3drm->LoadTextureFromResource(texture_id, &texture);
meshbuilder->SetTexture(texture);
texture->Release();
texture = 0;

Переменная texture_id идентифицирует загружаемую текстуру. Функция FindResource() применяется для задания идентификатора ресурса (IDR_FROSTINGTEXTURE) и типа ресурса ("TEXTURE").

Указатель texture инициализируется функцией LoadTextureFromResource(). Новая текстура связывается с объектом meshbuilder с помощью функции SetTexture(). После выполнения описанных действий указатель texture освобождается.

На третьем этапе работы функция SpaceDonutWin::CreateScene() создает и применяет наложение текстуры. Вот как выглядит предназначенный для этого код:

D3DRMBOX box;
meshbuilder->GetBox(&box);
D3DVALUE w = box.max.x - box.min.x;
D3DVALUE h = box.max.y - box.min.y;

LPDIRECT3DRMWRAP wrap;
d3drm->CreateWrap(D3DRMWRAP_FLAT, scene,
    D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(0.0), // начало координат наложения
    D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0), // ось z наложения
    D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(1.0), // ось y наложения
    D3DVALUE(0.5), D3DVALUE(0.5),                // начало координат текстуры
    D3DDivide(1,w), D3DDivide(1,h),              // масштаб текстуры
    &wrap);
wrap->Apply(meshbuilder);
wrap->Release();
wrap = 0;

Функция CreateWrap() интерфейса Direct3DRM подробно рассматривалась в главе 5 (в разделе, посвященном приложению Wraps), поэтому здесь данный код не обсуждается. Если говорить коротко, создается плоское наложение текстуры, растягивающее текстуру по размеру сетки. Затем наложение текстуры применяется к сетке с помощью функции Apply() интерфейса Direct3DRMWrap.

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

LPDIRECT3DRMFRAME leftframe;
d3drm->CreateFrame(scene, &leftframe);
leftframe->SetPosition(scene,
       D3DVALUE(-12), D3DVALUE(0), D3DVALUE(0));
leftframe->SetOrientation(scene,
        D3DVALUE(0), D3DVALUE(1), D3DVALUE(0),
        D3DVALUE(0), D3DVALUE(0), D3DVALUE(1));
leftframe->SetRotation(scene,
        D3DVALUE(0), D3DVALUE(1), D3DVALUE(0),
        D3DVALUE(0.1));
leftframe->AddVisual(meshbuilder);
leftframe->Release();
leftframe = 0;

Указатель leftframe инициализируется функцией CreateFrame(). Функция SetPosition() применяется, чтобы переместить фрейм на 12 единиц влево от его местоположения по умолчанию (начала координат).

Расположенный следом вызов функции SetOrientation(), необходим из-за ориентации сетки пончика в файле. Файл сетки создан таким образом, что пончик расположен вдоль оси Y (лежит лицом вниз). Мы же хотим, чтобы пончик был выровнен вдоль оси Z (стоял на боку). Чтобы сделать это, нам необходимо знать, где у сетки находятся лицевой и верхний векторы.

Значение по умолчанию для лицевого вектора объекта равно <0, 0, 1>, а значит лицевая грань объекта расположена в направлении положительных значений по оси Z. Значение по умолчанию для верхнего вектора объекта равно <0, 1, 0>. Из вышесказанного следует, что для того, чтобы сориентировать пончик желаемым образом, достаточно просто поменять местами лицевой и верхний векторы. Благодаря этому лицевая поверхность кольца будет направлена в ту сторону, куда раньше был направлен его верх, и наоборот.

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

Код для создания и настройки указателя rightframe выглядит аналогично коду для leftframe, поэтому здесь мы отметим только различия:

Обратите внимание, что в данном приложении один и тот же конструктор сеток присоединяется к разным фреймам. Это весьма полезная техника для отображения и анимации объектов. Однако не забывайте, что хотя на сцене будут присутствовать несколько объектов, в действительности существует только один объект. Если вы измените текстуру или цвет конструктора сеток, это изменение повлияет на все экземпляры данного объекта. Фактически, если вы запустите приложение SpaceDonut и измените параметры визуализации с помощью меню Render, вы увидите, что изменится изображение обоих экземпляров сетки.

На пятом этапе работы функции CreateScene() выполняется создание параллельно-точечного источника света и фрейма для его размещения. Код этой части выглядит так:

LPDIRECT3DRMLIGHT light;
d3drm->CreateLightRGB(D3DRMLIGHT_PARALLELPOINT,
        D3DVALUE(1.0), D3DVALUE(1.0), D3DVALUE(1.0),
        &light);

LPDIRECT3DRMFRAME lightframe;
d3drm->CreateFrame(scene, &lightframe);
lightframe->AddLight(light);

lightframe->Release();
lightframe = 0;
light->Release();
light = 0;

Функция CreateLightRGB() интерфейса Direct3DRM используется для инициализации указателя light. Константа D3DRMLIGHT_PARALLELPOINT задает тип источника света, а три числовых значения определяют красную, зеленую и синюю составляющие цвета источника света (мы создаем источник белого света).

Далее указатель lightframe инициализируется с помощью функции CreateFrame() интерфейса Direct3DRM. Созданный ранее источник света присоединяется к новому фрейму с помощью функции AddLight(). Затем указатели light и lightframe освобождаются.

На заключительном, шестом этапе работы функции CreateScene() выполняется создание и размещение порта просмотра:

d3drm->CreateFrame(scene, &camera);
camera->SetPosition(scene,
        D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50));
d3drm->CreateViewport(device, camera, 0, 0,
        device->GetWidth(), device->GetHeight(),
        &viewport);

Также, как и в других демонстрационных программах выполняется инициализация и размещение фрейма camera. В данном случае камера смещается на 50 единиц от начала координат по направлению к зрителю. Указатель viewport инициализируется функцией CreateViewport() интерфейса Direct3DRM.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz