В прошлый раз я рассказал вам основы текстурного отображения. Сейчас я продолжу, уже углубляясь в детали. Мне кажется, что вместо того, чтобы рассказать, как подгружать файлы .bmp или .jpg, лучше объяснить еще раз, как работает текстурное отображение в OpenGL. На этот раз снабдив вас работающими примерами.
Текстуры в наших примерах будут генерироваться в процессе выполнения программы, а не загружаться из внешних файлов. Однако основные принципы одинаковы: определите указатель и заполните его данными о пикселях. После этого грузите их в OpenGL — и все. Отличие только в том, что сгенерированные текстуры создаются по какому-то алгоритму, и их, следовательно, также иногда называют математическими текстурами. При работе с картинками информация о пикселях хранится в некотором графическом формате. Она считывается и передается по указателю на текстуру.
Так зачем же использовать математические текстуры? Во-первых — из соображений размера изображения. Democoders используют сгенерированные текстуры в intro, так как те не занимают никакого места — только лишь одна процедура/функция. Вторая причина — качество. При использовании рисунка у вас есть только этот рисунок. Если приблизиться, картинка остается неизменной, и только выглядит более зернисто. А вот при использовании математических текстур можно приближаться сколь угодно близко: все, что потребуется сделать — это увеличить выход данных алгоритма и перезагрузить текстуру.
Начнем с определения необходимых переменных еще раз. Как уже говорилось в прошлый раз, нам необходим один ID для текстуры и один указатель на ее содержимое.
TextureID : GLuint; TextureData : pByte;
Теперь зарезервируем немного места для нашей текстуры. На этот раз хотим текстуру с 128 пикселями в ширину и 128 пикселями в высоту. Используя 3 байта для задания цвета (RGB), найдем требуемый размер памяти, перемножив эти числа.
GetMem(TextureData, 128*128*3); // make room for a 128x128 RGB texture
Теперь место под нашу текстуру отведено. Но, конечно же, там ничего нету. Давайте заполним его сплошным цветом. Поскольку выбрана модель RGB, один пиксель задается тремя байтами. Первый — значение красного цвета, второй — зеленого, третий — красного. Указатель определен как pByte
, так что диапазон для цвета — от 0 (нет цвета) до 255 (максимальное количество цвета).
Сплошной цвет — это довольно просто. В нашем примере мы установим первый из трех цветов в максимум, а остальные — в 0. То есть у нас будет что-то вроде этого.
i := 0; while i < 128*128*3 do begin TextureData[i] := 255; TextureData[i+1] := 0; TextureData[i+2] := 0; i := i + 3; end;
Теперь инициализируем текстуру и подгрузим ее так, как делалось в прошлой главе.
glEnable(GL_TEXTURE_2D); glGenTextures(1, @TextureID); glBindTexture(GL_TEXTURE_2D, TextureID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexImage2D(GL_TEXTURE_2D, 0, 3, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureData); glBindTexture(GL_TEXTURE_2D, TextureID);
Возьмите исходник примера. Ознакомьтесь с заполнением данных текстуры, попробуйте что-нибудь свое.
С этого момента, мы можем очистить указатель. Почему? Мы подгрузили содержимое памяти с этого адреса в систему OpenGL, и, следовательно, (надеюсь, что так) в память видеокарты. Так почему же мы не делаем это? А потому, что, как было сказано, математические текстуры имеют преимущество в том, что можно менять их внешний вид путем изменения алгоритма. Если удалить указатель, то потом придется создавать его снова и опять заполнять память по новому адресу, вместо того, чтобы просто ее изменить. То есть часть мощности cpu будет расходоваться впустую. Конечно, при загрузке изображений из файлов, указатель может быть удален.
А теперь воспитанники старой школы могут углядеть вот что: в текстуру можно писать так же, как и в виртуальный экран. В ней, в сущности, можно осуществлять анимацию! Все, что нам нужно — это что-нибудь наподобие процедуры putpixel
, например, вот это.
procedure TexPutPixel(TexTarget : pByte; iTexX, iTexY : Longint; iTexColorR, iTexColorG, iTexColorB : Byte); var iTemp : Longint; begin // do primitive clipping. No values <> size of texture are allowed if (iTexX<0) or (iTexX>128) or (iTexY<0) or (iTexY>128) then exit; iTemp := (iTexX*3) + (iTexY*128*3); (TexTarget + iTemp + 0)^ := iTexColorR; (TexTarget + iTemp + 1)^ := iTexColorG; (TexTarget + iTemp + 2)^ := iTexColorB; end;
Здесь лежит небольшой пример того, что я имею ввиду. Скажу лишь по секрету о препятствии, прежде чем вы кинетесь программировать анимацию с помощью текстур: это тормозит. Фактически тормозит настолько сильно, что лишает этот прием всей его прелести. Но не волнуйтесь — существуют другие способы проделывать это.
Теперь, когда вы знакомы с основами, можно уже показать, как подгружать растры из ресурсов, что делается в следующей главе. Обратите внимание, что я объясню то, как подгружать ресурсы, а не то, как считывать внешние растровые изображения, поскольку последнее совсем очевидно. Считывайте информацию о пикселе из файла соответствующего графического формата и передавайте ее по указателю. Если вы не слишком сведущи в форматах графических файлов, то существует множество библиотек и модулей, которые могут в этом помочь. Просто зайдите на сайт FreePascal и поищите такие модули.