2D-приложения OpenGL и FreeBasic(перевод с нем.)
Содержание:
- Предисловие
- Уважаемые любознательные программисты!
Я написал небольшой учебник о том, как использовать OpenGl на первых порах в FreeBasic. Я хочу предложить свою помощь, чтобы у вас было с чего стартовать , используя эту великолепную API. Я постараюсь избавить вас от скучной теории и буду упираться непосредственно на важные вещи, которые имеют отношение к практике.Stormy aka Paul Grunewald
- Уважаемые любознательные программисты!
- 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-программиста (не волнуйтесь, все возможно!)
- Корректировка графики (см. раздел: текстуры)
- Общее
- Первые шаги с 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 выше , чем в оконном режиме.
glMatrixMode (GL_PROJECTION) glLoadIdentity
В OpenGL существуют различные матрицы, которые используются для представления объектов. В нашем случае (2D), мы используем так называемую проекционную матрицу. Если мы будем работать с 3D-объектами, эта матрица будет искажать объекты, беря их под косым углом просмотра. Однако, нас сейчас это мало интересует, потому что мы имеем дело только с 2D. :-) GlLoadIdentity используется для считывания текущей матрицы. Это, конечно, звучит сложновато, но со временем станет понятно. В отличие от QuickBasic или FreeBasic оси координат в OpenGL берут отсчет не в верхнем левом углу, а в нижнем левом углу. Эскиз экрана ниже с размерами 800x600, проиллюстрирует это:
С помощью команд ниже, мы указываем свою систему координат, чтобы она адаптировалась к системе координат 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. Ниже две иллюстрации:
Квадрат в верхнем Порядок вывода координат
левом углу
Перемещение объектов:
Для смещения объектов например по оси х + 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
Конечно в таком виде Вот так кажется лучше,
не так очевидно наложение, но работу с цветом разберем
Гораздо лучше бы выглядело чуть ниже
если квадраты были разного
цвета
Функция 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)
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
Взгляните на скриншот ниже, и будет наверное все ясно:
Можно ясно увидеть,
из каких точках выходят цвета.
Небольшой совет: Если вы хотите использовать альфа-канал, необходимо активировать специальный режим смешивания в итоге точки будут появляться частично прозрачными. Но об этом посвящена глава смешивание (ниже).
- Интеграция OpenGl графики для ОС Windows
и Linux
- Текстуры
- Общая информация
Если посмотреть с практической стороны, то текстуры являются обычными изображениями, которые "помещают" на объект. Техника обращений с текстурами будет чем-то схожа с техникой обращений изображений в FreeBasic посредством PUT. Тем не менее, есть некоторые ограничения. Текстуры могут быть только квадратны и размеры текстуры как по горизонтали, так и по вертикали должны представлять собой степени двойки. Примерами могут служить: 32x32, 64x64, 128x128, 256x256, и т. д.
Пример:
Текстура-трава 128x128
Текстура-галька 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:
- TEX_MIPMAP - являются готовыми, масштабными схемами, которые
используются, когда объект уменьшается. С помощью этого флага у вас есть
преимущество в скорости и графика выглядит более гладкой.
Пример изображения без этого флага и с ним:
- Killdata означает, что вы можете удалить временный буфер данных, который содержит данные о точках. Он может быть получен если нужны будут изменения в буфере. Если он не установлен, у вас больше памяти. (0 = очистить буфер, 1 = не очищать приемный буфер)
- TEX_NOFILTER - Эта опция влияет на так называемый Magfilter
("Увеличение фильтра"). Мы говорим об увеличении, когда объект больше,
чем его собственная текстура - только тогда этот фильтр активен. Если вы
передаете в флаг константу TEX_NOFILTER, GL_NEAREST активизируется, что
является достаточным для быстрого отображения текстур. Если не
передается, то GL_LINEAR включен, это означает, что графика «мягкая».
#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).
Это изображение показывает, как координаты расположены:
Таким образом, мы можем определить, какая часть текстуры нам необходима. К примеру, у нас есть графический файл, который хранит 10 кадров вращающегося астероида, мы можем выбрать один кадр...
В изображении выше мы берем часть с координатами (0,0), (1,0), (0,1) и (1,1)!
А в этом изображении наглядно видно как берется часть изображения "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 ' Освободить текстуру
В результате:
Размер прямоугольника(объекта) 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, то текстура заливает объект с повторами. Предположим, вместо единицы указываем двойку. Результат на рисунке:
И теперь последний совет для этой главы: никто не мешает вам раскрашивать текстуры , используя функцию glColor. Можете поэксперементировать с этим сами. Удачи! ;)
- Общая информация
- Смешивание
- Что это такое?
Большинство специальных эффектов в 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)
Используя данный метод можно рисовать текстуры, используя альфа-канал.
Возьмем в качестве фона текстуру № 1
Текстуру № 2 положим на первую.
С нашим режимом смешивания, мы можем хранить эти текстуры без каких-либо проблем.
Можно вывести со значением альфа-канала:
glColor4f 255, 255, 255, 128 -
Практическое применение и примеры
Я разработал небольшую программу, которая может послужить вам для экспериментов. Это как раз то, что я писал выше о вопросе смешивания на практике. СКАЧАТЬ: blending.zip
Скриншот:
Управление:
Стрелки - переключения на различные опции
1, 2, 3 - переход между текстурами
Enter - изменение значения
ESC - выход
B - BlendMode вкл / выкл
D - тест глубины вкл / выкл
- Что это такое?
- Полезные методы
Примечание:
Теперь следует небольшая коллекция полезных методов. Многие вещи взяты из той или иной игры. Ранее я писал без использования 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) ' Выход
Управление:
Стрелка вверх - увеличение объекта
Стрелка вниз - уменьшение объекта
Стрелка вправо- вращение вправо
Стрелка влево - вращение влево
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 (тайлмэп или карта из тайлов):
Этот Таймэп взят от одной из моих игр. Тайл имеет размер 32х32 пикселей.Текстура имеет размеры 256x256 пикселей. В результате этого в одной текстуре у меня помещается 64 тайла (8 колонок по 8 тайлов в каждой). Чтобы вычислить число возможных плиток в колонке, да и самих колонок, разделите длину текстуры на длину плитки (256/32 = 8).
Здесь все тайлы пронумерованы. Таким образом, вы можете определить для каждого тайла свой уникальный номер. В настоящее время проблема может заключаться в вычислении координат текстуры для тайлов относительно своего уникального числа.
Проблема:
К сожалению, мы не можем обращаться к текстуре с помощью единиц измерения пиксель. Координаты указываются в пределах области от 0 до 1. Это становится проблематичным, особенно если мы хотим делать с точностью до пикселя. Однако мы может обойти эту трудность. Ниже вы видите еще раз расположение наших координат текстуры. Глядя на это, мы можем рассчитать наши координаты для наших тайлов.
Решение:
Эту проблему мы решим с помощью математического подхода , учитывая:
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) ' Выход
Естественно, каждый может по своему усмотрению свободно расширять (изменять) эту технику (например, многослойные тайлы...).
- Отсечение
Под отсечением определяют представление частичных частей графических объектов. В том числе это называется визуализацией . Примерно так же как Tilemap, но на этот раз тайл должен быть не прямоугольной формы, а с переменной длиной и шириной.
Предположим для игры есть поверхность, в которой слева-внизу отображение стабильности корабля и рядом чуть правее отображается еще какой нибудь потенциал. А слева-сверху отображение кол-ва ракет. Для реализации , мы создадим большой "файл-контейнер" и в него вставим наши отдельные изображения.
Пример
Как вы можете видеть, в отличие от 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) ' Выход
Управление:
Стрелка вверх - добавить ракету
Стрелка вниз - снять ракету
Стрелка вправо - ремонт судна
Стрелка влево - повреждение судна
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) ' Выход
Управление:
Стрелки - позволяют нам двигать лошадь
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)
Пример файла:
Конечно вы можете создать свой, данные корректируются...
Вывод текста
Для этого имеются 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)
Скриншот:
В этом примере пять строк летают по экрану
Управление:
R - сброс всех текстовых элементов и цветов
Escape - выход
- Источники света
Теперь мы попробуем создавать реалистичные эффекты освещения. Возьмем простую черно-белую текстуру, разукрасим своим цветом и таким образом создадим наше собственное освещение. И так наша текстура:
Мы будем применять эту текстуру с тестом глубины 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) ' Выход
Скриншот:
Объекты летают по экрану
Управление:
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) ' Выход
Скриншот:
Управление:
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) ' выход
Скриншот:
Управление:
Стрелки - перемещение лошади
Escape - выход
Вид на высоте:
К примеру вы хотите установить тень для космического корабля, который отбрасывает ее на большую высоту. Все что вам нужно, это увеличить расстояние до тени и конечно уменьшить ее размеры. В рисунке ниже продемострирован другой угол падения света.
- Масштабирование и
вращение
- Заключение
Это окончание! Поздравляем! :-) Я надеюсь, что я помог вам хоть чем-то в этом учебнике. Я попытался дать немного понимания в мире OpenGL. Когда вы углубитесь в OpenGL, вы сможете обнаружить много более интересных вещей для себя. В реале их гораздо больше, чем показано здесь. Не останавливайтесь на этом уроке, интернет предлагает множество инструкций, описаний, вики и др., которые помогут вам в дальнейшем обучении. Это руководство должно стать только началом для Вас!