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

Текстурируем наши объекты

Хотя визуализация с цветами и освещением выглядит впечатляюще, если мы ограничимся ее использованием, объекты не будут выглядеть реалистично. Термин текстура (texture), используемый вне области трехмерной графики обычно описывает шероховатость объекта. В трехмерных сценах текстуры — это плоские двухмерные растровые изображения, которые можно использовать для имитации неровностей на примитиве. Вы можете взять растровое изображение травы, чтобы нарисовать реалистично выглядящий холм, или, возможно, облака, чтобы сделать небо. Direct3D может одновременно визуализировать восемь текстур для каждого примитива, но сейчас мы будем иметь дело только с одной текстурой.

Поскольку в качестве формата текстуры Direct3D использует обычные растры, текстурой может быть любое загруженное растровое изображение. Но как плоский двухмерный растр преобразуется в нечто, рисуемое на трехмерном объекте? У каждого визуализирумого в сцене объекта должны быть координаты текстуры, используемые в процессе растеризации для отображения каждого текселя на соответствующий пиксель экрана. Слово тексель (texel) обозначает элемент текстуры, или цветовое значение, соответствующее адресу в текстуре. Адреса очень похожи на номера столбцов и строк, и называются U и V соответственно.Обычно это скалярные значения из диапазона от 0.0 до 1.0. Точка с координатами 0, 0 находится в верхнем левом углу текстуры, а точка 1, 1 — в ее правом нижнем углу. Центру текстуры соответствуют координаты 0.5, 0.5 (рис. 3.2).


Рис. 3.2. Координаты текстуры

Рис. 3.2. Координаты текстуры


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

Листинг 3.2 Данные текстурированного куба

CustomVertex.PositionTextured[] verts = new CustomVertex.PositionTextured[36];

// Передняя грань
verts[0]  = new CustomVertex.PositionTextured(-1.0f,  1.0f,  1.0f, 0.0f, 0.0f);
verts[1]  = new CustomVertex.PositionTextured(-1.0f, -1.0f,  1.0f, 0.0f, 1.0f);
verts[2]  = new CustomVertex.PositionTextured( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f);
verts[3]  = new CustomVertex.PositionTextured(-1.0f, -1.0f,  1.0f, 0.0f, 1.0f);
verts[4]  = new CustomVertex.PositionTextured( 1.0f, -1.0f,  1.0f, 1.0f, 1.0f);
verts[5]  = new CustomVertex.PositionTextured( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f);

// Задняя грань (помните, что она направлена от камеры,
// поэтому вершины должны следовать по часовой стрелке)
verts[6]  = new CustomVertex.PositionTextured(-1.0f,  1.0f, -1.0f, 0.0f, 0.0f);
verts[7]  = new CustomVertex.PositionTextured( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f);
verts[8]  = new CustomVertex.PositionTextured(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f);
verts[9]  = new CustomVertex.PositionTextured(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f);
verts[10] = new CustomVertex.PositionTextured( 1.0f,  1.0f, -1.0f, 1.0f, 0.0f);
verts[11] = new CustomVertex.PositionTextured( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f);

// Верхняя грань
verts[12] = new CustomVertex.PositionTextured(-1.0f,  1.0f,  1.0f, 0.0f, 0.0f);
verts[13] = new CustomVertex.PositionTextured( 1.0f,  1.0f, -1.0f, 1.0f, 1.0f);
verts[14] = new CustomVertex.PositionTextured(-1.0f,  1.0f, -1.0f, 0.0f, 1.0f);
verts[15] = new CustomVertex.PositionTextured(-1.0f,  1.0f,  1.0f, 0.0f, 0.0f);
verts[16] = new CustomVertex.PositionTextured( 1.0f,  1.0f,  1.0f, 1.0f, 0.0f);
verts[17] = new CustomVertex.PositionTextured( 1.0f,  1.0f, -1.0f, 1.0f, 1.0f);

