Создание графических моделей с помощью Open Graphics Library

Текстура

Показывать лекцию целиком

Цель лекции: Рассмотреть создание текстуры из файла формата BMP, реализацию команд инициализации текстуры, изменения свойств текстуры и определения координат текстуры на Object Pascal и на C# с использованием библиотеки OpenTK.

Примеры на C# (WinForms) и Object Pascal (Delphi)

Пример № 9.1 Пример демонстрирует наложение нескольких копий текстуры на квадрат.

Пример № 9.2 Пример демонстрирует наложение текстуры на поверхность сферы.

Пример № 9.3 Пример демонстрирует формирование изображения цилиндра с наложением текстуры на его поверхность.

Содержание лекции

Под текстурой обычно понимают растровое изображение, отображаемое на гранях примитивов.

Предварительно содержание текстуры должно быть передано в библиотеку OpenGL. Для этого передается указатель на массив с информацией о текстуре. Информация о текстуре представляет собой построчное представление изображения в формате:

RGB….RGB
RGB….RGB
RGB….RGB

Где RGB – это красная, зеленая и синяя составляющие цвета каждого пиксела текстуры.

Размер текстуры должен быть кратен степени двойки: 32x32, 32x64, 128x256 и т д. Текстура может быть подготовлена различным образом: может быть сформирована программно или, например, загружена из какого-либо файла.

Использование изображения в формате BMP в качестве текстуры

Одним из наиболее близких форматов к формату текстуры OpenGL является формат хранения графических изображений BMP. Отличие в хранении данных о пикселах между форматом BMP и форматом текстуры OpenGL заключается в различном порядке байт, описывающих составляющие цвета пиксела. В формате BMP данные о каждом пикселе хранятся в порядке: BGR. В формате текстуры OpenGL цветовые характеристики пикселов хранятся в противоположном порядке: RGB.

Загрузка файла BMP в память

Формат файла BMP кроме самого изображения содержит так же общую информацию о нем. Например, такую как, формат изображения (в файле формата BMP изображение может храниться в нескольких разных вариантах), ширину и высоту изображения и др.

В программе на C# для использования изображения в формате ).

public static void LoadTexture(Bitmap bmp)
{
BitmapData data = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
GL.TexImage2D(TextureTarget.Texture2D, 0, 
PixelInternalFormat.Rgb, data.Width, data.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Bgr, 
PixelType.UnsignedByte, data.Scan0);
bmp.UnlockBits(data);
}

В качестве параметра методу передается объект типа ):

Bitmap bmpTex;
bmpTex = new Bitmap("OGL64x64.bmp");

В методе LoadTexture сначала вызывается метод LockBits объекта Bitmap, который блокирует объект в системной памяти. Затем вызывается метод GL.TexImage2D, который передает изображение в библиотеку OpenGL в качестве текстуры. В конце метода LoadTexture вызывается метод UnlockBits объекта Bitmap, который разблокирует объект в памяти.

В Object Pascal для использования изображения в формате BMP выполняются аналогичные действия. В листинге 9.3 приведена процедура, выполняющая загрузку изображения из файла формата BMP и его преобразование в формат текстуры OpenGL на языке Object Pascal. Для хранения информации о текстуре используется структура TGLTexture, в котором изображение текстуры хранится в динамическом массиве. Поля Height и Width структуры хранят информацию о высоте и ширине изображения в пикселах. Информация о размере текстуры необходима для инициализации текстуры.

type
  TGLTexture = record
    Height: Integer;
    Width: Integer;
    Data: array of Byte;
  end;
procedure LoadTextureFromBMP(const AFileName: String; var ATexture: TGLTexture);
var
  i, j: Integer;
  bmp: TBitmap;
begin
  bmp := TBitmap.Create;
  try
    bmp.LoadFromFile(AFileName); // загрузка текстуры из файла
    ATexture.Height := bmp.Height;
    ATexture.Width  := bmp.Width;
    SetLength(ATexture.Data, bmp.Width* bmp.Height*3);
    // заполнение битового массива
    For i:=0 to bmp.Height-1 do
      For j:=0 to bmp.Width-1 do begin
        ATexture.Data[(i*bmp.Width+j)*3+0]:=
GetRValue(bmp.Canvas.Pixels[j, i]);
        ATexture.Data[(i*bmp.Width+j)*3+1]:=
