Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Статьи / Вычисление текстурных координат полигона.

Вычисление текстурных координат полигона.

Автор: Курковский Александр


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

Итак, допустим, что Вам нужно загрузить в вашу программу некий 3D объект, сохраненный в каком-нибудь файловом формате, например *.3ds. Если сам формат поддерживает ТК, то тут нет проблем. Вы просто считываете их из файла.

А как быть в том случае, если сам формат в силу своей простоты не сохраняет ТК модели при импортировании, как, например, формат *.raw, который представляет собой просто массив вершин треугольных граней? В этом случае объект будет загружен не текстурированным.

А если захочется "набросить" на него какую-нибудь текстуру и посмотреть, как он будет выглядеть? Для этого придется самому вычислять ТК для каждой грани. А если граней несколько тысяч, как, например, в модели ланшафта?

Самый простой способ здесь - это задать ТК - м вершин треугольника следующие значения: (0,0) , (1,0) , (0,1). Если грань представляет собой прямоугольный треугольник с прямым углом в первой вершине, то текстура ляжет на неё "очень даже ничего". Но так как 99.9 % всех треугольных граней на практике имеют самые различные размеры, пропорции и ориентацию, то при таких значениях ТК текстура обязательно будет искажена и станет выглядеть криво и отвратно.

Те, кто знаком с 3D редактором Qoole (для Quake), наверно, помнит, как в нем производится автоматическое наложение текстуры на вновь созданный объект (brush). При отключенной опции Texture Lock текстура всегда лежит на грани ровно без масштабных искажений и перекосов. Если начать вращать или двигать brush по карте, то текстура будет лишь скользить по его поверхности, но никогда не будет искажаться.

Я долго пытался понять, как Qoole вычисляет ТК и, наконец, решил, что они являются особенным  образом трансформированными мировыми координатами вершин. На основании этого вывода мне удалось реализовать такое наложение текстуры при помощи следующего алгоритма:

//Исходные данные:

CVector3 v1,v2,v3;               //Мировые координаты вершин треугольной грани,
                                 //перечисленные в порядке против часовой стрелки.
int      tex_size_x, tex_size_y; //Размер текстуры по горизонтали и вертикали.
int      tex_size;               //Ширина текселя в масштабе мировых координат.
                                 
//Требуется найти:

CVector2 t1,t2,t3;               //Текстурные координаты грани.

//Решение:

tex_size = 1; //По умолчанию.
CVector3 n;   //Нормаль треугольника.

//Вычисляем нормаль треугольника:
n = Normalize((v2 - v1) ^ (v3 - v2)); //Оператор "^" - Cross Product.

Основная идея алгоритма заключается в том, что если повернуть грань таким образом, что его нормаль совпадет с мировой осью Z, то сама грань будет параллельна плоскости XY и тогда в качестве ТК можно будет просто взять мировые x, y координаты вершин, а оставшуюся координату z отбросить.

Для этого вычислим матрицу поворота 3x3, с помощью которой будем вращать вершины грани. Такую матрицу нужно вычислять для каждой грани индивидуально. Матрицу поворота будем представлять в виде трех взаимно перпендикулярных единичных векторов - столбцов. (Правила выполнения арифметических операций с векторами и матрицами расписывать не буду - их все знают). Используемые классы CVector2, CVector3, CMatrix3 тоже расписывать не буду. В качестве их подойдут любые классы, поддерживающие перегрузку операций над векторами и матрицами.

CVector3 rot_x, rot_y, rot_z; //Базисные вектора матрицы поворота.
CMatrix3 rot;                 //Матрица поворота.

//Вычисляем матрицу поворота:

//В качестве Z-вектора матрицы возьмем нормаль грани:
rot_z = n;

//В качестве X-вектора возьмем направляющий вектор первого ребра(v1..v2):
rot_x = Normalize(v2 - v1);

//Оставшийся Y-вектор вычисляем как обычно:
rot_y = rot_z ^ rot_x;

//Вычисляем матрицу:
rot = CMatrix3( rot_x, rot_y, rot_z );
rot = rot.Inverse();

//Вычисляем масштабирующий вектор:
CVector3 tex_scale = CVector3(tex_size_x, tex_size_y, 1) * tex_size;

//И наконец вычисляем ТК:

CVector3 vt; //Трансформированная вершина.

vt = (rot * v1) / tex_scale;   t1 = CVector2(vt.x, vt.y);
vt = (rot * v2) / tex_scale;   t2 = CVector2(vt.x, vt.y);
vt = (rot * v3) / tex_scale;   t3 = CVector2(vt.x, vt.y);

Таким образом, ТК вычисляются для всех остальных граней.

Вычисленные таким образом ТК вершин обеспечивают ровное, плоское и неискаженное наложение текстуры на грань.

18 июня 2002

#текстурирование

2001—2018 © GameDev.ru — Разработка игр