Данный материал взят с сайта old.shatalov.su и является его зеркалом

Создаём компьютерную игру. Создание игр на C++/DirectX

Есть вопросы?
Ошибка на сайте?
рус eng esp
Внимание! Данный сайт не обновляется. Новая версия: shatalov.su

Состояния рендеринга - render states

Дата создания: 2010-03-12 10:09:11
Последний раз редактировалось: 2012-02-08 11:10:34

    Предварительные уроки:
  1. Flexible vertex format. Перейти.
  2. Классы. Часть вторая. Перейти.

Сегодня мы рассмотрим состояния рендеринга в DirectX 9. На данный момент состояния рендеринга доживают свои последние дни. Им на смену пришли шейдеры. Собственно, мы рассмотрим состояния рендеринга, чтобы плавно перейти к изучению шейдеров.

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

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

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

Метод SetRenderState и перечисление D3DRENDERSTATETYPE

Типы состояний рендеринга можно посмотреть в перечислении D3DRENDERSTATETYPE. В документации: Direct3D9 → Reference → Direct3D Reference → Enumerations → D3DRENDERSTATETYPE.

Каждый тип состояний может принимать несколько значений. Различных типов - около сотни.

Для смены состояния в интерфейсе IDirect3DDevice9 существует специальный метод - SetRenderState:

HRESULT SetRenderState(
  D3DRENDERSTATETYPE State,
  DWORD Value
);

Первый аргумент говорит, какой тип состояния (из D3DRENDERSTATETYPE) будет меняться, а второй - это конкретное значение для данного типа состояния.

Конечно же, в Direct3D существует возможность узнать установленное в данный момент состояние рендеринга. Для этого используется метод IDirect3DDevice9::GetRenderState:

HRESULT GetRenderState(
  D3DRENDERSTATETYPE State,
  DWORD * pValue
);

Первый аргумент - тип состояния. Второй - указатель на переменную типа unsigned long, в которой будет сохранено значение.

Блоки состояний рендеринга - state blocks

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

В блоке состояний можно сохранить группу состояний, которые нужно поменять. В итоге, вместо того, чтобы менять через SetRenderState несколько состояний, можно применить один блок состояний.

Создание блока состояний происходит следующим образом:

IDirect3DStateBlock9* stateBlock = NULL;
videocard->BeginStateBlock();
// несколько вызовов SetRenderState
videocard->EndStateBlock(&stateBlock);

Обратите внимание, что переменная (экземпляр интерфейса IDirect3DStateBlock9), в которой будут сохранены состояния, передаётся в метод EndStateBlock, а не в BeginStateBlock.

Важное замечание! Между вызовами BeginStateBlock и EndStateBlock можно сохранить не только состояния рендеринга, но и много чего ещё: текущий источник данных ввода (нужно вызвать SetStreamSource), текущий буфер индексов (нужно вызвать SetIndices), текущий гибкий формат вершин (нужно вызвать SetFVF), текущие матрицы преобразования (нужно вызвать SetTransform). Все методы, которые можно вызывать между BeginStateBlock и EndStateBlock можно посмотреть на странице с описанием метода BeginStateBlock.

Интерфейс IDirect3DStateBlock9

Итак, в экземплярах интерфейса IDirect3DStateBlock9 можно сохранить определённые состояния рендеринга (и не только их - как мы видели выше, кое-что ещё).

Применить состояния, сохранённые в блоке состояний можно с помощью метода IDirect3DStateBlock9::Apply (apply - применить). Этот метод не принимает аргументов. Теперь посмотрим на пример. Допустим у нас есть два объекта, которые нужно выводить с разными состояниями рендеринга:

IDirect3DVertexBuffer9* vbCube;
IDirect3DVertexBuffer9* vbFrustum;
IDirect3DIndexBuffer9* ibCube;
IDirect3DIndexBuffer9* ibFrustum;

D3DMATRIX matCam;
D3DMATRIX matProj;
D3DMATRIX matCube;
D3DMATRIX matFrusum;

const unsigned long FVF_CUBE = D3DFVF_XYZ | DF3FVF_NORMAL |
                               D3DFVF_DIFFUSE | D3DFVF_SPECULAR |
                               D3DFVF_TEX1;