GetGValue(bmp.Canvas.Pixels[j, i]);
        ATexture.Data[(i*bmp.Width+j)*3+2]:=
GetBValue(bmp.Canvas.Pixels[j, i]);
      end;
  finally
    bmp.Free;
  end;
end;

Инициализация текстуры

При формировании изображения используется текущая текстура. Для определения текущей текстуры и её параметров используется команда TexImage2D:

C#:
  void TexImage2D(TextureTarget target, int level, PixelInternalFormat internalformat, 
int width, int height, int border, PixelFormat format, PixelType type, IntPtr pixels);
Object Pascal:
  procedure glTexImage2D (target: GLenum; level, components: GLint; width, height: 
GLsizei; border: GLint; format, _type: GLenum; pixels: Pointer);

Параметры команды:

  • target – определяет тип текстуры, параметр должен быть равен значению TextureTarget.Texture2D при использовании библиотеки OpenTK на C# и значению GL_TEXTURE_2D на Object Pascal.
  • level – уровень детализации текстуры, значение должно быть равно 0.
  • components – определяет количество цветовых компонентов в данных о текстуре.
  • В C# значения этого параметра определяются перечислением PixelInternalFormat.

    В Object Pascal параметр может принимать значения 1,2,3,4 и ряд специальных констант. При использовании команды в листингах 9.2 и 9.3 значение этого параметра равно 3, так как в файле формата BMP для каждого пиксела в массиве хранится три цветовых компонента.

  • width – ширина текстуры в пикселах.
  • height – высота текстуры в пикселах.
  • border – размер "окантовки", значение должно быть равно 0.
  • format - формат передаваемых данных о текстуре.
  • В C# значения этого параметра определяются перечислением PixelFormat. В Object Pascal параметр может принимать ряд значений, например, GL_RGB, GL_RGBA.
  • _type – определяет тип данных, используемый для передачи каждой составляющей цвета. В формате BMP для каждой составляющей цвета используется беззнаковое байтовое значение. Поэтому в C# для этого используется значение PixelType.UnsignedByte, а в Object Pascal –значение GL_UNSIGNED_BYTE.
  • pixels - указатель на блок памяти с подготовленным изображением текстуры в формате OpenGL. В Object Pascal при использовании процедуры из листинга 9.3 это должен быть указатель на нулевой элемент динамического массива поля Data структуры TGLTexture.
  • Настройка параметров отображения текстуры

    Для настройки параметров отображения текстуры, которые используются при изменении её положения в пространстве и масштабировании, используется команда TexParameter. Как и для многих других команд она реализуется в C# набором статических методов класса GL с различными типами параметров, а в Object Pascal – набором процедур с различным постфиксом и соотвествующими типами параметров. Ниже приведен вариант её реализация в C# и в Object Pascal для варианта, когда в качестве параметра передаётся целочисленное значение.

    C#:
      void TexParameter(TextureTarget target, TextureParameterName pname, int param);
    Object Pascal:
      procedure glTexParameteri (target, pname: GLenum; param: GLint);
    

    Параметры процедуры:

  • target – тип текстуры для которой определяются параметры. На C# параметр должен быть равен значению Texture2D перечисления TextureTarget, в Object Pascal – значению GL_TEXTURE_2D.
  • .
  • .
  • Значения параметра pname команды TexParameter для библиотеки OpenTK (C#) и Object Pascal
    ОписаниеOpenTK, C#Object Pascal
    Функция увеличения текстуры при масштабировании
    TextureParameterName.
    TextureMagFilter
    
    GL_TEXTURE_MAG_FILTER
    Функция уменьшения текстуры при масштабировании
    TextureParameterName.
    TextureMinFilter
    
    GL_TEXTURE_MIN_FILTER
    Значения параметра param команды TexParameter для библиотеки OpenTK (C#) и Object Pascal
    ОписаниеOpenTK, C#Object Pascal
    В качестве значения берется точка текстуры с ближайшими координатамиTextureMinFilter.NearestGL_NEAREST
    В качестве значения берется среднеарифметическое значение на основе 4 точек рядом с координатами точкиTextureMinFilter.LinearGL_LINEAR

    В листинге 9.4 приведен пример загрузки и инициализации текстуры на C# с помощью метода LoadTexture из листинга 9.1 .

    Bitmap bmpTex;
    private void Form1_Load(object sender, EventArgs e)
    {
    bmpTex = new Bitmap("OGL64x64.bmp");
      
    GLTexture.LoadTexture(bmpTex);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
    GL.Enable(EnableCap.Texture2D);
    }
    

    В листинге 9.5 приведен пример загрузки и инициализации текстуры на Object Pascal с помощью процедуры LoadTextureFromBMP из листинга 9.3 .

    Var
      Tex1: TGLTexture;
    …
    procedure TfrmGL.FormCreate(Sender: TObject);
    begin
    …
      LoadTextureFromBMP('..\OGL64x64.bmp',  Tex1);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Tex1.Width, Tex1.Height,
                  0, GL_RGB, GL_UNSIGNED_BYTE, @Tex1.Data[0]);
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); 
      glEnable(GL_TEXTURE_2D);
    …
    end;
    

    Для отображения текстуры, кроме загрузки текстуры и её инициализации, так же необходимо включить режим отображения текстуры с помощью команды Enable. Поэтому в листингах 9.4 и 9.5 после загрузки и инициализации текстуры вызывается команда Enable с параметром EnableCap.Texture2D на C# и параметром GL_TEXTURE_2D на Object Pascal для включения режима отображения текстуры. Для отключения режима отображения текстуры используется команда Disable.

    При формировании сложной сцены может потребоваться, чтобы одни объекты отображались с текстурой, а другие – без неё. В этом случае необходимо перед формированием одних объектов включать режим отображения текстуры, а перед формированием других объектов отключать формирование текстуры.

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

    Задание координат текстуры позволяет сопоставить вершины примитивов с изображением текстуры. Вне зависимости от размера изображения текстуры в пикселах, координаты текстуры определяются библиотекой OpenGL в интервале значений [0, 1].

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

    В библиотеке OpenTK (C#) команда реализуется с помощью набора статических методов класса GL с различними типами параметров, в Object Pascal – с помощью набора процедур. Ниже приведен вариант для случая, когда координаты указываются в качестве значений типа float:

    C#:
      void TexCoord2(float s, float t);
    Object Pascal:
      procedure glTexCoord2f (s,t: GLfloat);
    

    Параметры:

  • s – горизонтальная координата текстуры;
  • t – вертикальная координата текстуры.
  • Ниже приведен пример наложения текстуры на квадрат (рис 9.1).

    GL.Begin(BeginMode.Quads);
    GL.TexCoord2(0.0f, 0f);
    GL.Vertex2(-0.5f, -0.5f);
    GL.TexCoord2(0.0f, 1f);
    GL.Vertex2(-0.5f, 0.5f);
    GL.TexCoord2(1f, 1f);
    GL.Vertex3(0.5f, 0.5f);
    GL.TexCoord2(1f, 0f);
    GL.Vertex3(0.5f, -0.5f);
    GL.End();
    
    glBegin(GL_QUADS);
    glTexCoord2f(0,0);
    glVertex2f(-0.5, -0.5);
    glTexCoord2f(0,1);
    glVertex2f(-0.5, 0.5);
    glTexCoord2f(1,1);
    glVertex2f(0.5, 0.5);
    glTexCoord2f(1,0);
    glVertex2f(0.5, -0.5);
    glEnd;
    
    (рис 9.1) Результат наложения текстуры на квадрат

    Если необходимо, чтобы изображение дублировалось при наложении на примитив по горизонтали и/или вертикали, то в этом случае при задании координат текстуры необходимо указать значение больше 1. В листингах 9.8 и 9.9 приведен пример наложения текстуры, в котором изображение повторяется 4 раза по вертикали и по горизонтали (рис 9.8" type="example">листингах 9.8 и 9.9 приведен пример наложения текстуры, в котором изображение повторяется 4 раза по вертикали и по горизонтали (int w = 4; int h = 4; … GL.Begin(BeginMode.Quads); GL.TexCoord2(0.0, w); GL.Vertex3(-1.0f, 1.0f, 0.0f); GL.TexCoord2(0.0, 0.0); GL.Vertex3(-1.0f, -1.0f, 0.0f); GL.TexCoord2(h, 0.0); GL.Vertex3(1.0f, -1.0f, 0.0f); GL.TexCoord2(h, w); GL.Vertex3( 1.0f, 1.0f, 0.0f); GL.End();

    var
     w,h: Integer;
     begin
     … 
     w := 4; h := 4;
     glEnable(GL_TEXTURE_2D);
     glBegin(GL_QUADS);
       glTexCoord2f(0.0, w);    glVertex3f(-1.0,  1.0, 0.0);
       glTexCoord2f(0.0, 0.0);  glVertex3f(-1.0, -1.0, 0.0);
       glTexCoord2f(h, 0.0);    glVertex3f( 1.0, -1.0, 0.0);
       glTexCoord2f(h, w);     glVertex3f( 1.0,  1.0, 0.0);
     glEnd;
     glDisable(GL_TEXTURE_2D);
     …
     end; 
    
    (рис 9.2) Пример дублирования текстуры

    Наложение текстуры на сложные объекты

    При необходимости наложения текстуры на сложный объект координаты текстуры определяются индивидуально для каждого примитива.

    Ниже приведен пример наложения текстуры на сферу. Текстура как бы разбивается на прямоугольники, и каждый прямоугольник накладывается на отдельный четырехугольный примитив, из которых формируется поверхность сферы.

    void sphere(double r, int nx, int ny)
    {
      int i, ix, iy;
      double x, y, z;
      for (iy=0; iy < ny; ++iy)
      {
        GL.Begin(BeginMode.QuadStrip);
        for (ix = 0; ix <= nx; ++ix)
        {
          x = r * Math.Sin(iy * Math.PI / ny) * Math.Cos(2 * ix * Math.PI / nx);
          y = r * Math.Sin(iy * Math.PI / ny) * Math.Sin(2 * ix * Math.PI / nx);
          z = r * Math.Cos(iy * Math.PI / ny);
          //нормаль направлена от центра
          GL.Normal3(x, y, z);
          GL.TexCoord2((double)ix / (double)nx, (double)iy / (double)ny);
          GL.Vertex3(x, y, z);
      x = r * Math.Sin((iy + 1) * Math.PI / ny) * 
        Math.Cos(2 * ix * Math.PI / nx);
          y = r * Math.Sin((iy + 1) * Math.PI / ny) * 
            Math.Sin(2 * ix * Math.PI / nx);
          z = r * Math.Cos((iy + 1) * Math.PI / ny);
          GL.Normal3(x, y, z);
          GL.TexCoord2((double)ix / (double)nx, (double)(iy + 1) / (double)ny);
          GL.Vertex3(x, y, z);
        }
        GL.End();
      }
    }
    
    procedure sphere(r: GLFloat; nx, ny: integer);
    var 
      i, ix, iy: integer;
      x, y, z: GLFloat;
    begin
      for iy:=0 to ny-1 do begin
        glBegin(GL_QUAD_STRIP);
        for ix:=0 to nx do begin
          x:= r*sin (iy*pi/ny)*cos(2*ix*pi/nx);
          y:=r * sin (iy*pi/ny)*sin(2*ix*pi/nx);
          z:= r* cos(iy*pi/ny);
          glNormal3f(x, y, z);
          glTexCoord2f(ix/nx, iy/ny);
          glVertex3f(x, y, z);
          x:= r*sin ((iy+1)*pi/ny)*cos(2*ix*pi/nx);
          y:=r * sin ((iy+1)*pi/ny)*sin(2*ix*pi/nx);
          z:= r* cos((iy+1)*pi/ny);
          glNormal3f(x, y, z);
          glTexCoord2f(ix/nx, (iy+1)/ny);
          glVertex3f(x, y, z);
        end;
        glEnd;
      end;
    end;
    

    Методу класса sphere на C# и процедуре sphere на Object Pascal передаются три параметра:

  • r – радиус сферы;
  • nx и ny – количество окружностей параллельных плоскостям YX и XZ, которые используются для формирования изображения поверхности сферы.
  • (рис 9.3) Пример наложения текстуры на сферу

    Краткие итоги

    В OpenGL реализована поддержка нескольких типов текстур: линейных, поверхностных (двухмерных) и объемных (трехмерных) текстур.

    На практике часто используются двухмерные (поверхностные) тектуры. Этот тип текстур имеет две координаты s и t. Изображение для текстуры может быть сформировано программно или загружено из графического файла, например, формата BMP.

    Привязка текстуры к вершинам примитива выполняется с помощью команды TexCoord2. Для инициализации текстуры используется команда TexImage2D. Для отображения тектуры должны быть так же заданы алгоритмы масштабирования текстуры с помощью команды TexParameter. Отображение тектуры может быть включено и выключено при формировании примитивов с помощью команд Enable и Disable.

    Вернуться к учебному плану