// Нижняя грань (помните, что она направлена от камеры,
// поэтому вершины должны следовать по часовой стрелке)
verts[18] = new CustomVertex.PositionTextured(-1.0f, -1.0f,  1.0f, 0.0f, 0.0f);
verts[19] = new CustomVertex.PositionTextured(-1.0f, -1.0f, -1.0f, 0.0f, 1.0f);
verts[20] = new CustomVertex.PositionTextured( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f);
verts[21] = new CustomVertex.PositionTextured(-1.0f, -1.0f,  1.0f, 0.0f, 0.0f);
verts[22] = new CustomVertex.PositionTextured( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f);
verts[23] = new CustomVertex.PositionTextured( 1.0f, -1.0f,  1.0f, 1.0f, 0.0f);

// Левая грань
verts[24] = new CustomVertex.PositionTextured(-1.0f,  1.0f,  1.0f, 0.0f, 0.0f);
verts[25] = new CustomVertex.PositionTextured(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f);
verts[26] = new CustomVertex.PositionTextured(-1.0f, -1.0f,  1.0f, 1.0f, 0.0f);
verts[27] = new CustomVertex.PositionTextured(-1.0f,  1.0f, -1.0f, 0.0f, 1.0f);
verts[28] = new CustomVertex.PositionTextured(-1.0f, -1.0f, -1.0f, 1.0f, 1.0f);
verts[29] = new CustomVertex.PositionTextured(-1.0f,  1.0f,  1.0f, 0.0f, 0.0f);

// Правая грань (помните, что она направлена от камеры,
// поэтому вершины должны следовать по часовой стрелке)
verts[30] = new CustomVertex.PositionTextured( 1.0f,  1.0f,  1.0f, 0.0f, 0.0f);
verts[31] = new CustomVertex.PositionTextured( 1.0f, -1.0f,  1.0f, 1.0f, 0.0f);
verts[32] = new CustomVertex.PositionTextured( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f);
verts[33] = new CustomVertex.PositionTextured( 1.0f,  1.0f, -1.0f, 0.0f, 1.0f);
verts[34] = new CustomVertex.PositionTextured( 1.0f,  1.0f,  1.0f, 0.0f, 0.0f);
verts[35] = new CustomVertex.PositionTextured( 1.0f, -1.0f, -1.0f, 1.0f, 1.0f);

buffer.SetData(verts, 0, LockFlags.None);

Очевидно, самое крупное изменение здесь — это тип данных, который теперь используется для хранения списка вершин. Последние два значения с плавающей точкой, хранящиеся в каждой вершине, — это координаты текстуры U и V, используемые при визуализации примитива. Каждая грань куба — квадрат, и текстура тоже квадрат, так что здравый смысл подсказывает нанести текстуру непосредственно на каждый квадрат. Можете обратить внимание, что на вершину в «верхнем левом» углу примитива отображается непосредственно тексель 0, 0; точно так же на «нижнюю правую» вершину отображается тексель 1, 1. В результате каждая квадратная грань будет целиком содержать текстуру.

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

vb = new VertexBuffer(typeof(CustomVertex.PositionTextured), 36, device,
                      Usage.Dynamic | Usage.WriteOnly,
                      CustomVertex.PositionTextured.Format, Pool.Default);

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

private void DrawBox(float yaw, float pitch, float roll,
                     float x,   float y,     float z,
                     Texture t)
{
    angle += 0.01f;

    device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, roll) *
                             Matrix.Translation(x, y, z);
    device.SetTexture(0, t);
    device.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);
}

Первые шесть параметров этой функции те же самые. что мы использовали до этого. В параметрах yaw, pitch и roll мы передаем вращение нашего куба, а x, y и z — это перемещение куба. Последний параметр — новый; он представляет текстуру, которую мы хотим использовать при визуализации куба. Мы также вызываем метод SetTexture устройства, чтобы сообщить Direct3D какую текстуру мы хотим использовать при визуализации примитивов. Первый параметр этого метода — это этап текстурирования для которого мы устанавливаем данную текстуру. Возможно, вы помните, что недавно я упоминал о возможности визуализации для примитива до восьми текстур; так вот, первый параметр это индекс такой текстуры. Поскольку у нас только один набор координат текстуры, мы всегда должны использовать первый индекс, 0. Также, обратите внимание, что поскольку мы здесь изменяем переменную angle и мировое преобразование, то из метода SetupCamera эти строки убраны.

