2D-приложения OpenGL и FreeBasic(перевод с нем.)

Fblogo.gif                        OpenGL_Logo.jpg

Содержание:

  1. Предисловие
  2. OpenGL и FreeBasic
  3. Первые шаги с OpenGL
  4. Текстуры
  5. Смешивание
  6. Полезные методы
  7. Заключение


    1. Предисловие
      • Уважаемые любознательные программисты!
        Я написал небольшой учебник о том, как использовать OpenGl на первых порах в FreeBasic. Я хочу предложить свою помощь, чтобы у вас было с чего стартовать , используя эту великолепную API. Я постараюсь избавить вас от скучной теории и буду упираться непосредственно на важные вещи, которые имеют отношение к практике.

        Stormy aka Paul Grunewald

    2. OpenGL и FreeBasic
      • Общее
        Цитата:
        "OpenGL (Open Graphics Library) является спецификацией платформы и языком программирования, с независимым API (Application Programming Interface) для разработки 3D компьютерной графики." "Стандарт OpenGL описывает около 250 команд, которые позволяют визуализировать сложные 3D-сцены в реальном времени." Википедия

        Как видно из этой цитаты, OpenGL в основном связанна с 3D. Так зачем использовать OpenGL для 2D? Вы же можете просто использовать fbgfx2, содержащихся в FreeBASIC! Все очень просто: OpenGL с аппаратным ускорением! Это означает, что все функции рисования оптимизированы и задействуют аппаратные возможности видеокарты и поэтому очень быстро работают. Вместе с очень простым синтаксисом FreeBasic, новичкам будет легко создавать программы или игры, которые будут использовать мощнейшие возможности графики.
      • Преимущества
        Вот некоторые вещи, которые поддерживаются при использовании OpenGL (в сочетании с FreeBasic):
        • Использование возможностей видеокарты
        • Нагрузка на процессор и, следовательно, более высокая производительность
        • многочисленные учебники и документации в Интернете
        • простой API (OpenGL) и простой синтаксис (FreeBASIC)
        • Много новых функций рисования, задействующих аппаратное ускорение
        • Простая инициализация экрана с помощью простого синтаксиса FreeBasic
        • Независимость от платформы
      • Недостатки
        Там, где свет, есть конечно же и тени. Даже у роз есть шипы:
        • Тесная связь с видеокартой
        • Переосмысление FB-программиста (не волнуйтесь, все возможно!)
        • Корректировка графики (см. раздел: текстуры)
    3. Первые шаги с OpenGL
      • Интеграция OpenGl графики для ОС Windows и Linux
        Это первый практический урок и я сразу даю понять, как получить доступ к OpenGL в FreeBasic с помощью подключаемых файлов.
        Решение для ОС Windows и Linux:
         #INCLUDE Once "GL/gl.bi"
         #INCLUDE Once "GL/glu.bi"
        И вот мы вошли в ворота аппаратного ускорения! ;-) gl.bi содержит объявления функций (или прототипы функций OpenGL) библиотеки. glu.bi - утилита, которая предлагает несколько полезных функций, упрощающих работу.
      • Конфигурация параметров OpenGL 2D-режима
        Следующим наиболее важным шагом является экран инициализации. В других языках, таких как C/C++, это настоящая головная боль. Но в FreeBasic вы можете сделать это легко и эффективно с помощью команды ScreenRes:
        Screenres Width,height,depth,,&h2 Or fullscreen
        • ширина, высота в пикселях. Вы можете указать, например, 800 и 600, чтобы создать экран размером 800x600.
        • глубина цвета изображения в битах. Рекомендуется указать 32, потому что некоторые видеокарты медленны в 16bit режиме.
        • запись &h2 or &h1 означает:
          &h2(OpenGl), &h1(fullscreen). То есть задействуется режимы OpenGl и полноэкранный.
          Как правило, в полноэкнанном режиме FPS выше , чем в оконном режиме.
        Мы не должны забывать, что OpenGL по прежнему 3D API, поэтому должны внести соответствующие изменения:
         glMatrixMode (GL_PROJECTION)
         glLoadIdentity

        В OpenGL существуют различные матрицы, которые используются для представления объектов. В нашем случае (2D), мы используем так называемую проекционную матрицу. Если мы будем работать с 3D-объектами, эта матрица будет искажать объекты, беря их под косым углом просмотра. Однако, нас сейчас это мало интересует, потому что мы имеем дело только с 2D. :-) GlLoadIdentity используется для считывания текущей матрицы. Это, конечно, звучит сложновато, но со временем станет понятно. В отличие от QuickBasic или FreeBasic оси координат в OpenGL берут отсчет не в верхнем левом углу, а в нижнем левом углу. Эскиз экрана ниже с размерами 800x600, проиллюстрирует это:
        screens.jpg
        С помощью команд ниже, мы указываем свою систему координат, чтобы она адаптировалась к системе координат FreeBasic:
         glViewport(0,0,Width,height)
         glOrtho(0,Width,height,0,-128,128)
        Команда glOrtho, говоря упрощённо, задаёт размеры системы координат. Первые два параметра задают координаты левой и правой плоскостей отсечения. Следующие два параметра – верхней и нижней плоскостей отсечения. Последние два параметра задают расстояния до ближайшей и самой дальней плоскостей глубины.
        Далее небольшой трюк для повышения эффективности нашей OpenGL. Как мы знаем, OpenGl работает с 3D миром, но пользователь видит лишь определенную часть. То есть скажем заднюю часть он не видит. С помощью команд ниже, мы можем исключать из процесса вывода невидимые области:
         glMatrixMode (GL_MODELVIEW)
         glEnable (GL_CULL_FACE)
         glCullFace (GL_BACK)
        Это дает хоть и небольшое увеличение процента производительности, но все же!

        Теперь определим, что мы хотим использовать стандартные 2D текстуры:
         glEnable GL_TEXTURE_2D
         glLoadIdentity

        Особое значение имеют следующие команды:

        Тестирование глубины:
         glEnable (GL_DEPTH_TEST)
         glDepthFunc (GL_LESS)
        С помощью этих команд мы определяем необходимость удаления невидимых линий и поверхностей(без этого выводится все подряд и объекты, находящиеся дальше, могут оказаться в одной позиции с более близкими объектами).
        Это удобно , поскольку в проекте дает возможность использовать Z-координаты. Таким образом мы можем устанавливать, к примеру, что звездное небо изображается под астероидами и космическими кораблями. У фонового режима была бы здесь координата Z1 , а все объекты (астероиды, космические корабли) координату Z2. Можно варьировать это естественно сколько угодно, особенно если нам надо показать еще поверх линейку меню или актуальный счет...

        Альфа-тест:
         glEnable (GL_ALPHA_TEST)
         glAlphaFunc (GL_GREATER, 0.1)

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

        Основная информация:

        В этом разделе мы узнали, как подключить OpenGL в свой проект и настроить для 2D-приложений. Вот код, с которого вы могли бы начать свой собственный проект:

        Инициализация:
        ' Интеграция OpenGL
        #INCLUDE Once "GL/gl.bi"
        #INCLUDE Once "GL/glu.bi"
        
        ' Определение констант, которые важны для экрана
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полноэкранный режим ( &h0 = обычный, &h1 = полноэкранный )
        
        Screenres scrnX,scrnY,depth,,&h2 Or fullscreen
        
        ' Конфигурация OpenGL
        glMatrixMode(GL_PROJECTION)      ' Определение матрицы
        glLoadIdentity
        glViewport(0,0,scrnX,scrnY)      ' Установка оси координат
        glOrtho(0,scrnX,scrnY,0,-128,128)
        glMatrixMode(GL_MODELVIEW)       ' Отключить вывод невидимых частей
        glEnable(GL_CULL_FACE)
        glCullFace(GL_BACK)
        glEnable GL_TEXTURE_2D           ' Включение текстур
        glLoadIdentity
        glEnable(GL_DEPTH_TEST)          ' Тест глубины
        glDepthFunc(GL_LESS)
        glEnable(GL_ALPHA_TEST)          ' Тест Альфа
        glAlphaFunc(GL_GREATER, 0.1)

        Главный цикл:
        Do
          glClear  GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT
          '
          ' !! здесь различные команды !!
          '
          glFlush ' Обработка команд
          Flip
        Loop Until Multikey(&h01) ' Выход из цикла при нажатии Esc
        • glClear сбрасывает значения всего перечисленного в качестве параметров (в нашем случае очищает буфер цвета и буфер глубины) (в какой-то степени "CLS" для OpenGL).
        • Команда glFlush вызывает немедленное рисование ранее переданных команд
        • Изменяет текущую видимую видеостраницу экрана
      • Формы и цвета в OpenGL

        Объекты:

        OpenGL может предложить широкий спектр объектов, с помощью которых можно рисовать. К примеру:

        • Точки
        • Линии
        • Различные треугольники
        • Четырехугольники (для нас наиболее важно)
        • и ломаные линии (полигоны)

        Каждый из этих объектов для создания требует минимальный набор точек. То есть для линии по логике вещей нужно две точки, для квадрата четыре и т. д. Но что надо сделать, чтобы OpenGL начал рисовать? Предлагаю небольшой шаблон:
        glBegin [Objekt]
         [Koordinate 1]
          ... bis ...
         [Koordinate n]
        glEnd

        В параметре функции glBegin указывается определенная константа для соответствующего рисуемого объекта. Эти константы указаны в файлах заголовков OpenGL. Я подготовил небольшой перечень, в котором обобщены все важные объекты:
        Объект Константа Минимальное кол-во точек для создания
        Точка GL_POINTS 1
        Линия GL_LINES 2
        Треугольник GL_TRIANGLES 3
        Площадь GL_QUADS 4
        Многоугольник GL_POLYGON 3

        В итоге,если вы хотите нарисовать квадрат, то нужно просто указать константу GL_QUADS для функции glBegin. Ниже разместить функцию glEnd. А в рамках этого «блока» нужно указать координаты:
        glVertex3f  x, y, z
        Функция glVertex определяет неподвижную точку. Тройка означает, что команде передаются три аргумента. В данном случае это координаты в формате (x, y, z). Сигнатура f означает, что параметры этой команды имеют тип float (single в FreeBasic). Другим примером является команда glVertex2i: Здесь передаются два целочисленных аргумента (x,y).

        А вот готовый пример квадрата:
        glBegin GL_QUADS
            glVertex2i  0, 50    '' Внизу слева  (1.координата)
            glVertex2i 50, 50    '' Внизу справа (2.координата)
            glVertex2i 50,  0    '' Вверху справа(3.координата)
            glVertex2i  0,  0    '' Вверху слева (4.координата)
        glEnd

        В данном случае координаты представляют собой квадрат с длиной стороны 50 пикселей. Таким образом, вы можете вставить код в наш основной цикл до команды glFlush. Ниже две иллюстрации:

        quad1.gif                                               quad2.gif
        Квадрат в верхнем                                Порядок вывода координат
        левом углу

        Перемещение объектов:

        Для смещения объектов например по оси х + 50 и у + 50 есть замечательная функция:
        glTranslatef х, у, z
        Для использования в нашем примере просто добавьте glTranslatef 100,100,0 до функции glBegin GL_QUAD и вуаля: наш квадрат был перенесен в координаты (100, 100, 0). Объекты могут быть наложены друг на друга так же с помощью glTranslatef. Обратите внимание на следующий пример.

        Наложение объектов:
            ' Квадрат A
            glTranslatef 100,100,0       ' z = 0
            glBegin GL_QUADS
            glVertex2i  0, 50    '' Внизу слева  (1.координата)
            glVertex2i 50, 50    '' Внизу справа (2.координата)
            glVertex2i 50,  0    '' Вверху справа(3.координата)
            glVertex2i  0,  0    '' Вверху слева (4.координата)
            glEnd
        
            glLoadIdentity
        
            ' Квадрат B
            glTranslatef 125,125,1       ' z = 1
            glBegin GL_QUADS
            glVertex2i  0, 50    '' Внизу слева  (1.координата)
            glVertex2i 50, 50    '' Внизу справа (2.координата)
            glVertex2i 50,  0    '' Вверху справа(3.координата)
            glVertex2i  0,  0    '' Вверху слева (4.координата)
            glEnd
        
            glLoadIdentity

        quad3.gif                                                    quad4.gif
        Конечно в таком виде                                           Вот так кажется лучше,
        не так очевидно наложение,                              но работу с цветом разберем
        Гораздо лучше бы выглядело                             чуть ниже
        если квадраты были разного
        цвета

        Функция glLoadIdentity восстанавливает матрицу. Или если проще делает что-то вроде «перезагрузки», и это означает для нас, что наш "Курсор" отправляется к точке (0,0). Убрав эту команду после вывода первого квадрата (А) , наш второй квадрат окажется в координатах 225,225 (100+125). То есть координаты второго квадраты будет зависеть от координат последнего вывода. Пример:
        glTranslatef 100,100,0 'переход от (0, 0) (100, 100)
        'GlLoadIdentity  'Не делать восстановление (0, 0)
        glTranslatef 25,25,0 'переход от (100, 100) к (125, 125)

        Цвета:

        С помощью команды ниже вы можете указать цвет:
        glColor red, green, blue[, alpha]
        Существуют различные вариации этой команды:
        • glColor3f: красный, синий, зеленый значения между 1 и 0 (с плавающей точкой)
        • glColor4f: красный, синий, зеленый и альфа значения между 1 и 0 (с плавающей точкой)
        • glColor3ub: красный, синий, зеленый значения 255-0 (UBYTE)
        • glColor4ub: красный, синий, зеленый и альфа значения 255-0 (UBYTE)
        Кстати, я рекомендую два последних варианта, хотя это лишь вопрос предпочтения. Альфа-значения интерпретируются только в 32-битном режиме. Для того, чтобы изменить цвет, просто пропишите команду glColor с соответствующими параметрами перед командой glBegin:
        glTranslatef 100,100,0       ' z = 0
        glColor3ub 255,0,0 ' красный квадрат (A)
        glBegin GL_QUADS
        ...
        glTranslatef 125,125,1       ' z = 1
        glColor3ub 0,0,255 ' синий квадрат (B)
        glBegin GL_QUADS
        ...
        Раскраска вершин:

        Можно определять отдельные точки с определенным цветом:
        glTranslatef 100,100,0
        
        glBegin GL_TRIANGLES
            glColor3ub 255,0,0    '' красный цвет
            glVertex2i   0, 50    '' нижняя левая точка
            glColor3ub 0,255,0    '' зеленый цвет
            glVertex2i 100, 50    '' нижняя правая точка
            glColor3ub 0,0,255    '' синий цвет
            glVertex2i  50,  0    '' верхняя центральная точка
        glEnd
        Взгляните на скриншот ниже, и будет наверное все ясно:

        triangle1.gif
        Можно ясно увидеть,
        из каких точках выходят цвета.

        Небольшой совет: Если вы хотите использовать альфа-канал, необходимо активировать специальный режим смешивания в итоге точки будут появляться частично прозрачными. Но об этом посвящена глава смешивание (ниже).
    4. Текстуры
      • Общая информация
        Если посмотреть с практической стороны, то текстуры являются обычными изображениями, которые "помещают" на объект. Техника обращений с текстурами будет чем-то схожа с техникой обращений изображений в FreeBasic посредством PUT. Тем не менее, есть некоторые ограничения. Текстуры могут быть только квадратны и размеры текстуры как по горизонтали, так и по вертикали должны представлять собой степени двойки. Примерами могут служить: 32x32, 64x64, 128x128, 256x256, и т. д.

        Пример:

        texture_grass.jpg
        Текстура-трава 128x128
        texture_pebbles.jpg
        Текстура-галька 256x256

        Кстати, мы также можем использовать специальные фильтры для текстур, которые уже известны в различных 3D-играх.
      • Наша структура текстуры
        Чтобы описать текстуру , нам необходима структура , в которой будет храниться вся важная информация.
        Type GFXType
             Handle As gluint
             Width As Integer
             Height As Integer
             TextureFilter As Integer
             imageData As Ubyte Ptr
             bpp As Uinteger
             textype As Uinteger
        End Type

        Пояснение

        Handle - хендл текстуры
        Width , Height - ширина и высота текстуры
        TextureFilter - фильтр для текстуры (пока нам не требуется)
        imageData - начало блока памяти изображения текстуры
        bpp - глубина цвета бит на пиксель
        texture - флаг , представляющий информацию о том используется ли альфа-канал или нет. Возможные значения: здесь GL_RGB и GL_RGBA

        Для того, чтобы использовать эту структуру, мы должны объявить переменную этого типа данных:
        Dim Textur As GFXType
      • Загрузка графики
        Я подготовил небольшую коллекцию процедур для загрузки текстур ( 2d_opengl.zip ). Таким образом, вы можете загружать изображения форматов BMP, TGA и PNG. Формат BMP используется только, если вы не хотите использовать альфа-канал. С другой стороны форматы TGA и PNG ; полностью поддерживают альфа-канал , хорошо сжимаются , что экономит место на жестком диске, и экономят трафик в интернете.
        Для интеграции этого пакета, пишем следующее в вашем исходном коде:

        #INCLUDE "inc/2d_opengl.bi"
        Теперь вы можете использовать функцию load_texture:
        Function load_texture(filename As String, _
                 Byref texture As GFXType, _
                 flags As Integer = 0, _
                 killdata As Integer = 0) As Integer
        • Имя файла текстуры, включая путь
        • Переменная нашей структуры, в которую записываются данные о текстуре
        • С флагами мы можем включить определенные опции и фильтры для текстуры. Возможные значения для флагов:
          • TEX_NOFILTER - Эта опция влияет на так называемый Magfilter ("Увеличение фильтра"). Мы говорим об увеличении, когда объект больше, чем его собственная текстура - только тогда этот фильтр активен. Если вы передаете в флаг константу TEX_NOFILTER, GL_NEAREST активизируется, что является достаточным для быстрого отображения текстур. Если не передается, то GL_LINEAR включен, это означает, что графика «мягкая».
            Различия между GL_NEAREST и GL_LINEAR:
            gl_nearest_and_gl_linear.png
          • TEX_MIPMAP - являются готовыми, масштабными схемами, которые используются, когда объект уменьшается. С помощью этого флага у вас есть преимущество в скорости и графика выглядит более гладкой.
            Пример изображения без этого флага и с ним:
            mipmaps.jpg
          • Killdata означает, что вы можете удалить временный буфер данных, который содержит данные о точках. Он может быть получен если нужны будут изменения в буфере. Если он не установлен, у вас больше памяти. (0 = очистить буфер, 1 = не очищать приемный буфер)
        Фу! Уже много справочной информации! :) Использование же просто, однако:
        #INCLUDE "inc/2d_opengl.bi"
        
        Dim Textur As GFXType
        load_texture("grassflaeche.bmp", Textur)
        И вуаля: наша текстура загружена!
      • Отображение графики
        Теперь самое интересное. Нам нужно натянуть нашу текстуру на один из имеющихся объектов OpenGL. Из объектов выберем квадрат. Как вы помните, первоначально его код выглядел так:
        glTranslatef 100,100,0       ' z = 0
        glBegin GL_QUADS
            glVertex2i  0, 50    '' LINKS UNTEN  (1. Koordinate)
            glVertex2i 50, 50    '' RECHTS UNTEN (2. Koordinate)
            glVertex2i 50,  0    '' RECHTS OBEN  (3. Koordinate)
            glVertex2i  0,  0    '' LINKS OBEN   (4. Koordinate)
        glEnd
        Чтобы это сделать, надо определить координаты текстуры. Делается это функцией:
        glTexCoord2f x, y
        Здесь действует другая система координат. Начинается с нижнего левого угла (0, 0) и идет к верхнему правому углу (1, 1). Все, что между ними, следовательно, дробные числа (0,1, 0,2 ... 0,9).
        Это изображение показывает, как координаты расположены:
        texturec.png
        Таким образом, мы можем определить, какая часть текстуры нам необходима. К примеру, у нас есть графический файл, который хранит 10 кадров вращающегося астероида, мы можем выбрать один кадр...

        textured.png
        В изображении выше мы берем часть с координатами (0,0), (1,0), (0,1) и (1,1)!

        font2001.png
        А в этом изображении наглядно видно как берется часть изображения "A" из текстуры шрифта.

        Чтобы OpenGL знал, какой из графических файлов, мы используем, до того как указывать координаты , мы должны передать хендл текстуры в функцию :
        glBindTexture GL_TEXTURE_2D, Handle
        Далее мы должны связать координаты текстуры с правильными координатами нашего объекта:
        #INCLUDE "inc/2d_opengl.bi"   ' Подключение заголовка
        Dim Textur As GFXType   ' Textur definieren
        Call load_texture("grassflaeche.bmp", Textur)   ' Загрузка текстуры
        
        ' Eigentlicher Zeichenteil:
        
        glBindTexture GL_TEXTURE_2D, Textur.Handle  ' Привязка текстуры
        
        glTranslatef 100,100,0       ' z = 0
            glBegin GL_QUADS
            glTexCoord2f 0, 0
            glVertex2i  0, 50    '' Внизу слева  (1.координата)
            glTexCoord2f 1, 0
            glVertex2i 50, 50    '' Внизу справа (2.координата)
            glTexCoord2f 1, 1
            glVertex2i 50,  0    '' Вверху справа(3.координата)
            glTexCoord2f 0, 1
            glVertex2i  0,  0    '' Вверху слева (4.координата)
        glEnd
        glLoadIdentity
        
        glBindTexture GL_TEXTURE_2D, 0 ' Освободить текстуру

        В результате:
        texture1.jpg
        Размер прямоугольника(объекта) 50х50 меньше чем фактический размер текстуры 128х128 пикселей . В результате активен флаг minfilter. Если необходимо сделать объект в размер текстуры, то удобнее это сделать , используя данные из структуры:
        glBegin GL_QUADS
            glVertex2i 0, textur.height            '' Внизу слева  (1.координата)
            glVertex2i textur.width, textur.height '' Внизу справа (2.координата)
            glVertex2i textur.width, 0             '' Вверху справа(3.координата)
            glVertex2i 0,  0                       '' Вверху слева (4.координата)
        glEnd

        TEXTUR.WIDTH и TEXTUR.HEIGHT были установлены автоматически во время загрузки текстуры. То есть, по сути у нас всегда есть правильный размер и можем масштабировать тестуры без потерь. Еще одно замечание: если вы задаете glTextCoord2f со значениями выше, чем 1, то текстура заливает объект с повторами. Предположим, вместо единицы указываем двойку. Результат на рисунке:

        texture2.jpg
        И теперь последний совет для этой главы: никто не мешает вам раскрашивать текстуры , используя функцию glColor. Можете поэксперементировать с этим сами. Удачи! ;)
    5. Смешивание
      • Что это такое?
        Большинство специальных эффектов в OpenGL основано на так называемом смешивании (blending). Смешивание используется, чтобы комбинировать цвет данного пикселя, который должен быть выведен с пикселем, уже находящемся на экране. Как будут объединены цвета, будет зависеть от альфа-канала, и/или функции смешивания, которая задается. Самая важная функциональность, что встретит нас, - это "ALPHABLENDING". Вместе с тем можно позволять управлять, к примеру, частично-прозрачными четырехугольниками и использовать текстуры с альфаканалом. Представляя прозрачные объекты, мы должны упорядочивать их по глубине, то есть определить последовательность их перекрытия. Чтобы активировать режим смешивания, требуется следующая команда:
        glEnable(GL_BLEND)
        Предварительно установить желаемый способ смешивания; это делается с помощью функции:
        glBlendFunc( source , destination )
        Для параметров source и destination могут использоваться различные константы, которые приводят к различным режимам смешивания.

        Константа Используемый буфер Расчетный смесь фактор
        GL_ZERO source or destination (0, 0, 0, 0)
        GL_ONE source or destination (1, 1, 1, 1)
        GL_DST_COLOR source (Rd, Gd, Bd, Ad)
        GL_SRC_COLOR destination (Rs, Gs, Bs, As)
        GL_ONE_MINUS_DST_COLOR source (1, 1, 1, 1)-(Rd, Gd, Bd, Ad)
        GL_ONE_MINUS_SRC_COLOR destination (1, 1, 1, 1)-(Rs, Gs, Bs, As)
        GL_SRC_ALPHA source or destination (As, As, As, As)
        GL_ONE_MINUS_SRC_ALPHA source or destination (1, 1, 1, 1)-(As, As, As, As)
        GL_DST_ALPHA source or destination (Ad, Ad, Ad, Ad)
        GL_ONE_MINUS_DST_ALPHA source or destination (1, 1, 1, 1)-(Ad, Ad, Ad, Ad)
        GL_SRC_ALPHA_SATURATE source (f, f, f, 1); f=min(As, 1-Ad)

        Третий (последний) столбец содержит формулу, которая в свою очередь принадлежит константам в первом столбце. Буквы R, G, B, A означают значения цвета для красного, зеленого, синего и альфа соответственно. D и S обозначают соответствующий используемый буфер. Используя формулы можно рассчитать вручную, но лучше проверить самим , например в программе ниже.

        Я покажу тот который нравится мне и я его часто использую:
        glBlendFunc (GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA)

        Используя данный метод можно рисовать текстуры, используя альфа-канал.

        blending.jpg Возьмем в качестве фона текстуру № 1

        blending.png Текстуру № 2 положим на первую.

        blendinh.jpg С нашим режимом смешивания, мы можем хранить эти текстуры без каких-либо проблем.

        blendini.jpg Можно вывести со значением альфа-канала:
        glColor4f 255, 255, 255, 128

      • Практическое применение и примеры
        Я разработал небольшую программу, которая может послужить вам для экспериментов. Это как раз то, что я писал выше о вопросе смешивания на практике. СКАЧАТЬ: blending.zip

        Скриншот:

        blendinn.jpg

        Управление:
        Стрелки - переключения на различные опции
        1, 2, 3 - переход между текстурами
        Enter - изменение значения
        ESC - выход
        B - BlendMode вкл / выкл
        D - тест глубины вкл / выкл

    6. Полезные методы
      Примечание:
      Теперь следует небольшая коллекция полезных методов. Многие вещи взяты из той или иной игры. Ранее я писал без использования OpenGl , но впоследствии понял , что для большей производительности надо перестраиваться на эту библиотеку. Я давал ранее ссылку на скачивание архива 2d_opengl.zip , в котором осуществлены функции для загрузки изображений и инициализации OpenGL, но не помешает лишний раз ее вставить... . Кроме того все примеры вы можете взять здесь: examples.zip . Вы можете запускать их паралельно при обучении с помощью этого учебника.
       
      • Масштабирование и вращение

        Сперва попробую объяснить про масштабирование и изменения размеров объектов. И сразу же поговорю о повороте объектов.

        Масштаб:

        Для масштабирования объекта, требуется одна команда:
        glscalef x, y, z
        Так для увеличения объекта в два раза подойдет такой код:
        glscalef 2, 2, 1
        А для уменьшения объекта в два раза можно приспособить этот код:
        glscalef .5, .5, 1
        После того как объект был составлен(увеличен или уменьшен) , не забываем сбрасывать матрицу с помощью glLoadIdentity

        Поворот:

        В OpenGL можно повернуть всю матрицу. Команда для этого:
        glrotatef degree, x, y, z
        В параметре degree мы указываем угол поворота. Поскольку у нас 2D мир, то параметры x , y должны быть равны нулю, а параметр z =1.

        Пример: (example_scal_rot.bas)

        В примере кратко методы вращения и масштабирования. После исходный кода приведен скриншот и еще ниже пояснение:
        ' 
        ' Written by Paul Grunewald (aka Stormy)
        ' Date: 07.28.2006
        ' Mail: Paul.Grunewald@gmail.com
        '
        #INCLUDE "inc/2d_opengl.bi"
        
        ' Константы
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полный экран ( &h0 = нет, &h1 = да )
        Dim As Integer midX, midY
        Dim As Integer w = 64, h = 64, change
        Dim As Single rot = 0, scal = 1
        Dim As GFXType texture
        
        ' Поиск центра
        midX = scrnX / 2
        midY = scrnY / 2
        
        ' Инициализируем экран
        Init_ogl2D(scrnX, scrnY, depth, fullscreen)
        
        glEnable GL_TEXTURE_2D                  ' Позволяем текстуру для проекта.
        load_texture ("resources/examples/eagle.tga", texture)  ' Загрузим текстуру
        glBindTexture GL_TEXTURE_2D, texture.handle     ' И активируем ее.
        
        Windowtitle "[ Rotation & Scale ] scale factor: "_
                     + STR$(scal) + " rotation degree: " + STR$(rot)
        
        Do
          glCls  
          glColor3f 1,1,1
               glTranslatef midX,midY,0
               glscalef scal, scal, 1 
               glrotatef rot, 0, 0, 1       
            glBegin GL_QUADS
                glTexCoord2f 0.0, 0.0 ' нижний левый
                glVertex2i   -w/2, h/2
                glTexCoord2f 1.0, 0.0 ' нижний правый
                glVertex2i   w/2, h/2
                glTexCoord2f 1.0, 1.0 ' верхний правый
                glVertex2i   w/2, -h/2
                glTexCoord2f 0.0, 1.0 ' верхний левый
                glVertex2i   -w/2, -h/2
            glEnd
             glLoadIdentity
          
          glFlush 
          Flip 
          Screensync
          change = 0
          If Multikey(SC_UP) Then scal+=.05: change = 1
          If Multikey(SC_DOWN) Then scal-=.05: change = 1: If scal <= .1 Then scal=.05  
        
          If Multikey(SC_RIGHT) Then rot+=5: change = 1
          If Multikey(SC_LEFT) Then rot-=5: change = 1
          
          If change = 1 Then 
           Windowtitle "[ Rotation & Scale ] scale factor: "_
                        + STR$(scal) + " rotation degree: " + STR$(rot)
          End If        
        Loop Until Multikey(SC_ESCAPE) ' Выход

        screensp.jpg

        Управление:
        Стрелка вверх - увеличение объекта
        Стрелка вниз - уменьшение объекта
        Стрелка вправо- вращение вправо
        Стрелка влево - вращение влево
        Escape - выход

        Пояснение исходника:

        В этом примере мы можем наш квадрат (Quad), который обтянут текстурой, увеличивать, уменьшать и вращать. Здесь новая команда(функция) Init_ogl2D. По сути это просто сокращение множества команд инициализации для 2D экрана. Таким образом теперь нужны лишь минимальные параметры :) . Так же представлена команда GlCls (аналог CLS в FreeBasic) , она может упростить ваш переход на OpenGl. ;-) Но самое интересное начинается внутри цикла. Мы размещаем позицию рисования по середине нашего экрана. Для этого используем ранее вычисленные значения половины ширины и высоты экрана (MIDX, MIDY) и помещаем их в функцию glTranslatef. Однако для того чтобы организовать вращение объекта вокруг своей серединной точки, нужно разместить его исходя из серединных координат. Или проще говоря, сдвинуть координаты объекта вверх и влево на половину размера объекта.
        И последнее замечание: В этом примере используется флаг по умолчанию при загрузке текстуры, но вы так же можете попробовать любой способ из предложенных ниже:
        load_texture ("resources/examples/eagle.tga", texture, TEX_NOFILTER)
        load_texture ("resources/examples/eagle.tga", texture, TEX_MIPMAP)
        load_texture ("resources/examples/eagle.tga", texture, TEX_MIPMAP Or TEX_NOFILTER)
      • Tilemaps

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

        Пример Tilemap (тайлмэп или карта из тайлов):

        tilemap.jpg

        Этот Таймэп взят от одной из моих игр. Тайл имеет размер 32х32 пикселей.Текстура имеет размеры 256x256 пикселей. В результате этого в одной текстуре у меня помещается 64 тайла (8 колонок по 8 тайлов в каждой). Чтобы вычислить число возможных плиток в колонке, да и самих колонок, разделите длину текстуры на длину плитки (256/32 = 8).

        tilemap_numbered.jpg

        Здесь все тайлы пронумерованы. Таким образом, вы можете определить для каждого тайла свой уникальный номер. В настоящее время проблема может заключаться в вычислении координат текстуры для тайлов относительно своего уникального числа.

        Проблема:
        К сожалению, мы не можем обращаться к текстуре с помощью единиц измерения пиксель. Координаты указываются в пределах области от 0 до 1. Это становится проблематичным, особенно если мы хотим делать с точностью до пикселя. Однако мы может обойти эту трудность. Ниже вы видите еще раз расположение наших координат текстуры. Глядя на это, мы можем рассчитать наши координаты для наших тайлов.

        texturec.png

        Решение:
        Эту проблему мы решим с помощью математического подхода , учитывая:
        1) Высоту, ширину текстуры
        2) Высоту, ширину тайла
        3) Порядковый номер конкретного тайла
        Высота и ширина текстуры твердо закреплены в структуре GFXType. Данные тайла можно определить следующим образом:
        Const tile_width = 32   ' ширина
        Const tile_height = 32  ' высота
        Так как возможность вынимать тайлы из текстуры может использоваться не единожды, то лучше все это дело занести в процедуру:
        Sub PutTile (Byval texture As GFXType,
                Byval index As Integer,
                Byval w As Integer,
                Byval h As Integer,
                Byval x As Integer,
                Byval y As Integer,
                Byval z As Integer = 0)
        index - представляет собой идентификатор тайла.
        w, h - размеры для тайла
        x, y, z - координаты, в которых надо поместить тайл.
        Пока все хорошо. Теперь нам нужно вычислить правильные координаты текстур:
        Dim As Double cx, cy, rw, rh, horiz, vert
            index = index - 1
            horiz = Fix(texture.width/w)    '' Количество горизонтальных тайлов
            vert = Fix(texture.height/h)    '' Количество вертикальных тайлов 
            cx = (index Mod horiz)/horiz    '' X-Расположение нашего тайла
            cy = (index\vert)/vert          '' Y-Расположение нашего тайла
            rw = 1/horiz            '' Ширина тайла (в текстурных координатах)
            rh = 1/vert         ''  Высота тайла (в текстурных координатах)
        Теперь это может казаться довольно запутанным, хотя здесь чистая математика! Для примера возьмем тайл с номером 11. В процедуре мы вычитаем 1, потому что внутренне отсчет первого тайла начинается с нулевой позиции. При вычислении мы получаем:

        horiz = 8 и vert = 8.
        cx = (10 mod 8) / 8 результат в 2/8 = 1/4.
        cy = (10 \ 8) / 8 = 1/8.

        Имея эти значения , мы можем вычислить 4 текстурных координаты:
        glBindTexture GL_TEXTURE_2D, texture.handle
        glTranslatef x,y,z
        
        glBegin GL_QUADS
            glTexCoord2f cx,1-cy-rh
            glVertex2i 0, h         ''  Внизу слева
            glTexCoord2f cx+rw,1-cy-rh
            glVertex2i w, h         '' Внизу справа
            glTexCoord2f cx+rw,1-cy
            glVertex2i w, 0         '' Вверху справа
            glTexCoord2f cx,1-cy
            glVertex2i 0, 0         '' Вверху слева
        glEnd
        End Sub
        Даже в том случае, если вы не поняли, как это работает, вы не должны отчаиваться! Далеко не каждый водитель , знает как устроен его автомобиль. Главное знать как им пользоваться. Данную процедуру PutTile я внес в заголовки и вы можете ей пользоваться , не задумываясь ;)
        Пример: (example_tilemap.bas)
        Я подготовил пример, где все это можно увидеть в действии:
        ' 
        ' Written by Paul Grunewald (aka Stormy)
        ' Date: 07.28.2006
        ' Mail: Paul.Grunewald@gmail.com
        '
        #INCLUDE "inc/2d_opengl.bi"
        
        ' Константы
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полноэкранный режим ( &h0 = нет, &h1 = да )
        ' Tiles
        Const tile_width = 32   ' ширина
        Const tile_height = 32  ' высота
        
        Dim As GFXType texture
        
        ' Initialisiere den Schirm
        Init_ogl2D(scrnX, scrnY, depth, fullscreen)
        
        glEnable GL_TEXTURE_2D                      ' Позволяем текстуру.
        load_texture ("resources/examples/tilemap.tga", texture)    ' Загрузка текстуры
        
        Windowtitle "[ Tilemap ]"
        
        ' карта
        
        Dim MyMap( 1 To 8, 1 To 8 ) As Integer => _
            { { 0, 0, 0, 0, 0, 0, 0, 0 }, _
              { 0, 0, 0, 0, 0, 0, 7, 0 }, _
              {41,42,42,42,42,45, 0, 0 }, _
              {49,50,50,50,50,53, 0, 0 }, _
              {49,54,55,63,50,53, 0, 4 }, _
              {57,62,59,60,58,61, 5, 3 }, _
              { 0, 0, 0, 0, 0, 0, 0, 0 }, _
              { 2, 2, 0, 0, 0,12,13, 0 } }
        
        Dim As Integer x, y    
        
        Do
          glCls  
          glColor3f 1,1,1
           For y = 1 To 8
            For x = 1 To 8
            If MyMap(y, x) <> 0 Then
             PutTile (texture,_
                      MyMap(y, x),_
                  tile_width,_
                  tile_height,_
                  (x*tile_width),_
                  (y*tile_height),_
                  0)
             End If     
            Next x
           Next y
          glFlush 
          Flip 
          Screensync
        Loop Until Multikey(SC_ESCAPE) ' Выход
        

        screenshot_tilemap.jpg

        Естественно, каждый может по своему усмотрению свободно расширять (изменять) эту технику (например, многослойные тайлы...).
      • Отсечение

        Под отсечением определяют представление частичных частей графических объектов. В том числе это называется визуализацией . Примерно так же как Tilemap, но на этот раз тайл должен быть не прямоугольной формы, а с переменной длиной и шириной.
        Предположим для игры есть поверхность, в которой слева-внизу отображение стабильности корабля и рядом чуть правее отображается еще какой нибудь потенциал. А слева-сверху отображение кол-ва ракет. Для реализации , мы создадим большой "файл-контейнер" и в него вставим наши отдельные изображения.
        Пример

        space_hud.jpg

        Как вы можете видеть, в отличие от Tilemaps , мы заранее знаем о размерах и координатах наших объектов, находящихся в текстуре, и закладываем их в код:
        1 пустая табличка (0,0) и (254, 46)
        1 заполненная табличка (0,46) и (254, 92)
        2 пустая табличка (0,92) и (254, 138)
        2 заполненная табличка (0.138) и (254, 184)
        Готовая к бою ракета (0,207) и (24, 254)
        Пустая, не существующая ракета (25,207) и (49, 254)
        Опять же, нам понадобится удобная процедура для получения этих изображений, которая пригодится для будущей работы:
        Sub PutClip (Byval texture As gfxtype,
                Byval x As Integer,
                Byval y As Integer,
                Byval z As Integer = 0,
                Byval offset_x1 As Integer,
                Byval offset_y1 As Integer,
                Byval offset_x2 As Integer,
                Byval offset_y2 As Integer)

        x, y, z - координаты вывода объекта.
        offset_ (x1, y1) и offset_ (x2, y2) - координаты отсечения.

        Размер нашей области переменный, следовательно зависит от наших координат текстуры.
        Dim As Integer quad_width, quad_height
        
        quad_width = Abs(offset_x1 - offset_x2)
        quad_height = Abs(offset_y1 - offset_y2)
        Далее аналогично, как с Tilemap , принцип тот же:
        glBindTexture GL_TEXTURE_2D, texture.handle
        glTranslatef x,y,z
        
        glBegin GL_QUADS
            glTexCoord2f offset_x1/texture.width, 1-offset_y2/texture.height
            glVertex2f   0, quad_height
            glTexCoord2f  offset_x2/texture.width, 1-offset_y2/texture.height
            glVertex2f   quad_width,  quad_height
            glTexCoord2f offset_x2/texture.width, 1-offset_y1/texture.height
            glVertex2f   quad_width, 0
            glTexCoord2f offset_x1/texture.width, 1-offset_y1/texture.height
            glVertex2f   0, 0
        glEnd
        glLoadIdentity
        
        End Sub
        Пример (example_clipping.bas)
        ' 
        ' Written by Paul Grunewald (aka Stormy)
        ' Date: 07.28.2006
        ' Mail: Paul.Grunewald@gmail.com
        '
        #INCLUDE "inc/2d_opengl.bi"
        
        ' Константы
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полноэкранный режим ( &h0 = нет, &h1 = да )
        
        Dim As GFXType texture
        
        ' Initialisiere den Schirm
        Init_ogl2D(scrnX, scrnY, depth, fullscreen)
        
        glEnable GL_TEXTURE_2D                      'Позволение текстуры.
        load_texture ("resources/examples/space_hud.tga", texture)  ' Загрузка текстуры
        
        Windowtitle "[ Clipping ]"
        
        Type Player
         health_current As Integer
         health_max As Integer
         shield_current As Integer
         shield_max As Integer
         rockets_current As Integer 
         rockets_max As Integer 
        End Type
        
        Dim MyPlayer As Player
        
        With MyPlayer
         .health_current = 100
         .health_max = 100
         .shield_current = 50
         .shield_max = 250
         .rockets_current = 2
         .rockets_max = 5
        End With
        
        Type CoordsTex
         As Integer X1, Y1, X2, Y2
        End Type
        
        Dim As CoordsTex health_empty, health_full, shield_empty, shield_full, rockets_active, rockets_inactive
        
        With shield_empty
         .X1 = 0: .Y1 = 0:  .X2 = 254: .Y2 = 46
        End With
        With shield_full
         .X1 = 0: .Y1 = 46:  .X2 = 254: .Y2 = 92
        End With
        With health_empty
         .X1 = 0: .Y1 = 92:  .X2 = 254: .Y2 = 138
        End With
        With health_full
         .X1 = 0: .Y1 = 138:  .X2 = 254: .Y2 = 184
        End With
        With rockets_active
         .X1 = 0: .Y1 = 207 :  .X2 = 24: .Y2 = 254
        End With
        With rockets_inactive
         .X1 = 25: .Y1 = 207:  .X2 = 49: .Y2 = 254
        End With
        
        Dim r As Integer
        
        Do
          glCls  
          glColor3f 1,1,1
          With health_empty
           PutClip (texture, 20, 420, 0, .X1, .Y1, .X2, .Y2)
          End With
          With health_full
           PutClip (texture, 20, 420, 1, .X1, .Y1, .X1 + (255*(MyPlayer.health_current/MyPlayer.health_max)), .Y2)
          End With
          With shield_empty
           PutClip (texture, 280, 420, 0, .X1, .Y1, .X2, .Y2)
          End With
          With shield_full
           PutClip (texture, 280, 420, 1, .X1, .Y1, .X1 + (255*(MyPlayer.shield_current/MyPlayer.shield_max)), .Y2)
          End With
          
          For r = 1 To MyPlayer.rockets_max
           If MyPlayer.rockets_current >= r Then 
            With rockets_active
            PutClip (texture, 10+(r-1)*50, 10, 0, .X1, .Y1, .X2, .Y2)
            End With   
           Else
            With rockets_inactive
            PutClip (texture, 10+(r-1)*50, 10, 0, .X1, .Y1, .X2, .Y2)
            End With      
           End If
          Next r
          
          If Multikey(SC_UP) Then
           If MyPlayer.rockets_current < MyPlayer.rockets_max Then MyPlayer.rockets_current += 1
          End If
        
          If Multikey(SC_DOWN) Then
           If MyPlayer.rockets_current > 0 Then MyPlayer.rockets_current -= 1
          End If
        
          If Multikey(SC_RIGHT) Then
            If MyPlayer.health_current < MyPlayer.health_max Then 
             MyPlayer.health_current+=1
            Else
            If MyPlayer.shield_current < MyPlayer.shield_max Then 
             MyPlayer.shield_current+=1
            End If 
           End If 
          End If      
        
          If Multikey(SC_LEFT) Then
            If MyPlayer.shield_current > 0 Then 
             MyPlayer.shield_current-=1
            Else
            If MyPlayer.health_current > 0 Then 
             MyPlayer.health_current-=1
            End If 
           End If 
          End If      
           
          glFlush 
          Flip 
          Screensync
        Loop Until Multikey(SC_ESCAPE) ' Выход
        

        screenshot_clipping.jpg

        Управление:
        Стрелка вверх - добавить ракету
        Стрелка вниз - снять ракету
        Стрелка вправо - ремонт судна
        Стрелка влево - повреждение судна
        Escape - выход
      • Эффект - Зеркало

        Зеркальные эффекты реализуются очень просто! По меньшей мере, это зависит от обстоятельств, как детализировано хотелось бы создавать эти отражения. Мы предпримем простые отражения без ряби и искажений волн на водной поверхности.
        Теория:
        Первое, мы должны выяснить , как наш зеркальный эффект должен выглядеть. По существу это одно полу-прозрачное отражение объекта на 180°. Кажется проблематичным привести это отражение также точно на нашу водную поверхность. Чтобы решить эту проблему, OpenGL предлагает нам возможность смешивания и тест глубины. Мы устанавливаем водную поверхность на определенный уровень, который будет не равен уровням, на которых будет размещены трава, камень, и т.д. , но будет равен уровню нашей отражаемой текстуры. Когда придет время смешать нашу текстуру с водной поверхностью, перед этим нужно указать команду:
        glDepthTest(GL_EQUAL)
        А для того, чтобы отразить тектуру по вертикали используется специально подготовленная процедура PutTextureVert. Далее пример (example_mirror.bas):
        ' 
        ' Written by Paul Grunewald (aka Stormy)
        ' Date: 07.28.2006
        ' Mail: Paul.Grunewald@gmail.com
        '
        #INCLUDE "inc/2d_opengl.bi"
        
        ' Константы
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полноэкранный режим ( &h0 = нет, &h1 = да )
        
        Dim As GFXType texture
        
        ' Инициализировать экран
        Init_ogl2D(scrnX, scrnY, depth, fullscreen)
        
        glEnable GL_TEXTURE_2D                      ' Позволение текстуры.
        load_texture ("resources/examples/horse.tga", texture)      ' загрузка текстуры
        
        Windowtitle "[ Mirror ]"
        
        Dim As Integer x, y    
        x = 150
        y = 0
        Do
          glCls  
          
          glDepthfunc (GL_LESS)      ' Стандартный тест глубины
          glDisable (GL_BLEND)       ' Нет наложения
          
          glBindTexture GL_TEXTURE_2D, 0 ' отказываемся от текстуры, поскольку используем цветные объекты
          
          glColor3ub 19,160,0        ' Зеленая лужайка
          PutBox (1, 200, 640, 480, 1)
          
          glColor3ub 0,16,160        ' Водная поверхность
          PutBox (150, 230, 480, 480, 2)
          
          glColor3ub 255,255,255
          PutTexture (texture, x, y, 10) ' Рисунок
          ' Рисуем зеркало
          glDepthfunc (GL_EQUAL)     ' Тест глубины: Тот же уровень
          glEnable (GL_BLEND)
                         ' Наш режим смешивания для прозрачности
          glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
          
          glColor4f 1,1,1,.5         ' цвет измененной фигуры
          PutTextureVert (texture, x, y+texture.height, 2)
          
          If Multikey(SC_LEFT) Then x-=1  
          If Multikey(SC_RIGHT) Then x+=1
          If Multikey(SC_UP) Then y-=1
          If Multikey(SC_DOWN) Then y+=1
            
          glFlush 
          Flip 
          Screensync
        Loop Until Multikey(SC_ESCAPE) ' Выход
        

        screenss.jpg

        Управление:
        Стрелки - позволяют нам двигать лошадь
        Escape - выход
      • Списки отображения

        Теперь мы подошли к очень интересной технологии, которую предлагает нам OpenGL. Списки отображения это своеобразное "хранилище" записей наших команд. Данное "хранилище" закрепляется за определенным именем. Это очень удобно для объектов, которые имеют неизменяемые формы. Самое большое преимущество списков - это увеличение скорости при их использовании. Принцип основан на том, что все команды хранятся в памяти видеокарты, а значит доступ к ним максимально быстрый.

        Преимущества

        ---скорость

        Недостатки

        ---высокое потребление памяти
        ---подходит только для статических объектов

        Как использовать списки отображения?

        Списки отображения очень просты в применении. Надо использовать следующие команды:
        Dim GL_list As uninteger    ' хендл (имя) нашего списка
        glNewList GL_list, GL_COMPILE   ' Начать новый список(начать запись)
        
        '' OpenGL команды ''
        
        glEndList           ' Конец записи
        glCallList GL_list ' Вызов списка

        glNewList имеет следующие параметры:
        • Gl_list - Целое число, являющееся именем дисплейного списка.
        • Режим компиляции. Возможны следующие значения:
          • GL_COMPILE - Команды просто компилируются.
          • GL_COMPILE_AND_EXECUTE - Команды выполняются по мере компилирования их в дисплейный список.

        Какой код записывать в списки это личное дело каждого. Я например использую определенный код для паузы в игре...
      • Шрифт

        До сих пор мы не могли использовать шрифты в OpenGL. Нам нужно придумать что-то , чтобы закрыть этот пробел. Я могу вам показать один из способов, который не является совершенным, но довольно прост в применении. Для начала определим две структуры:
        Type FontChar
            Width As Integer
            Height As Integer
            Left As Integer
            Right As Integer
        End Type
        
        Type FontSystem
            Char(0 To 255) As FontChar
            listbase As Uinteger
            texture As Uinteger
            Spacing As Integer
            Fixed As Ubyte
        End Type

        Инициализировать шрифт можно так:
        Dim MyFont As FontSystem
        Далее загрузка нужного материала, вынесенная в процедуру
        Загрузка наборного материала
        Чтобы загружать наборный материал требуется 2 файла. Один графическое изображение шрифта. Второй, содержит ширину, высоту и расстояния для каждой буквы. Графический файл имеет размер 256x256 пикселей. В нем имеется 256 символов, которые в отдельности имеют величину 16x16 пиксел.
        Sub FontLoad (Byref Font As FontSystem,
                Byval SourceFile As String,
                Byval DataFile As String)

        Пример файла:
        font_stb.jpg

        Конечно вы можете создать свой, данные корректируются...

        Вывод текста
        Для этого имеются 2 пути. Берете glPrint, чтобы выводить уже готовый текст. FontPrint немного отличается в параметрах и оставляет свободу действий для изменений относительно прозрачности, теста глубины, цвета, и т. д.
        Sub glPrint (Byref Font As FontSystem,
                Byval text As String,
                Byval x As Integer,
                Byval y As Integer,
                Byval z As Integer,
                Byval R As Integer,
                Byval G As Integer,
                Byval B As Integer,
                Byval A As Integer = 255)
        
        Sub FontPrint (Byref Font As FontSystem,
                Byval Text As String,
                Byval X As Integer,
                Byval Y As Integer,
                Byval Z As Integer = 0,
                Byval scale As Single = 1)

        Определение ширины шрифта
        В некоторых случаях, необходимо определение точной ширины текста в пикселях, например для вывода его по центру.
        Function FontGetWidth (Byref Font As FontSystem,
                Byval Text As String) As Integer
        Удаление шрифта
        Sub DestroyFont (Byref Font As FontSystem)

        Теперь у нас есть самое необходимое для работы и для наглядности пример:
        '
        ' Written by Paul Grunewald (aka Stormy)
        ' Date: 07.28.2006
        ' Mail: Paul.Grunewald@gmail.com
        '
        #INCLUDE "inc/2d_opengl.bi"
        
        ' Константы
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полноэкранный режим ( &h0 = нет, &h1 = да )
        
        ' Инициализация OpenGL
        Init_ogl2D(scrnX, scrnY, depth, fullscreen)
        
        Windowtitle "[ Font ]"
        
        Dim MyFont As FontSystem
        MyFont.Spacing = 2
        FontLoad(MyFont, "resources/examples/standard.tga","resources/examples/standard.dat")
        
        Type BouncingText
            As String Text
            As Integer R, G, B
            As Integer posX, posY
            As Integer dirX, dirY
            As Integer w
        End Type
        
        Dim MyText(1 To 5) As BouncingText
        Dim As Integer e, x, y, r
        
        Randomize Timer
        
        For e = 1 To 5
            With MyText(e)
                .text = "Example text #" + Str$(e)
                .w = FontGetWidth(MyFont, .text)
                .posX = Int(Rnd * scrnX - .w) + 1
                .posY = Int(Rnd * scrnY - 16) + 1
                .dirX = Int(Rnd * 6) - 3
                .dirY = Int(Rnd * 6) - 3
                .R = Int(Rnd * 128) + 128
                .G = Int(Rnd * 128) + 128
                .B = Int(Rnd * 128) + 128
            End With
        Next e
        
        glEnable (GL_BLEND)
        
        Do
            glCls
            For x = 0 To Fix(scrnX / 16)
                For y = 0 To Fix(scrnY / 16)
                    r = Int(Rnd * 50)+40
                    glPrint (MyFont, Str$(Int(Rnd * 2)), x*16,y*16,0,r,r,r)
                Next y
            Next x
            For e = 1 To 5
                With MyText(e)
                    glPrint (MyFont, .text, .posX, .posY, 1, .R, .G, .B)
                    If .posX + .w >= scrnX Then .dirX = .dirX*-1
                    If .posY + 16 >= scrnY Then .dirY = .dirY*-1
                    If .posX <= 0 Then .dirX = .dirX*-1
                    If .posY <= 0 Then .dirY = .dirY*-1
                    .posX += .dirX
                    .posY += .dirY
        
                    If Multikey(SC_R) Then
                        .posX = Int(Rnd * scrnX - .w) + 1
                        .posY = Int(Rnd * scrnY - 16) + 1
                        .dirX = Int(Rnd * 6) - 3
                        .dirY = Int(Rnd * 6) - 3
                        .R = Int(Rnd * 128) + 128
                        .G = Int(Rnd * 128) + 128
                        .B = Int(Rnd * 128) + 128
                    End If
        
                End With
            Next e
            glFlush
            Flip
            Screensync
        Loop Until Multikey(SC_ESCAPE) ' Выход
        
        DestroyFont(MyFont)
        

        Скриншот:

        screenst.jpg
        В этом примере пять строк летают по экрану

        Управление:
        R - сброс всех текстовых элементов и цветов
        Escape - выход
      • Источники света

        Теперь мы попробуем создавать реалистичные эффекты освещения. Возьмем простую черно-белую текстуру, разукрасим своим цветом и таким образом создадим наше собственное освещение. И так наша текстура:

        light001.jpg

        Мы будем применять эту текстуру с тестом глубины glDepthFunc (GL_ALWAYS) и режимом смешивания glBlendFunc (GL_SRC_ALPHA, GL_DST_ALPHA).

        Пример: (example_light.bas)
        '
        ' Written by Paul Grunewald (aka Stormy)
        ' Date: 07.28.2006
        ' Mail: Paul.Grunewald@gmail.com
        '
        #INCLUDE "inc/2d_opengl.bi"
        
        ' Константы
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полноэкранный режим ( &h0 = нет, &h1 = да )
        
        ' Инициализация OpenGL
        Init_ogl2D(scrnX, scrnY, depth, fullscreen)
        
        Windowtitle "[ Light ]"
        
        Type Light
            As Integer R, G, B, A
            As Single X, Y, dirX, dirY
            As Integer w
        End Type
        
        Dim MyLight(1 To 5) As Light
        Dim As Integer e
        
        Randomize Timer
        
        For e = 1 To 5
            With MyLight(e)
                .w = Int(Rnd * 256) + 128
                .X = Int(Rnd * scrnX) + 1
                .Y = Int(Rnd * scrnY) + 1
                .R = Int(Rnd * 128) + 128
                .G = Int(Rnd * 128) + 128
                .B = Int(Rnd * 128) + 128
                .A = Int(Rnd * 128) + 128
                .dirX = Int(Rnd * 6) - 3
                .dirY = Int(Rnd * 6) - 3
            End With
        Next e
        
        Dim As GFXType light_texture, background_texture
        Dim As Integer x, y
        
        glEnable GL_TEXTURE_2D                       ' Позволение текстуры.
        load_texture ("resources/examples/light.tga", light_texture)     ' Загрузка текстуры для света
        load_texture ("resources/examples/brick.tga", background_texture)' Загрузка текстуры для фона
        
        glEnable GL_BLEND
        
        Do
            glCls
            glColor4f .5,.5,.5,1
            glDepthFunc (GL_LESS)
            glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        
            For x = 0 To Fix(scrnX / background_texture.width)
                For y = 0 To Fix(scrnY / background_texture.height)
                    PutTexture (background_texture,_
                    x * background_texture.width,_
                    y * background_texture.height, 0)
                Next y
            Next x
        
            glDepthFunc (GL_ALWAYS)
            glBlendFunc (GL_SRC_ALPHA, GL_DST_ALPHA)
            For e = 1 To 5
                With MyLight(e)
                    glColor4ub .R, .G, .B, 255
                    glBindTexture GL_TEXTURE_2D, light_texture.handle
                    DrawTexQuad (.X, .Y, 1, .w, .w)
        
                    If .X + .w/2 >= scrnX Then .dirX = .dirX*-1
                    If .Y + .w/2 >= scrnY Then .dirY = .dirY*-1
                    If .X + .w/2 <= 0 Then .dirX = .dirX*-1
                    If .Y + .w/2 <= 0 Then .dirY = .dirY*-1
                    .X += .dirX
                    .Y += .dirY
        
                    If Multikey(SC_R) Then
                        .X = Int(Rnd * scrnX) + 1
                        .Y = Int(Rnd * scrnY) + 1
                        .R = Int(Rnd * 128) + 128
                        .G = Int(Rnd * 128) + 128
                        .B = Int(Rnd * 128) + 128
                        .A = Int(Rnd * 128) + 128
                        .w = Int(Rnd * 256) + 128
                        .dirX = Int(Rnd * 6) - 3
                        .dirY = Int(Rnd * 6) - 3
                    End If
        
                End With
            Next e
            glFlush
            Flip
            Screensync
        Loop Until Multikey(SC_ESCAPE) ' Выход
        

        Скриншот:
        screensu.jpg
        Объекты летают по экрану

        Управление:
        R кнопки - сброс всех позиций света и цвета
        Escape - выход

        Собственно, создается впечатление, что в примере просто делается фон цветным. Это мало отражает насущную необходимость освещения. В следующем примере сделаем освещение действительно нужным :)

        Пример: (example_light2.bas):
        '
        ' Written by Paul Grunewald (aka Stormy)
        ' Date: 07.28.2006
        ' Mail: Paul.Grunewald@gmail.com
        '
        #INCLUDE "inc/2d_opengl.bi"
        
        ' Константы
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полноэкранный режим ( &h0 = нет, &h1 = да )
        
        ' Инициализация OpenGL
        Init_ogl2D(scrnX, scrnY, depth, fullscreen)
        
        Windowtitle "[ Light #2 ]"
        
        Type Light
            As Integer R, G, B, A
            As Single X, Y, dirX, dirY
            As Integer w
        
        End Type
        
        Dim MyLight(1 To 5) As Light
        Dim As Integer e
        
        Randomize Timer
        
        For e = 1 To 5
            With MyLight(e)
                .w = Int(Rnd * 256) + 128
                .X = Int(Rnd * scrnX) + 1
                .Y = Int(Rnd * scrnY) + 1
                .R = Int(Rnd * 128) + 128
                .G = Int(Rnd * 128) + 128
                .B = Int(Rnd * 128) + 128
                .A = Int(Rnd * 128) + 128
                .dirX = Int(Rnd * 6) - 3
                .dirY = Int(Rnd * 6) - 3
            End With
        Next e
        
        Dim As GFXType light_texture, background_texture
        Dim As Integer x, y
        
        glEnable GL_TEXTURE_2D                       ' Позволение текстуры.
        load_texture ("resources/examples/light.tga", light_texture)     ' Загрузка текстуры для света
        load_texture ("resources/examples/brick.tga", background_texture)' Загрузка текстуры для фона
        
        glEnable GL_BLEND
        
        Do
            glCls
            glColor4f .5,.5,.5,1
            glDepthFunc (GL_LESS)
            glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        
            For x = 0 To Fix(scrnX / background_texture.width)
                For y = 0 To Fix(scrnY / background_texture.height)
                    PutTexture (background_texture,_
                    x * background_texture.width,_
                    y * background_texture.height, 0)
                Next y
            Next x
        
            glDepthFunc (GL_ALWAYS)
            glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA)
            For e = 1 To 5
                With MyLight(e)
                    glColor4ub .R, .G, .B, 255
                    glBindTexture GL_TEXTURE_2D, light_texture.handle
                    DrawTexQuad (.X, .Y, 2, .w, .w)
        
                    If .X + .w/2 >= scrnX Then .dirX = .dirX*-1
                    If .Y + .w/2 >= scrnY Then .dirY = .dirY*-1
                    If .X + .w/2 <= 0 Then .dirX = .dirX*-1
                    If .Y + .w/2 <= 0 Then .dirY = .dirY*-1
                    .X += .dirX
                    .Y += .dirY
        
                    If Multikey(SC_R) Then
                        .X = Int(Rnd * scrnX) + 1
                        .Y = Int(Rnd * scrnY) + 1
                        .R = Int(Rnd * 128) + 128
                        .G = Int(Rnd * 128) + 128
                        .B = Int(Rnd * 128) + 128
                        .A = Int(Rnd * 128) + 128
                        .w = Int(Rnd * 256) + 128
                        .dirX = Int(Rnd * 6) - 3
                        .dirY = Int(Rnd * 6) - 3
                    End If
        
                End With
            Next e
        
            glDepthFunc(GL_LESS)
            glBlendFunc GL_DST_COLOR, GL_DST_COLOR
            glColor4f 0,0,0,1:
            glBindTexture GL_TEXTURE_2D, 0
            PutBox (0,0,scrnX,scrnY,3)
        
            glFlush
            Flip
            Screensync
        Loop Until Multikey(SC_ESCAPE) ' Выход
        

        Скриншот:
        screensv.jpg

        Управление:
        R - сброс всех позиций света и цвета
        Escape - выход
      • Тень

        Поскольку этот учебник для начинающих , мы не будем затрагивать сложные эффекты затенения. Если это будет сильно необходимо, в интернете найдете немало ссылок на эту тематику. Но вернемся к нашей реализации теней в 2D-мире.

        Как мы можем реализовать 2D тень?

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

        Пример (example_shadow.bas):
        '
        ' Written by Paul Grunewald (aka Stormy)
        ' Date: 07.28.2006
        ' Mail: Paul.Grunewald@gmail.com
        '
        #INCLUDE "inc/2d_opengl.bi"
        
        ' Константы
        Const scrnX = 640
        Const scrnY = 480
        Const depth = 32
        Const fullscreen = &h0           ' Полноэкранный режим ( &h0 = нет, &h1 = да )
        
        Dim As GFXType texture, background_texture
        
        'Инициализация
        Init_ogl2D(scrnX, scrnY, depth, fullscreen)
        
        glEnable GL_TEXTURE_2D                      ' Позволение текстуры
        load_texture ("resources/examples/horse.tga", texture)      ' Лошадь
        load_texture ("resources/examples/grass.tga", background_texture)' Фон
        glEnable GL_BLEND                       ' Blending erlauben
        
        Windowtitle "[ Shadow ]"
        
        Dim As Integer x, y, bx, by
        x = 150
        y = 0
        
        Do
            glCls
        
            ' Изображение фона
        
            For bx = 0 To Fix(scrnX / background_texture.width)
                For by = 0 To Fix(scrnY / background_texture.height)
                    PutTexture (background_texture,_
                    bx * background_texture.width,_
                    by * background_texture.height, 0)
                Next by
            Next bx
        
            ' Тень
            glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
            glColor4ub 0,0,0,128
            glBindTexture GL_TEXTURE_2D, texture.handle
            DrawTexQuad (x+5, y+5, 2, texture.width, texture.height)
        
            glColor3ub 255,255,255
            glBindTexture GL_TEXTURE_2D, texture.handle
            DrawTexQuad (x, y, 3, texture.width, texture.height)
        
            'PutTexture (texture, x, y, 10) ' Figur
        
            If Multikey(SC_LEFT) Then x-=1
            If Multikey(SC_RIGHT) Then x+=1
            If Multikey(SC_UP) Then y-=1
            If Multikey(SC_DOWN) Then y+=1
        
            glFlush
            Flip
            Screensync
        Loop Until Multikey(SC_ESCAPE) ' выход
        

        Скриншот:
        screensw.jpg

        Управление:
        Стрелки - перемещение лошади
        Escape - выход

        Вид на высоте:
        К примеру вы хотите установить тень для космического корабля, который отбрасывает ее на большую высоту. Все что вам нужно, это увеличить расстояние до тени и конечно уменьшить ее размеры. В рисунке ниже продемострирован другой угол падения света.

        shadow01.jpg

    7. Заключение

      Это окончание! Поздравляем! :-) Я надеюсь, что я помог вам хоть чем-то в этом учебнике. Я попытался дать немного понимания в мире OpenGL. Когда вы углубитесь в OpenGL, вы сможете обнаружить много более интересных вещей для себя. В реале их гораздо больше, чем показано здесь. Не останавливайтесь на этом уроке, интернет предлагает множество инструкций, описаний, вики и др., которые помогут вам в дальнейшем обучении. Это руководство должно стать только началом для Вас!
      Stormy aka Paul Grunewald
      Официальный сайт учебника
      Перевод: Станислав Будинов