Сегодня нам предстоит изучить достаточно сложный материал - текстуры. Мы научимся их загружать и натягивать на объекты. Работать с текстурами, это тебе не шубу в трусы запихивать. Приготовься к достаточно сложной работе.
Рис 1. Результат
Посмотри на рисунок 1. Здесь ты можешь увидеть результат сегодняшней работы. Я не смог подобрать более удачную позицию, но если ты запустишь пример, то сможешь увидеть все прелести текстуры. Для этого я сделал сферу вращающейся.
Как ты видишь, для примера используется сфера обтянутая текстурой. В качестве текстуры я использую системную иконку, так что ты сегодня научишься ещё и превращать иконы в bmp формат. Я не буду объяснять всю прогу, а остановлюсь только на том, что относится к текстуре.
Теперь бросим свой зоркий взгляд на функцию рисования:
procedure TForm1.FormPaint(Sender: TObject);
var
ps:TPaintStruct;
begin
BeginPaint(Handle,ps);
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glPushMatrix;
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
glTexImage2d(GL_TEXTURE_2D,0,3,TextureW,TextureH,0,GL_RGB,
GL_UNSIGNED_BYTE,Texture^);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_2D);
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
gluQuadricTexture(quadObj,GL_TRUE);
glTranslatef(TX,TY,0);
glRotatef(RX,1,0,0);
glRotatef(RY,0,1,0);
glRotatef(RZ,0,0,1);
gluSphere(quadObj,2.7, 15, 10);
TX:=TX+SX;
TY:=TY+SY;
if (TX<-10) or (TX>10) then
SX:=-SX;
if (TY<-10) or (TY>10) then
SY:=-SY;
glDisable(GL_TEXTURE_2D);
glPopMatrix;
glDisable(GL_DEPTH_TEST);
glFlush();
swapBuffers(dc);
EndPaint(Handle,ps);
end;
Посмотри внимательно на эту процедуру. Большинство тебе должно быть уже понятно.
glTexParameteri - устанавливает параметры текстуры. Первый параметр - тип, который может быть GL_TEXTURE_1D или GL_TEXTURE_2D. Второй параметр - символическое имя параметры, который нужно установить. Мы можем изменять: GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T и GL_TEXTURE_BORDER_COLOR . Третий параметр - значение, которое мы хотим установить, указанному в предыдущем параметре. Может не совсем понятно, но сейчас всё будет ясно. Давай рассмотрим каждый параметр в отдельности:
GL_TEXTURE_MIN_FILTER - устанавливает тип функции, которая будет использоваться при уменьшении текстуры. Если ты решил изменить этот параметр, то ты должен указать GL_TEXTURE_MIN_FILTER вторым в функции glTexParameteri. В этом случае, третий параметр сможет принимать значения:
GL_NEAREST - возвращает значение элемента текстуры, который является самым близким.
GL_LINEAR - возвращает среднее значение из четырёх ближайших к центру тестируемого пиксела.
GL_NEAREST_MIPMAP_NEAREST - выбирает ближайший MIPMAP и дальше использует тип NEAREST.
GL_LINEAR_MIPMAP_NEAREST - выбирает ближайший MIPMAP и дальше использует тип LINEAR.
GL_NEAREST_MIPMAP_LINEAR - выбирает два MIPMAP и дальше использует тип NEAREST.
GL_LINEAR_MIPMAP_LINEAR - выбирает два MIPMAP и дальше использует тип LINEAR.
GL_TEXTURE_MAG_FILTER - устанавливает функцию используемую при увеличении текстуры. Если ты решил её изменить, то ты должен установить GL_TEXTURE_MAG_FILTER вторым параметром в glTexParameteri, а третий сможет принимать значения:
GL_NEAREST
GL_LINEAR
GL_TEXTURE_WRAP_S - устанавливает повторения текстуры по координате S. Может принимать значения:
GL_CLAMP - запрещает повторение текстуры. Одна её копия будет натянута на весь объект.
GL_REPEAT - разрешает повторение текстуры.
GL_TEXTURE_WRAP_Т - то же самое, что и GL_TEXTURE_WRAP_S, только для координаты Т. Возможные значения третьего параметра те же.
GL_TEXTURE_BORDER_COLOR - устанавливает цвет обрамления текстуры. При этом параметре, третий сможет принимать значение массива из четырёх значений, которые интерпретируются как цвет (RGBA). Пор умолчанию (0,0,0,0).
Теперь перейдём к функции glTexEnvf. Она устанавливает параметры среды текстуры. Первый параметр должен быть GL_TEXTURE_ENV. Второй должен быть GL_TEXTURE_ENV_MODE. Третий может принимать значения GL_MODULATE, GL_DECAL или GL_BLEND. Я использую второй параметр, а ты можешь попробовать другие и посмотреть на результат.
Функция glTexImage2d устанавливает 2D текстуру. Первый параметр должен быть GL_TEXTURE_2D. Второй параметр - уровень детализации текстуры (0 - основной). Третий - количество цветовых компонент в текстуре (может быть 1,2,3 или 4). Дальше идёт ширина и высота. Дальше идёт обрамление (может быть 0 или 1). Я использую 0, чтобы не было никаких обрамлений. Далее - формат, в котором хранятся цветовые данные в массиве. Здесь могут быть значения GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE или GL_LUMINANCE_ALPHA. Предпоследний параметр - тип в котором хранятся цветовые данные (может быть GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT или GL_FLOAT). Последний параметр - указатель на массив текстуры.
Надо заметить, что в качестве последнего параметра должна передаватся реальная ссылка на текстуру. Прежде чем вызывать glTexImage2d, текстура должна быть уже загружена и подготовлена в этой переменной. Чуть позже я покажу, как загружается текстура.
После всех этих настроек, можно включить текстуру. Я это дулаю с помощью:
Первые две необязательны. Они просто включают использование определённых функций текстуры. Я их привёл только для примера, и в большинстве случаев они вам не понадобятся. Последний непосредственно включает показ текстуры. Теперь, все объекты которые мы будем рисовать, будут обтянуты выбранной текстурой. Это будет происходить, пока мы не отключим текстуру с помощью glDisable(GL_TEXTURE_2D) .
Всё остальное должно быть понятно. Так что давай ещё рассмотрим процедуру для загрузки текстуры из системной иконки.
procedure TForm1.GetTexture;
var
BMInfo :TBitmapInfo;
I,ImageSize :Integer;
Temp :Byte;
Bitmap :Graphics.TBitmap;
TempImage :TBitmap;
MemDC : HDC;
begin
//Подготавливаю переменные, для работы с картинками.
Bitmap:=TBitmap.Create;
TempImage:=TBitmap.Create;
TempImage.Width:=32;
TempImage.Height:=32;
//Прорисовываю системную иконку (Icon) в TempImage
//Если захочешь использовать текстуру из BMP файла
//то можно его напрямую засунуть в TempImage.
//Для этого достаточно написать TempImage.LoadFromFile('filename.bmp');
TempImage.Canvas.Draw(0,0,Icon);
//Определяю ближайшие размеры текстуры которые воспримет OpenGL.
Bitmap.Width:=RoundUpToPowerOf2(TempImage.Width);
Bitmap.Height:=RoundUpToPowerOf2(TempImage.Height);
//Копирую текстуру из TempImage в Bitmap с учётом размеров
//воспринимаемых OpenGL
Bitmap.Canvas.CopyRect(Rect(0,0,Bitmap.Width,Bitmap.Height),
TempImage.Canvas, Rect(0,0,32,32));
//Создаю контекст в памяти.
MemDC:=CreateCompatibleDC(0);
try
with bitmap do
begin
ImageSize:=bitmap.Width*bitmap.Height;
TextureW:=bitmap.Width;
TextureH:=bitmap.Height;
//Выделяю память для переменной Texture, в которой будет
//хранится преобразованная текстура. Обрати внимание, как она
//объявлена.
GetMem(Texture,ImageSize*3);
MemDC:=CreateCompatibleDC(0);
FillChar(BMInfo,SizeOf(BMInfo),0);
//Заполняю BMInfo информацией о текстуре.
With BMInfo.bmiHeader do
begin
biSize:=sizeof(TBitmapInfoHeader);
biBitCount:=24;
biWidth:=bitmap.Width;
biHeight:=-bitmap.Height;
biPlanes:=1;
biCompression:=BI_RGB;
end;
//Копирую данные из Bitmap в Texture
GetDIBits(MemDC,Bitmap.Handle,0,bitmap.Height,
Texture,BMInfo,DIB_RGB_COLORS);
{$R-}
//Запускаю цикл, в котором меняются местами цветовые
//компоненты красного и голубого т.е. идёт перевод из RGB в BGR
for i:=0 to ImageSize-1 do
begin
Temp:=Texture^[i*3];
Texture^[I*3]:=Texture^[I*3+2];
Texture^[I*3+2]:=Temp;
end;
{$R-}
glPixelStorei(GL_UNPACK_ALIGNMENT,0);
glPixelStorei(GL_UNPACK_ROW_LENGTH,0);
glPixelStorei(GL_UNPACK_SKIP_ROWS,0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS,0);
end;
finally
//Освобождаю всё не нужное.
Bitmap.Free;
TempImage.Free;
DeleteDC(MemDC);
end;
end;
Я не буду её расписывать, потому что она не совсем относится к OpenGL, но я постарался снабдить текст комментариями, чтобы можно было разобраться. Обязательно посмотри на них, там я описал много интересного. Для большей ясности я дам ещё несколько заметок:
Формат изображения нужного OpenGL очень похож на BMP. Только в BMP используется цвет RGB (красный, зелёный, голубой), а в OpenGL используется BGR (голубой, зелёный, красный). Это значит, что нам нужно каждый третий байт цвета поменять местами с первым.
У картинки должно быть определённое отношение ширины к высоте (ширина=2^n + 2, а высота=2^m + 2. Двойку нужно прибавлять при использовании обромлений). Чтобы не забивать особо голову этими функциями я написал небольшую функцию, которая поможет нам в определёнии размеров, так что используй её в своих творениях:
function TForm1.RoundUpToPowerOf2(Value: Integer): Integer;
var
LogTwo : Extended;
begin
LogTwo:=log2(Value);
if Trunc(LogTwo) < LogTwo then
Result:=Trunc(Power(2,Trunc(LogTwo)+1))
else
Result:=value;
end;
На сегодня всё. Можешь качать и играться с исходниками.