Перед тем, как изменить код визуализации для вызова новой функции, нам необходимо определить текстуры, которые мы будем использовать. В папке с исходным кодом проекта на CD-ROM есть три текстуры, которые подключаются как ресурсы. Это файлы puck.bmp, ground.bmp и banana.bmp. В меню Project щелкните по пункту Add Existing Item и добавьте три изображения к вашему проекту. После того, как изображения добавлены вы должны просмотреть свойства каждого из них и изменить значение элемента Build Action на Embedded Resource. Поскольку мы хотим использовать три текстуры, добавьте к коду приложения объявления трех объектов текстуры. Это делается путем добавления следующего кода после объявления буфера вершин:

private Texture tex  = null;
private Texture tex1 = null;
private Texture tex2 = null;

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

tex  = new Texture(device, new Bitmap(this.GetType(), "puck.bmp"),
                   0, Pool.Managed);
tex1 = new Texture(device, new Bitmap(this.GetType(), "banana.bmp"),
                   0, Pool.Managed);
tex2 = new Texture(device, new Bitmap(this.GetType(), "ground.bmp"),
                   0, Pool.Managed);

Этот конструктор текстуры получает четыре параметра. Первый — это устройство, которое мы будем использовать для визуализации текстуры. Все ресурсы сцены (текстуры, буферы вершин и т.д.) связаны с устройством. Следующий параметр System.Drawing.Bitmap мы используем чтобы получить данные текстуры. В данном случае мы используем конструктор растрового изображения для загрузки файла из ресурсов. Третий параметр — это флаги использования, которые мы кратко обсудили, когда разговаривали о буфере вершин. Сейчас мы будем использовать здесь 0, поскольку использование нас не интересует. Последний параметр — это пул памяти для хранения текстуры. Для удобства мы пока будем использовать управляемый пул. Вот другие конструкторы, которые можно использовать для создания текстур:

public Texture (System.IntPtr lp,
                Microsoft.DirectX.Direct3D.Device device,
                Microsoft.DirectX.Direct3D.Pool pool)

public Texture (Microsoft.DirectX.Direct3D.Device device,
                System.Int32 width, System.Int32 height,
                System.Int32 numLevels,
                Microsoft.DirectX.Direct3D.Usage usage,
                Microsoft.DirectX.Direct3D.Format format,
                Microsoft.DirectX.Direct3D.Pool pool)

public Texture (Microsoft.DirectX.Direct3D.Device device,
                System.IO.Stream data,
                Microsoft.DirectX.Direct3D.Usage usage,
                Microsoft.DirectX.Direct3D.Pool pool)

Первый конструктор получает аргумент IntPtr, являющийся неуправляемым указателем на COM-интерфейс IDirect3DTexture9. Он предназначен для взаимодействия с неуправляемым кодом. Следующий конструктор позволяет нам создать чистую текстуру с нуля, задав высоту, ширину и количество уровней детализации, вместо того, чтобы читать эти значения из файла. Последний конструктор очень похож на тот, который мы использовали в нашем приложении, только в нем используется поток вместо объекта растрового изображения. Для работы этого конструктора необходимо, чтобы поток можно было загрузить в объект System.Drawing.Bitmap. Есть и другие интересные функции для загрузки текстур в классе TextureLoader; мы обратимся к ним в последующих главах.

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

// Рисуем наши кубы
DrawBox(angle / (float)Math.PI, angle / (float)Math.PI * 2.0f,
        angle / (float)Math.PI / 4.0f, 0.0f, 0.0f, 0.0f, tex);
DrawBox(angle / (float)Math.PI, angle / (float)Math.PI / 2.0f,
        angle / (float)Math.PI * 4.0f, 5.0f, 0.0f, 0.0f, tex1);
DrawBox(angle / (float)Math.PI, angle / (float)Math.PI * 4.0f,
        angle / (float)Math.PI / 2.0f, -5.0f, 0.0f, 0.0f, tex2);

Это должно визуализировать каждый из трех имеющихся кубов, только теперь их грани будут не окрашены в разные цвета, а текстурированы. Пример, содержащийся на CD-ROM рисует девять кубов (рис. 3.3).


Рис. 3.3. Текстурированные кубы

Рис. 3.3. Текстурированные кубы



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

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