Область просмотра

1.03.2000 — 22.04.2000


Определения

Область просмотра
— часть пространства, видимая на экране.
Z-Buffer
— буфер величиной с экран, в котором сохраняются координаты Z нарисованных пикселей. Также известен как "Depth Buffer" — "Буфер Глубины" — по аналогии с тем, что ось Z добавляет третью величину — глубину, к ширине и высоте.
Матрица
— здесь: двухмерный массив 4х4, содержащий значения векторов систем координат.
Проекция
— перевод координат из пространства просмотра (камеры) на экран. Из трехмерных — в двухмерные. Обычно осуществляется при помощи значений, помещенных в специальную матрицу.
Угол обзора
— угол, образованный левой границей экрана, камерой и правой границей экрана.

Z-Buffer

Z-Buffer используется для обработки случаев, когда рисуемые объекты занимают одно и то же место на экране.

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

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

Поэтому сортируются не треугольники, а пиксели. Это работает так:

Когда пиксель рисуется, заодно вычисляется его координата Z — его удаленность от камеры. А в Z-Buffer'е содержатся значения Z уже нарисованных пикселей. Так вот: найденная Z сравнивается с координатой Z, уже нарисованной на экране точке. Если рисуемая точка ближе к экрану, чем уже нарисованная, то точка рисуется, и в Z-Buffer заносится ее значение Z. Если точка находится от камеры дальше, чем та, что уже нарисована, то она не рисуется, и вообще о ней можно забыть. :-)

Современные ускорители, естественно, все это выполняют сами, так что программисту только остается уведомить драйвер о том, чтобы ускоритель использовал Z-Buffer.

Код, который нужно будет изменить:

1. В формате пикселя в конструкторе Graphics нужно изменить значение глубины Z-Buffer'а. Здесь нужно учитывать следующее: чем больше бит выделяется под Z-Buffer, тем выше точность сортировки пикселей. Ускорители, существующие на данный момент, поддерживают 16 и 24 -битные Z-Buffer'а, но лучше поставить 32, так как лишняя точность не помешает. ;-)

pfd.cDepthBits = 32;

2. Потом — в конструкторе объекта Graphics, после инициализации OpenGL, нужно вызвать функцию, которая бы включила Z-Buffer. (В будущем, возможно, я добавлю в движок некоторые классные эффекты, которые будут требовать отключения буфера глубины.)

glEnable(GL_DEPTH_TEST);

3. Изменить ту строчку, в которой очищается экран так, чтобы очищался еще и Z-Buffer. Буфер глубины очищается значением максимальной удаленности от экрана — первый рисуемый пиксель всегда будет отображаться, поскольку он ничем не закрывается от камеры.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

Стеки трансформаций

OpenGL имеет два стека трехмерных трансформаций: для преобразования координат в трехмерном пространстве, и для проекции. Для рисования на экране точки сначала трансформируются стеком ObjectToView, а затем -- стеком ViewToScreen.

В OpenGL, как и во всем прочем трудовом мире, объект cCoords, подробно описанный в предыдущей главе, называется "матрицей". Просто значения векторов берутся, и загоняются в массив 4x4; при этом пустые клетки заполняются единицами и нолями... в Elfish преобразования cCoords в матрицу не используется.

Угол просмотра

Матрица проекции задает правила для области просмотра, такие как угол обзора. OpenGL предоставляет простую функцию контроля этого.

Как уже отмечалось в главе "Цветной треугольник", по умолчанию область просмотра ограничена по координатам Z, X и Y от -1 до 1. Чтобы исправить это явное недоразумение, в OpenGL предусмотрена  функция определения области просмотра: glFrustrum(left, right, bottom, top, znear, zfar).

Все очень просто: znear определяет удаленность экрана от камеры. left, right, bottom, top — это координаты на плоскости экрана, которые определяют, каким образом границы экрана будут интерпретироваться в пространстве просмотра. Точка (left, bottom, znear) — это левый нижний угол экрана, (right, top, znear) — правый верхний.

Параметр zfar определяет максимальную удаленность объектов от камеры — он имеет отношение к буферу глубины: чем меньше отношение zfar/znear, тем меньше битов будет требоваться для обсчета Z-Buffer'а, и тем точнее этот обсчет будет.

Итак, положим некоторые переменные для дальнейшего использования.

///////////////////////////////////////////////////////////
//    Некоторые предопределенные константы.
///////////////////////////////////////////////////////////

const float NearestPlane = 1.001f;
const float FarestPlane = 1022.9f;
// Как я крут в оптимизации!!! ;-)

Для задания znear и zfar OpenGL требует именно положительных значений.

Теперь я хочу сделать функцию SetFOV(float FOV), которая устанавливала бы угол обзора в градусах. Кажись, FOV расшифровывается как "Field Of View", что по-нашенскому "Область Просмотра".

// Следующие строки взяты прямо из исходников Quake!!!
#ifndef M_PI
#define M_PI        3.14159265358979323846
#endif
// Конец исходников Quake. Спасибо ID Software!!! :-)

#include <math.h>

void Graphics::SetFOV(float FOV)
{
    // Получить размер области рисования - это нужно
    // для вычисления отношения ширины окна к его высоте.
    RECT r; GetClientRect(Ahwnd, &r);

    // Соотношение ширины к высоте.
    double aspect = double(r.right)/double(r.bottom);

    // Немного математики...
    double ymax = NearestPlane * tan(FOV * M_PI / 360.0);

    // Здесь все очевидно.
    double ymin = -ymax;
    double xmin = ymin * aspect;
    double xmax = ymax * aspect;

    // Переключиться к стеку матриц проекции.
    glMatrixMode(GL_PROJECTION);

    // Сделать трансформацию единичной.
    glLoadIdentity();

    // Добавить матрицу области просмотра.
    glFrustum(xmin, xmax, ymin, ymax, NearestPlane, FarestPlane);

    // Переключиться к стеку матриц по умолчанию.
    glMatrixMode(GL_MODELVIEW); // Model->View
}

Левосторонняя система координат

Для преобразования правосторонней системы координат в левостороннюю нужно дописать в конструктор oGraphics следующие строки.

    // Матрица преобразования в левостороннюю систему.
    // Почти как в анриле...
    float Matrix[16] =
    {
        +1, +0, +0, +0,
        +0, +1, +0, +0,
        +0, +0, -1, +0, // на месте -1 находится ZAxis.z
        +0, +0, +0, +1
    };
    glLoadMatrixf(Matrix);

Эта функция заставит изменяться знаки координаты Z всех передаваемых в OpenGL векторов. Например, если есть вектор (12, 3, 5), то когда пошлешь его на экран, он преобразуется в (12, 3, -5).

Пример

В примере я нарисую два пересекающихся между собой треугольника, чтобы было видно работу Z-Buffer'а.

И сделаю FOV = 90 по умолчанию, поставив вызов SetFOV(90) в конец конструктора Graphics.

Содержание


(c) 2000 Константин Михеев — проект "Elfish Engine"

Hosted by www.Geocities.ws

1