Пример объекта, у которого нормали
Рисунок 5.3. Пример объекта, у которого нормали вершин не совпадают с нормалями граней. Векторы нормалей вершин выделены черным цветом, а векторы нормалей граней — серым
Для описания нормалей вершин нам необходимо добавить соответствующие члены в структуру данных вершины:
struct Vertex { float _x, _y, _z; float _nx, _ny, _nz; static const DWORD FVF; } const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL;
Обратите внимание, что мы убрали члены данных, задающие цвет вершины, которые использовали в предыдущей главе. Дело в том, что теперь для вычисления цвета вершин мы будем использовать данные освещения.
Для простых объектов, таких как кубы и сферы, нормали вершин можно оперделить путем осмотра. Для сложных сеток необходим более алгоритмизированный способ. Предположим, что треугольник образован вершинами p0, p1 и p2, и нам необходимо вычислить нормали n0, n1 и n2 для каждой из вершин.
Простейший подход заключается в том, чтобы вычислить нормаль грани для треугольника и использовать ее в качестве нормали для всех трех вершин. Сперва вычислим два вектора, лежащих в полскости треугольника:
Тогда нормаль грани вычисляется по формуле:
Поскольку нормаль каждой вершины совпадает с нормалью грани:
Ниже приведена функция, которая вычисляет нормаль треугольной грани на основании координат трех ее вершин. Обратите внимание, что функция предполагает, что вершины перечислены по часовой стрелке. Если это не так, нормаль будет указывать в противоположном направлении.
void ComputeNormal(D3DXVECTOR3* p0, D3DXVECTOR3* p1, D3DXVECTOR3* p2, D3DXVECTOR3* out) { D3DXVECTOR3 u = *p1 - *p0; D3DXVECTOR3 v = *p2 - *p0;
D3DXVec3Cross(out, &u, &v); D3DXVec3Normalize(out, out); }
Использование нормали грани в качестве нормалей вершин не позволяет добиться гладкого изображения состоящих из треугольных граней сложных кривых поверхностей. Лучшим методом вычисления нормалей вершин является усреднение нормалей (normal averaging). Чтобы вычислить вектор нормали vn для вершины v, мы вычисляем нормали граней всех треугольников сетки, в которые входит данная вершина v. Затем вектор нормали вершины vn получается путем вычисления среднего значения всех этих нормалей граней. Давайте разберем конкретный пример. Предположим, вершина v входит в три треугольника, для которых известны их нормали граней n0, n1 и n2. Тогда vn вычисляется путем усреднения нормалей граней:
В процессе преобразований может получиться так, что векторы нормалей станут денормализованными. Так что лучше всего предусмотреть возможность подобной ситуации и приказать Direct3D заново нормализовать все векторы нормалей после преобразований, включив режим визуализации D3DRS_NORMALIZENORMALS:
Device->SetRenderState(D3DRS_NORMALIZENORMALS, true);