const unsigned long FVF_FRUSTUM = D3DFVF_XYZ | D3DFVF_DIFFUSE;

// заполнение буферов и матриц

videocard->SetTransform(D3DTS_PROJECTION, &matProj);
videocard->SetTransform(D3DTS_VIEW, &matCam);

IDirect3DStateBlock9* statesCube = NULL;
IDirect3DStateBlock9* statesFrustum = NULL;

videocard->BeginStateBlock();
videocard->SetTransform(D3DTS_WORLD,&matCube);
videocard->SetStreamSource(0,vbCube,0,sizeof(cubeVertex));
videocard->SetIndices(ibCube);
videocard->SetFVF(FVF_CUBE);
videocard->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
videocard->SetRenderState(D3DRS_SHADEMODE,D3DSHADE_GOURAUD);
videocard->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);
videocard->SetRenderState(D3DRS_LIGHTING,TRUE);
videocard->EndStateBlock(&statesCube);

videocard->BeginStateBlock();
videocard->SetTransform(D3DTS_WORLD,&matFrustum);
videocard->SetStreamSource(0,vbFrustum,0,sizeof(frustumVertex));
videocard->SetIndices(ibFrustum);
videocard->SetFVF(FVF_FRUSTUM);
videocard->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
videocard->SetRenderState(D3DRS_SHADEMODE,D3DSHADE_FLAT);
videocard->SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE);
videocard->SetRenderState(D3DRS_LIGHTING,FALSE);
videocard->EndStateBlock(&statesFrustum);

// начало основного цикла

statesCube->Apply();
// вывод куба

statesFrustum->Apply();
//вывод пирамиды

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

Состояния рендеринга в DirectX - render states

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

D3DRS_FILLMODE:
Этот тип позволяет задать способ вывода треугольников: D3DFILL_POINT - треугольники выводятся точками (на месте вершин), D3DFILL_WIREFRAME - треугольники выводятся в виде рёбер, D3DFILL_SOLID - нормальные закрашенные треугольники. По умолчанию используется D3DFILL_SOLID.

D3DRS_CULLMODE:
В DirectX стороны треугольников определяются порядком вершин. Если вершины заданы по часовой стрелке - это передняя сторона, против часовой - задняя. По умолчанию задняя сторона треугольников отбрасывается (cull). Тип состояний D3DRS_CULLMODE позволяет контролировать, какую сторону треугольника отбрасывать: D3DCULL_NONE - выводятся обе стороны, D3DCULL_CW - отбрасываются стороны треугольников, вершины которых определены по часовой стрелке (clockwise), D3DCULL_CCW - отбрасываются стороны треугольников, вершины которых определены против часовой стрелки (counterclockwise). По умолчанию используется D3DCULL_CCW.

D3DRS_LIGHTING:
Данный тип состояний позволяет включить свет. TRUE - свет включён, FALSE - выключен. При включённом освещении правильно выводятся только те вершины, у которых есть нормали. Именно по этой причине (у наших вершин не было нормалей) мы и выключали освещение. По умолчанию используется TRUE.

D3DRS_ZENABLE:
Состояние буфера глубины. D3DZB_FALSE - z-буфер не используется, D3DZB_TRUE - z-буфер (буфер глубины) используется, D3DZB_USEW - вместо z-буфера используется w-буфер. В наших программах по умолчанию было значение D3DZB_FALSE (зависит от инициализации структуры D3DPRESENT_PARAMETERS).

Смешивание (blending) и alpha:
Большая группа типов состояний: D3DRS_SRCBLEND, D3DRS_DESTBLEND, D3DRS_ALPHAREF, D3DRS_ALPHABLENDENABLE, D3DRS_BLENDOP, D3DRS_TEXTUREFACTOR, D3DRS_VERTEXBLEND и др. По смешиванию у нас будет отдельный большой (возможно не один) урок.

Трафаретный буфер (stencil buffer):
Также довольно большая группа состояний: D3DRS_STENCILENABLE, D3DRS_STENCILFUNC... С этим буфером мы познакомимся позже.

Туман (fog):
Ещё одна большая группа, которая устанавливает свойства тумана: D3DRS_FOGENABLE, D3DRS_FOGCOLOR, D3DRS_FOGSTART...

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

На сегодня всё.