OpenGL для начинающих

== Часть I ==
=== 00 Введение ===
Решив разобраться с OpenGL и просмотрев DRBK, я увидел печальную картину - то что там есть никак не дает пинка в нужном направлении для написания программ, использующих OpenGL. Ниже я постарался собрать в пучок результаты моих исследований и получил этот текст и MyOpenGL.pas. 
Я совсем не претендую на правильность терминов в описании и на то, как надо писать OpenGL-программы. Но простую OpenGL-программу вы точно сможете написать, 
использовав MyOpenGL.pas, после прочтения нижеследующего, как на VCL, так и на чистом WinAPI.
Может кому пригодится для начала - написанное достаточно просто >:-)
Были использованы следующие источники:
* Исходники Jan Horn (http://www.sulaco.co.za, http://home.global.co.za/~jhorn) В частности Skyboxes и Гаусово размытие. Второе уже есть в DRKB без имени автора Работа с графикой и мультимедиа > DerectX, OpenGL > OpenGL - радиальное размытие (названия функций, основа программы и много другое взяты оттуда).
* Учебник по OpenGL (главы 1.3, 1.5) с www.ru-coding.com.
* MS SDK > OpenGL Programmer's Reference, поставляемый вместе с Delphi (общая справка)
* модуль EasyGL.pas Данилова Андрея (http://dasoft.land.ru) (своровал пару идей по оформлению VCL).
* Статья по рисованию вращающего сердечка на OpenGL (не помню где брал).
* Перевод Народного учебника по OpenGL от NeHe (http://nehe.gamedev.net). Брать тут - http://pmg.org.ru/nehe/nehehtml.zip
(ну очень хороший учебник, хоть и на C; must see).
=== 01 Контексты устройства и воспроизведения ===
"DC (Device Context - контекст устройства). Это то, на чём мы рисуем, и в Delphi контекст устройства представлен как TCanvas" (см. 1) Получить его можно следующим образом:
var
 DC: hDC;
begin
 DC := GetDC(Form1.Hanle);
 // или
 DC := Form1.Canvas.Handle;
"Графическая система OpenGL, как и любое другое приложение Windows, также нуждается в ссылке на окно, на котором будет осуществляться воспроизведение - специальной ссылке на контекст воспроизведения - величина типа HGLRC (Handle openGL Rendering Context, ссылка на контекст воспроизведения OpenGL). Для получения этого контекста OpenGL нуждается в величине типа HDC (контекст воспроизведения) окна, на который будет осуществляться вывод." Создать его можно следующим образом:
uses OpenGL;
var
 DC: hDC;
 RC: hGLRC;
begin
 DC := GetDC(Form1.Handle); // получаем контекст устройства
 SetDCPixelFormat(DC); // устанавливаем формат точки (см. 2)
 RC := wglCreateContext(DC); // создать новый контекст воспроизведения
 wglMakeCurrent(DC, RC); // устанавливаем его текущим
Здесь SetDCPixelFormat(DC) самописная функция, код см. в MyOpenGL.pas.
Дополнительно:
# GDI - графика в Delphi
# Работа с OpenGL - Введение
# Работа с OpenGL - Минимальная программа
=== 02 Инициализация и завершение работы с OpenGL ===
Поскольку часть операций при инициализации зависит от размеров окна и должна повторяться при каждом изменении размеров окна, то эта часть вынесена в отдельную процедуру, которая выполняется при инициализации и при изменении размеров окна.
{------------------------------------------------------------------}
{ Инициализация OpenGL }
{------------------------------------------------------------------}
procedure glInit(Wnd: hWND);
const
 // Константы, задающие свойства материала фигур
 mat1_amb : array [0..2] of Single = (0.2, 0.2, 0.2);
 mat1_dif : array [0..2] of Single = (0.8, 0.8, 0.0);
 mat1_spec: array [0..2] of Single = (0.6, 0.6, 0.6);
 mat1_shininess = 10;
 // Константы для источника света
 light_pos : array [0..3] of glFloat=(100.0, 100.0, 0.0, 1.0);
 light_amb : array [0..3] of glFloat=(0.6, 0.6, 0.6, 1.0);
 light_dif : array [0..3] of glFloat=(1.0, 1.0, 1.0, 1.0);
 light_spec : array [0..3] of glFloat=(1.0, 1.0, 1.0, 1.0);
 light_spot_direction : array [0..3] of glFloat=(1.0, 1.0, 1.0, 1.0);
 // Цвет тумана
 fogColor: array [0..3] of GLfloat = (0, 1.0, 0, 1.0);
var
 fogMode: GLint;
begin
 // Инициализация контекста воспроизведения OpenGL
 DC := GetDC(Wnd); // Получить контекст устройства для окна
 SetDCPixelFormat(DC); // Установить формата пикселов
 RC := wglCreateContext(DC); // создать новый контекст воспроизведения
 wglMakeCurrent(DC, RC); // Установить его текущим
 // Свойства материала для режима glEnable(GL_COLOR_MATERIAL)
 glMaterialfv(GL_FRONT, GL_AMBIENT, @mat1_amb);
 glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat1_dif);
 glMaterialfv(GL_FRONT, GL_SPECULAR, @mat1_spec);
 glMaterialf (GL_FRONT, GL_SHININESS, mat1_shininess);
 // Свойства источника света GL_LIGHT1 для режима glEnable(GL_LIGHTING)
 // Стандартный источник одна команда: glEnable(GL_LIGHT0);
 glLightfv(GL_LIGHT1, GL_POSITION, @light_pos);
 glLightfv(GL_LIGHT1, GL_AMBIENT, @light_amb); // направленность
 glLightfv(GL_LIGHT1, GL_DIFFUSE, @light_dif); // рассеивание
 glLightfv(GL_LIGHT1, GL_SPECULAR, @light_spec);
 glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, @light_spot_direction);
 // разрешить источник света GL_LIGHT1
 glEnable(GL_LIGHT1);
 // Свойства тумана для режима glEnable(GL_FOG)
 fogMode := GL_EXP; // GL_EXP2, GL_LINEAR
 glFogi(GL_FOG_MODE, fogMode); // установить режим
 glFogfv(GL_FOG_COLOR, @fogColor); // цвет тумана
 glFogf(GL_FOG_DENSITY, 0.002); // плотность - 0.2%
 glHint(GL_FOG_HINT, GL_DONT_CARE); // GL_NICEST, GL_FASTEST.
 glClearColor(0.0, 0.0, 0.2, 1.0); // Установить цвет фона - синий
 glClearDepth(1.0); // Очистить буфер глубины
 glDepthFunc(GL_LESS); // Тип теста глубины
 glShadeModel(GL_SMOOTH); // плавное цветовое сглаживание
 // glEnable(..) - включить режим, glDisable(..) - отключить
 glEnable(GL_LIGHTING); // разрешить освещённость
 //glEnable(GL_FOG); // разрешить тумана
 glEnable(GL_DEPTH_TEST); // разрешить тест глубины, с использованием
 // функции определенной в glDepthFunc
 glEnable(GL_NORMALIZE); // разрешить нормали (различение передней
 // и задней сторон плоских объектов)
 glEnable(GL_COLOR_MATERIAL); // разрешить использование материалов на объектах
 glEnable(GL_TEXTURE_2D); // разрешить наложение текстур
 //glEnable(GL_BLEND); // разрешить смешивание (напр. прозрачность)
 // не совместимо с glEnable(GL_DEPTH_TEST)
 glLoadTexture('1.bmp', Texture); // загружаем текстуру
end;
{------------------------------------------------------------------}
{ Обработчик на изменение размеров окна }
{------------------------------------------------------------------}
procedure glOnResize(Width, Height: Integer);
begin
 if (Height = 0) then Height := 1; // Предупреждаем деление на 0
 glViewport(0, 0, Width, Height); // Устанавливаем область отображения
 // на все окно
 // Настройка матрицы проекции
 glMatrixMode(GL_PROJECTION); // Выбираем матрицу проекции
 glLoadIdentity(); // Устанавливаем ее единичной
 // Устанавливаем тип проекции - Ортогональный
 glOrtho(-Width div 2, Width div 2, -Height div 2, Height div 2, -800, 800);
 // Настройка видовой матрицы
 glMatrixMode(GL_MODELVIEW); // Выбираем матрицу проекции
 glLoadIdentity(); // Устанавливаем ее единичной
end;
Инициализация OpenGL производится последовательным вызовом этих двух процедур - сначала gLInit (в ее начале происходит создание контекста воспроизведения, упомянутого в первой главе), а потом glOnResize. Отмечу, что включение/выключение режимов можно делать когда угодно, а не только при инициализации.
Завершение работы с OpenGL проводит функция glKill.
{------------------------------------------------------------------}
{ Завершение работы с OpenGL }
{------------------------------------------------------------------}
procedure glKill(Wnd: hWND);
begin
 wglMakeCurrent(DC, 0);
 wglDeleteContext(RC);
 ReleaseDC(Wnd, DC);
end;
=== 03 Создание и рисование примитивов ===
Ниже будет описан процесс отрисовки, использующий таймер. 
Сначала подготавливаем объекты сцены процедурой сферу и треугольник, а потом командой SwapBuffers(..) выводим сцену на экран. Для улучшения понимания здесь порезанный вариант MyOpenGL.glDraw.
{------------------------------------------------------------------}
{ Создание объектов под отрисовку }
{------------------------------------------------------------------}
procedure glDraw();
var
 Obj: GLUquadricObj;
begin
 // очистка Экрана и буфера глубины
 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 // Пример рисования разноцветного треугольника
 glBegin(GL_POLYGON);
 glColor(0.0, 0.5, 0.0, 0.0);
 glVertex3F(-50, -sqrt(3)*50, 0); // 1-я вершина
 glColor(0.5, 0.0, 0.0, 0.0);
 glVertex3F(-100, -sqrt(3)*100, 0); // 2-я вершина
 glColor(0.0, 0.0, 0.5, 0.0);
 glVertex3F(-125, -sqrt(3)*100, 0); // 3-я вершина
 glEnd;
 // Пример рисования сферы
 glColor(1.0, 1.0, 1.0, 0.5); // установить цвет объекта
 Obj := gluNewQuadric;
 gluSphere(Obj, 100, 25, 25); // создать сферу R = 100, детализация - 25х25
 // Выводим на экран подготовленную сцену
 SwapBuffers(DC);
end;
По таймеру выполняем
 glDraw();
gluNewQuadric используется для создания объемных фигур одной командой:
var
 Obj: GLUquadricObj;
begin
 Obj := gluNewQuadric;
 // Режимы отображения фигуры
 gluQuadricDrawStyle(Obj, GLU_FILL); // GLU_POINT, GLU_LINE, GLU_SILHOUETTE
 gluQuadricOrientation(Obj, GLU_INSIDE); // GLU_OUTSIDE
 gluQuadricNormals(Obj, GLU_SMOOTH); // - нормаль для каждой точки
 // GLU_FLAT - для сегмента
 // GLU_NONE - не строить нормалей
 gluQuadricTexture(Obj, GL_TRUE); // - разрешить наложение текущей текстуры
 // Создание фигуры
 gluSphere(Obj, 100, 25, 25); // сфера R = 100, детализация - 25х25
 // или
 gluCylinder(Obj, 10, 100, 150, 30, 1); // цилиндр - R0 = 10, R1 = 100, H = 150, детализация - 30 плоскостей
 // или
 gluDisk(Obj, 10, 100, 30, 1); // Диск - R0 = 10, R1 = 100, детализация - 100
 // или
 gluPartialDisk(Obj, 10, 100, 30, 100, 0, 120); // треть диска с параметрами как у верхнего
 // Освобождаем память
 gluDeleteQuadric(Obj);
Quadric-объекты являются надстройкой над конструкцией glBegin-glEnd.
Для glBegin определены следующие константы (подробнее см. SDK OpenGL):
* GL_POINTS - последовательность точек
* GL_LINES - линии (пара точек; пара точек) 
* GL_LINE_STRIP - ломаная (аналог последовательности lineto)
* GL_LINE_LOOP - замкнутая 
* GL_TRIANGLES - треугольник
* GL_TRIANGLE_STRIP - объединенная группа треугольников
* GL_TRIANGLE_FAN
* GL_QUADS - четырехугольник
* GL_QUAD_STRIP - объединенная группа четырехугольников
* GL_POLYGON - плоский полигон 
Так же стоит обратить внимание на glPolygonMode, позволяющий по различному отображать строимую фигуру (см. код для Quadric)
=== 04 Преобразования координат и проекции ===
Цитата
Q: Возникла такая ситуация. Есть несколько объектов. Hyжно повеpнyть некотоpые из них. Функция glRotate() повоpачивает всю сценy. Сyществyет ли функция, котоpая повоpачивает только некотоpые (не все) вершины?
"Для задания различных преобразований объектов сцены в OpenGL используются операции над матрицами, при этом различают три типа матриц: видовая, проекций и текстуры. Все они имеют размер 4x4"
В каждый момент можем работать только с одним из типов матрицы. Выбрать нужную можно следующим образом:
 // mode =
 // GL_MODELVIEW - выбрать видовую матрицу
 // GL_PROJECTION - выбрать матрицу проекций
 // GL_TEXTURE - выбрать матрицу текстур
 glMatrixMode(<mode>);
По умолчанию все матрицы нулевые, поэтому при инициализации их заполняем, разумеется выбрав сначала тип матрицы. Можно заполнить какой-то конкретной или просто единичной, как в MyOpenGL.glInit. (Единичная - это та, у которой на диагонали единицы, а остальные нули).
 // отличие glLoadMatrixf от glLoadMatrixg заключается в том, как будет
 // восприниматься входной массив, определяющий матрицу
 // так - <строка1>, <строка2>, <строка3>, <строка4>
 // или так - <столбец1>, <столбец2>, <столбец3>, <столбец4>
 glLoadMatrixf(<указатель на массив из 16 элементов типа double>);
 // или
 glLoadMatrixg(<указатель на массив из 16 элементов типа double>);
 // или
 glLoadIdentity(); // заполнение единичной
Часто нужно сохранить содержимое текущей матрицы для дальнейшего использования, для чего используют команды
 glPushMatrix(); // сохранить матрицу (в стеке)
 glPopMatrix(); // извлечь матрицу (из стека)
Вспоминаем, что такое стек :)
Для GL_MODELVIEW-матриц глубина стека не менее 32, для других типов не менее 2. 
Далее попробую объяснить для чего нужны различные матрицы и как их использовать. Итак, задача такова, что надо создать трехмерную фигуру, изменить ее и потом отобразить на плоскость окна. Реализация OpenGL такова, что при этом имеем четыре перехода из одних координат в другие.
 Координаты объекта (4D) => Видовые координаты (4D) => Усеченные координаты (4D) => Нормализованные координаты (3D) => Оконные координаты (2D)
(4D - это точка имеет 4 координаты, первые 3 - ее 3D координаты, последняя дополнительная составляющая).
Переход от координат объекта к видовым осуществляется при использовании GL_MODELVIEW матрицы. По видовым координатам вычисляются усеченные координаты, используя GL_PROJECTION матрицу (название усеченные координаты идет от того, что для любой точки фигуры (x1, x2, x3, x4), верно -1 <= x_i <= 1, то есть GL_PROJECTION матрица отображает фигуру в 4D-куб c длиной стороны 2 и центром в (0, 0, 0, 0)). Нормализованные координаты получаются из усеченных следующим преобразованием 
 (x1', x2', x3') = (x1/x4, x2/x4, x3/x4).
Размер окна, куда будет отображаться фигура заданная в нормализованных координатах, определяется функцией glViewPort. 
Теперь, думаю, код glOnResize достаточно понятен.
 // 1
 glViewport(0, 0, Width, Height); // Устанавливаем область отображения
 // на все окно
 // 2
 // Настройка матрицы проекции
 glMatrixMode(GL_PROJECTION); // Выбираем матрицу проекции
 glLoadIdentity(); // Устанавливаем ее единичной
 // Устанавливаем тип проекции - Ортогональный
 glOrtho(-Width div 2, Width div 2, -Height div 2, Height div 2, -800, 800);
 // 3
 // Настройка видовой матрицы
 glMatrixMode(GL_MODELVIEW); // Выбираем матрицу проекции
 glLoadIdentity(); // Устанавливаем ее единичной
Блоки 1, 2 и 3 можно менять местами. Единственное отличие, если поменять местами 2 и 3, то что текущая матрица будет не GL_MODELVIEW, а GL_PROJECTION.
Примечание: GL_PROJECTION матрицу лучше выбирать так, чтобы:
* GL_MODELVIEW была единичной;
* исходные размеры объекта были в пределах отрезка [-1; 1].
Цитата
A: Для того, чтобы пpименить pазные пpеобpазования к pазным объектам сцены, надо делать так:
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();
 glRotate, glScale, glTranslate // ... --- в общем, всё что нyжно...
Затем pисyешь объекты...
После того, как наpисовал, yказываешь новое пpеобpазование:
glLoadIdentity(); glRotate, glScale, glTranslate // ...
Снова pисyешь объекты. И так повтоpяешь, сколько надо.
А теперь все это так, как было бы в коде (напр. в MyOpenGL.glDraw)
 // Указываем, что работать будем с видовой матрицей
 // Если она и так была текущей, то можно не выполнять
 glMatrixMode(GL_MODELVIEW);
 // Сбрасываем до единичной
 // (Считаем, что GL_PROJECTION подобрана, как указано выше)
 glLoadIdenty();
 glTranslatef(100.0, 0.0, 0.0); // Добавим к матрице перенос на 100 вдоль ОХ
 glPushMatrix; // сохраняем матрицу в стек - она нам еще понадобится
 // Рисуем какую-нить фигуру1
 {...}
 glRotate(-90, 0.0 , 1.0, 0.0); // Поворот на -90 градусов вокруг оси OY
 glRotate(60, 1.0, 0.0, 0.0); // Поворот на 60 градусов вокруг оси OX.
 // Рисуем фигуру2..
 {...}
 glPopMatrix; // восстанавливаем матрицу из стека
 glTranslatef(0.0, 10.0, 0.0); // двигаем на 10 по оси OX
 // Рисуем фигуру3
 {...}
После этого имеем - фигура1 перенесена от центра на 100 по оси ОХ. Фигура2 имеет матрицу - соответствующую следующим последовательным действиям: перенос на 100, поворот, поворот. glPopMatrix - возвращает нас к исходной матрице, содержащей только перенос на 100, потому GL_MODELVIEW-матрица у фигуры3 - это перенос на 100 + перенос на 10 по оси ОY.
Вообще говоря, последовательность неких действий - это домножение исходной матрицы на матрицы, отвечающие этим действиям. Поскольку умножение матриц некоммуникативно (AB <> BA), то и при смене чередовании элементов последовательности, получим другой результат. 
Дополнительно - www.ru-coding.com/ogl_1_3.php
=== 04а GL_PROJECTION ===
Считаем, что GL_MODELVIEW матрица единичная.
Задание матрицы проекции, которое могло быть напр. в MyOpenGL.glOnResize
Перспективная проекция:
 glFrustum(-Width div 2, Width div 2, -Height div 2, Height div 2, 100, 300);
 glTranslatef(0, 0, -200);
Ортогональная проекция:
 glOrtho(-Width div 2, Width div 2, -Height div 2, Height div 2, -800, 800);
Примерный смысл для параметров команд glFrustum и glOrtho.
 
Первые четыре, расписанные как (left, top) и (right, bottom) определяют прямоугольник. Лучше представить его как окно, через которое мы смотрим на мир (т. е. на группу объектов, которые были получены при преобразовании их исходных координат их GL_MODELVIEW-матрицами). Чем оно больше (больше значения параметров), тем больше объекты (по размерам) можем видеть (по другому - как будто отходим назад от объектов). При симметричных координатах - смотрим на центр.
Если провести через все углы линии ортогональные прямоугольнику, то высекаемый ими объем будет тем, что мы видим в мире. Поскольку объекты в бесконечности никого не интересуют (напр. не видны из-за малого размера при перспективной проекции), то логично ввести плоскость, параллельную плоскости окна и если объект оказался за ней, то забыть о его существовании - это сильно упрощает OpenGL расчеты. Логично ввести такую же плоскость и для объектов вблизи (хотя бы из соображений симметрии ;-)), т. е. если объект ближе чем она, то он не выводится. Пятый и шестой параметры - как раз и задают расположение второй и первой плоскости: при перспективной проекции - как расстояние от окна до требуемых плоскостей (следствие: всегда пятый параметр меньше шестого и оба больше 0), при 
ортогональной - расстояние от начала координат (поскольку при ортогональной проекции нет искажении из-за того как далеко от нас находится центр, то можно представить его где-либо перед нами и соответственно задать координаты плоскостей отсечения).
Если мы рисуем объекты вокруг центра координат, напр. так создаются Quadric-объекты, то для перспективной проекции также надо добавить сдвиг, помещающий центр координат МЕЖДУ плоскостями отсечения (этот же сдвиг можно сделать и в GL_MODELVIEW матрице). Для ортогональной этого делать не надо - центр и так где-то перед нами.
Другой способ задавать перспективную проекцию - это воспользоваться gluPerspective.
;Примечание: чем меньше расстояние между плоскостями отсечения, тем быстрее все считается. 
=== Текструры ===
Текстуру сначала надо загрузить и получить ее идентификатор (хендл). Перед созданием объекта текстура выбирается по своему идентификатору (делается текущей) и автоматом прилепляется к объекту (GLUquadricObj).
uses
 OpenGL;
var
 Texture: glUint;
// функция для установки текущей текстуры по идентификатору texture
// в openGL.pas ее почему то нет :(, потому импортируем
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
// Где-то вначале программы, перед использованием glDraw
LoadTexture('1.bmp', Texture);
{------------------------------------------------------------------}
{ Создание объектов под отрисовку }
{------------------------------------------------------------------}
procedure glDraw();
var
 Obj : GLUquadricObj;
begin
 // 1-ый вариант
 glBindTexture(GL_TEXTURE_2D, Texture); // устанавливаем текущую текстуру Texture
 Obj := gluNewQuadric;
 gluQuadricTexture(Obj, GL_TRUE); // при создании объекта наложить текущую текстуру
 gluSphere(Obj, 100, 25, 25); // создать сферу R = 100, детализация - 25х25
 // или так
 // 2-ой вариант
 glBindTexture(GL_TEXTURE_2D, Texture); // устанавливаем текущую текстуру Texture
 // Рисуем фигуру, на которую наложится текстура при помощи glTexCoord2f
 glBegin(GL_QUADS);
 glNormal3f(0.0, 0.0, 1.0);
 // 1-я точка текстуры + 1-я точка объекта
 glTexCoord2f(0.0, 0.0);
 glVertex3f(-100.0, -100.0, 0.0);
 // 3-я точка текстуры + 2-я точка объекта
 glTexCoord2f(1.0, 0.0);
 glVertex3f( 100.0, -100.0, 0.0);
 // 3-я точка текстуры + 3-я точка объекта
 glTexCoord2f(1.0, 1.0);
 glVertex3f(100.0, 100.0, 0.0);
 // 1-я точка текстуры + 1-я точка объекта
 glTexCoord2f(0.0, 1.0);
 glVertex3f(-100.0, 100.0, 0.0);
 glEnd();
При этом должен быть разрешен режим наложения текстур (по умолчанию - отключен) 
 glEnable(GL_TEXTURE_2D);
;Примечание: Функция MyOpenGL.glLoadTexture поддерживает загрузку только 8 и 24-битных изображений. 
Дополнительно
* www.ru-coding.com/ogl_1_5.php
=== 06 Unit MyOpenGL.pas ===
unit MyOpenGL;
interface
uses windows, OpenGL;
procedure glOnResize(Width, Height: Integer);
procedure glInit(Wnd: hWND);
procedure glKill(Wnd: hWND);
procedure glDraw();
procedure glLoadTexture(Filename: String; var Texture: GLuint);
implementation
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
function gluBuild2DMipmaps(Target: GLenum; Components, Width, Height: GLint; Format, atype: GLenum; Data: Pointer): GLint; stdcall; external glu32;
procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external opengl32;
procedure SetDCPixelFormat(DC: hDC); forward;
var
 DC: hDC; // контекст устройства
 RC: hGLRC; // контекст воспроизведения
 Texture: GLuint; // хэндл текстуры
 Angle: Integer = 0; // угол поворота сферы
{------------------------------------------------------------------}
{ Обработчик на изменение размеров окна }
{------------------------------------------------------------------}
procedure glOnResize(Width, Height: Integer);
begin
 if (Height = 0) then Height := 1; // Предупреждаем деление на 0
 glViewport(0, 0, Width, Height); // Устанавливаем область отображения
 // на все окно
 // Настройка матрицы проекции
 glMatrixMode(GL_PROJECTION); // Выбираем матрицу проекции
 glLoadIdentity(); // Устанавливаем ее единичной
 // Устанавливаем тип проекции - Ортогональный
 glOrtho(-Width div 2, Width div 2, -Height div 2, Height div 2, -800, 800);
 // Настройка видовой матрицы
 glMatrixMode(GL_MODELVIEW); // Выбираем матрицу проекции
 glLoadIdentity(); // Устанавливаем ее единичной
end;
{------------------------------------------------------------------}
{ Инициализация OpenGL }
{------------------------------------------------------------------}
procedure glInit(Wnd: hWND);
const
 // Константы, задающие свойства материала фигур
 mat1_amb : array [0..2] of Single = (0.2, 0.2, 0.2);
 mat1_dif : array [0..2] of Single = (0.8, 0.8, 0.0);
 mat1_spec: array [0..2] of Single = (0.6, 0.6, 0.6);
 mat1_shininess = 10;
 // Константы для источника света
 light_pos : array [0..3] of glFloat = (100.0, 100.0, 0.0, 1.0);
 light_amb : array [0..3] of glFloat = (0.6, 0.6, 0.6, 1.0);
 light_dif : array [0..3] of glFloat = (1.0, 1.0, 1.0, 1.0);
 light_spec : array [0..3] of glFloat = (1.0, 1.0, 1.0, 1.0);
 light_spot_direction : array [0..3] of glFloat = (1.0, 1.0, 1.0, 1.0);
 // Цвет тумана
 fogColor: array [0..3] of GLfloat = (0, 1.0, 0, 1.0);
var
 fogMode: GLint;
begin
 // Инициализация контекста воспроизведения OpenGL
 DC := GetDC(Wnd); // Получить контекст устройства для окна
 SetDCPixelFormat(DC); // Установить формата пикселов
 RC := wglCreateContext(DC); // создать новый контекст воспроизведения
 wglMakeCurrent(DC, RC); // Установить его текущим
 // Свойства материала для режима glEnable(GL_COLOR_MATERIAL)
 glMaterialfv(GL_FRONT, GL_AMBIENT, @mat1_amb);
 glMaterialfv(GL_FRONT, GL_DIFFUSE, @mat1_dif);
 glMaterialfv(GL_FRONT, GL_SPECULAR, @mat1_spec);
 glMaterialf (GL_FRONT, GL_SHININESS,mat1_shininess);
 // Свойства источника света GL_LIGHT1 для режима glEnable(GL_LIGHTING)
 // Стандартный источник одна команда: glEnable(GL_LIGHT0);
 glLightfv(GL_LIGHT1, GL_POSITION,@light_pos);
 glLightfv(GL_LIGHT1, GL_AMBIENT, @light_amb); // направленность
 glLightfv(GL_LIGHT1, GL_DIFFUSE, @light_dif); // рассеивание
 glLightfv(GL_LIGHT1, GL_SPECULAR, @light_spec);
 glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, @light_spot_direction);
 // разрешить источник света GL_LIGHT1
 glEnable(GL_LIGHT1);
 // Свойства тумана для режима glEnable(GL_FOG)
 fogMode := GL_EXP; // GL_EXP2, GL_LINEAR
 glFogi(GL_FOG_MODE, fogMode); // установить режим
 glFogfv(GL_FOG_COLOR, @fogColor); // цвет тумана
 glFogf(GL_FOG_DENSITY, 0.002); // плотность - 0.2%
 glHint(GL_FOG_HINT, GL_DONT_CARE); // GL_NICEST, GL_FASTEST.
 glClearColor(0.0, 0.0, 0.2, 1.0); // Установить цвет фона - синий
 glClearDepth(1.0); // Очистить буфер глубины
 glDepthFunc(GL_LESS); // Тип теста глубины
 glShadeModel(GL_SMOOTH); // плавное цветовое сглаживание
 // glEnable(..) - включить режим, glDisable(..) - отключить
 glEnable(GL_LIGHTING); // разрешить осещенность
 //glEnable(GL_FOG); // разрешить тумана
 glEnable(GL_DEPTH_TEST); // разрешить тест глубины, с использованием
 // функции определенной в glDepthFunc
 glEnable(GL_NORMALIZE); // разрешить нормали (различение передней
 // и задней сторон плоских объектов)
 glEnable(GL_COLOR_MATERIAL); // разрешить использование материалов на объектах
 glEnable(GL_TEXTURE_2D); // разрешить наложение текстур
 //glEnable(GL_BLEND); // разрешить смешивание (напр. прозрачность)
 // не совместимо с glEnable(GL_DEPTH_TEST)
 glLoadTexture('1.bmp', Texture); // загружаем текстуру
end;
{------------------------------------------------------------------}
{ Завершение работы с OpenGL }
{------------------------------------------------------------------}
procedure glKill(Wnd: hWND);
begin
 wglMakeCurrent(DC, 0);
 wglDeleteContext(RC);
 ReleaseDC(Wnd, DC);
end;
{------------------------------------------------------------------}
{ Создание объектов под отрисовку }
{------------------------------------------------------------------}
procedure glDraw();
var
 Obj: GLUquadricObj;
begin
 // очистка Экрана и буфера глубины
 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 glMatrixMode(GL_MODELVIEW); // Переключаемся на видовую матрицу
 glLoadIdentity; // Сбрасываем все преобразования
 // Пример рисования разноцветного треугольника
 glBegin(GL_POLYGON);
 glColor(0.0, 0.5, 0.0, 0.0); // Цвет 1-ой вершины
 glVertex3F(-50, -sqrt(3)*50, 0); // 1-я вершина
 glColor(0.5, 0.0, 0.0, 0.0); // Цвет 2-ой вершины
 glVertex3F(-100, -sqrt(3)*100, 0); // 2-я вершина
 glColor(0.0, 0.0, 0.5, 0.0); // Цвет 3-ей вершины
 glVertex3F(-125, -sqrt(3)*100, 0); // 3-я вершина
 glEnd;
 // Следующая фигура будет нарисована повернутой
 // Поворачиваем сферу так, чтобы северный полюс на текстуре
 // смотрел в нужную сторону
 glRotate(-90, 0.0, 1.0, 0.0);
 glRotate(60, 1.0, 0.0, 0.0);
 // здесь поворот сферы вокруг оси
 glRotate(-Angle, 0.0, 0.0, 1.0);
 // Пример рисования сферы
 glColor(1.0, 1.0, 1.0, 0.5); // установить цвет объекта
 glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Функция смешивания для непрозрачности,
 // базирующаяся на значении альфы
 // Отрабатывает только при glEnable(GL_BLEND)
 glBindTexture(GL_TEXTURE_2D, Texture); // установить текущую текстуру Texture
 Obj := gluNewQuadric;
 gluQuadricTexture(Obj, GL_TRUE); // разрешить наложить текущую текстуру на
 // создаваемый объект
 gluSphere(Obj, 100, 25, 25); // создать сферу R = 100, детализация - 25х25
 gluDeleteQuadric(Obj); // Освобождаем память
 Angle := (Angle + 5) mod 360;
 // Выводим на экран подготовленную сцену
 SwapBuffers(DC);
end;
{------------------------------------------------------------------}
{ Выбор приемлемого формата точки для данного DC }
{------------------------------------------------------------------}
procedure SetDCPixelFormat(DC: hDC);
var
 pfd: TPixelFormatDescriptor;
 nPixelFormat: Integer;
begin
 FillChar(pfd, SizeOf(pfd), 0);
 with pfd do
 begin
 nSize := SizeOf(pfd);
 nVersion := 1;
 dwFlags := PFD_DRAW_TO_WINDOW or
 PFD_SUPPORT_OPENGL or
 PFD_DOUBLEBUFFER;
 iPixelType := PFD_TYPE_RGBA;
 cColorBits := 16;
 cDepthBits := 64;
 iLayerType := PFD_MAIN_PLANE;
 end;
 nPixelFormat := ChoosePixelFormat(DC, @pfd);
 SetPixelFormat(DC, nPixelFormat, @pfd);
end;
{------------------------------------------------------------------}
{ Загрузка 8 или 24-битного bmp-файла текстуры }
{------------------------------------------------------------------}
procedure glLoadTexture(Filename: String; var Texture: GLuint);
type
 BitmapHeader = record
 FH: BitmapFileHeader;
 IH: BitmapInfoHeader;
 end;
var
 ColorTable: array of TRGBQuad;
 Data: array of Byte;
 RGBData: array of TRGBTriple;
 Bmp: BitmapHeader;
 tmp, i, L: Cardinal;
 F: hFile;
begin
 F := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
 if F = INVALID_HANDLE_VALUE then
 begin
 MessageBox(0,PChar('File '+ FileName + ' not found'), 'glLoadTexture',0);
 exit;
 end;
 ReadFile(F, Bmp, SizeOf(BitmapHeader), tmp, nil);
 L := Bmp.IH.biWidth * Bmp.IH.biHeight;
 SetLength(RGBData, L);
 case Bmp.IH.biBitCount of
 8:
 begin
 SetLength(ColorTable, Round(Exp(Bmp.IH.biBitCount * Ln(2))));
 ReadFile(F, ColorTable[0], SizeOf(TRGBQuad) * Round(exp(Bmp.IH.biBitCount * Ln(2))), tmp, nil);
 SetLength(Data, L);
 ReadFile(F, Data[0], SizeOf(Byte) * L, tmp, nil);
 for i := 0 to L - 1 do
 begin
 RGBData[L - i - 1].rgbtRed := ColorTable[Data[i]].rgbBlue;
 RGBData[L - i - 1].rgbtGreen := ColorTable[Data[i]].rgbGreen;
 RGBData[L - i - 1].rgbtBlue := ColorTable[Data[i]].rgbRed;
 end;
 end;
 24:
 begin
 SetLength(Data, L * 3);
 ReadFile(F, Data[0], SizeOf(Byte) * L * 3, tmp, nil);
 for i := 0 to L - 1 do
 begin
 RGBData[L - i - 1].rgbtRed := Data[3 * i];
 RGBData[L - i - 1].rgbtGreen := Data[3 * i + 1];
 RGBData[L - i - 1].rgbtBlue := Data[3 * i + 2];
 end;
 end;
 else
 MessageBox(0, 'Format not support', 'glLoadTexture', 0);
 end; { end case}
 CloseHandle(F);
 // Далее код из BMP.pas от Jan Horn
 glGenTextures(1, Texture);
 glBindTexture(GL_TEXTURE_2D, Texture);
 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); { Texture blends with object background }
 // glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); { Texture does NOT blend with object background }
 {
 Select a filtering type. BiLinear filtering produces very good results with little performance impact
 GL_NEAREST - Basic texture (grainy looking texture)
 GL_LINEAR - BiLinear filtering
 GL_LINEAR_MIPMAP_NEAREST - Basic mipmapped texture
 GL_LINEAR_MIPMAP_LINEAR - BiLinear Mipmapped texture
 }
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); { only first two can be used }
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); { all of the above can be used }
 gluBuild2DMipmaps(GL_TEXTURE_2D, 3, Bmp.IH.biWidth, Bmp.IH.biHeight, GL_RGB, GL_UNSIGNED_BYTE, RGBData);
 // Use when not wanting mipmaps to be built by openGL
 // glTexImage2D(GL_TEXTURE_2D, 0, 3, Bmp.IH.biWidth, Bmp.IH.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, RGBData);
end;
end.
=== 07 Собранный пример VCL ===
На форме таймер, в проект включен MyOpenGL.pas. В той же папке лежит 8 или 24-битный bmp-фаил.
unit Unit1;
interface
uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, ExtCtrls, MyOpenGL;
type
 TForm1 = class(TForm)
 Timer1: TTimer;
 procedure FormDestroy(Sender: TObject);
 procedure FormResize(Sender: TObject);
 procedure Timer1Timer(Sender: TObject);
 procedure FormCreate(Sender: TObject);
 private
 { Private declarations }
 public
 { Public declarations }
 end;
var
 Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormDestroy(Sender: TObject);
begin
 glKill(Form1.Handle);
end;
procedure TForm1.FormResize(Sender: TObject);
begin
 glOnResize(Form1.ClientWidth, Form1.ClientHeight);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
 glDraw();
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
 glInit(Form1.Handle);
 glOnResize(Form1.ClientWidth, Form1.ClientHeight);
end;
end.
=== 08 Собранный пример WinApi ===
В той же папке лежит 8 или 24-битный bmp-файл. 
program a;
uses
 windows,
 messages,
 MyOpenGL in 'MyOpenGL.pas';
var
 MainWnd: hWND;
function WindowProc(Wnd: HWND; Msg: Integer; wParam: wParam; lParam:lParam):lResult;stdcall;
begin
 case Msg of
 WM_DESTROY:
 begin
 glKill(MainWnd);
 postquitmessage(0);
 Result := 0;
 exit;
 end;
 WM_TIMER:
 begin
 glDraw(); // Рисуем сцену
 Result := 0;
 end;
 WM_SIZE:
 begin
 // В lParam содержатся размеры клиентской области ;-)
 glOnResize(loWord(lParam), hiWord(lParam));
 Result := 0;
 end;
 else
 Result := DefWindowProc(Wnd, Msg, wParam, lParam);
 end;
end;
var
 Mesg: TMsg;
 wc: TWndClassEx;
begin
 wc.cbSize := SizeOf(wc);
 wc.style := CS_HREDRAW or CS_VREDRAW;
 wc.lpfnWndProc := @WindowProc;
 wc.cbClsExtra := 0;
 wc.cbWndExtra := 0;
 wc.hInstance := hInstance;
 wc.hIcon := LoadIcon(0, IDI_WINLOGO);
 wc.hCursor := LoadCursor(0, IDC_ARROW);
 wc.hbrBackground := COLOR_BTNFACE+2;
 wc.lpszMenuName := nil;
 wc.lpszClassName := 'aWnd';
 RegisterClassEx(wc);
 // Создаем окно 400х400
 MainWnd := CreateWindowEx(0, 'aWnd', 'MyOpenGL', WS_OVERLAPPEDWINDOW or WS_VISIBLE, 100, 100, 400, 400, 0, 0, hInstance, nil);
 // Создаем контекст воспроизведения OpenGL и его параметры
 glInit(MainWnd);
 // Задаем матрицы OpenGL для размера окна 400х400
 glOnResize(400, 400);
 // Устанавливаем таймер 10мс для перерисовки сцены
 SetTimer(MainWnd, 1, 10, nil);
 while GetMessage(Mesg, 0, 0, 0) do
 begin
 TranslateMessage(Mesg);
 DispatchMessage(Mesg);
 end;
end.
== Часть II ==
В этой части будут только указаны исправления кода MyOpenGL.pas, которые надеюсь без труда сделаете сами, или просто общие сведения.
=== 09 Списки ===
"Список - это некоторый набор команд OpenGL, который можно создать один раз, например, перед началом работы программы, а потом им пользоваться вызывая его. По логике он аналогичен процедуре (или функции, как хотите). Отличие его от процедуры в том, что при формировании списка библиотека сохраняет его в каком-то своем, оптимизированном формате и вызовы списка приводят к значительно меньшим вычислительным затратам чем вызов функции, делающей тоже самое. Особенно хорошо это заметно на картах, где операции со списками реализованы аппаратно." (Взято с http://firststeps.ru/mfc/opengl/r.php?41)
Где-то до использования MyOpenGL.glDraw (напр. в MyOpenGL.glInit)
// Глобальная переменная для MyOpenGL.pas
var
 MyList: GLuint;
begin
 // указываем, что в списке два набора команд
 MyList := glGenLists(2);
 // Создаем первый набор команд
 // Флаг GL_COMPILE указывает что его надо только создать
 // Использование GL_COMPILE_AND_EXECUTE сразу же еще и выполнило бы этот набор
 // В первом наборе будем рисовать треугольник
 glNewList(MyList, GL_COMPILE);
 glBegin(GL_POLYGON);
 glVertex3F(0.0, 0.0, 0.0);
 glVertex3F(100.0, 0.0, 0.0);
 glVertex3F(50.0, sqrt(3)*50, 0.0);
 glEnd;
 // Указываем что завершили создание набора команд
 glEndList();
 // Создаем второй набор команд
 // Во втором наборе к матрице GL_MODELVIEW будет добавляться параллельный
 // перенос на 50 по оси OX и на 1 по оси OZ (это чтобы поверх остального)
 // Поставить переменные значения в перенос нельзя, так как это будет уже
 // другая матрица. Хотя может я и ошибаюсь.
 glNewList(MyList + 1, GL_COMPILE);
 glTranslatef(50, sqrt(3) * 12.5, 1);
 // Указываем что завершили создание набора команд
 glEndList();
MyOpenGL.glDraw можно переписать так. В итоге получим, что рисуется одна неподвижная фигура (зеленая), а другая вращающаяся, выполненная как и первая, и перенесенная в центр первой и на уровень выше первой (красная).
procedure glDraw();
begin
 // очистка Экрана и буфера глубины
 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
 // Сбрасываем GL_MODELVIEW до единичной
 glLoadIdentity();
 // Устанавливаем зеленый цвет для фигур
 glColor(0.0, 1.0, 0.0, 1);
 // Создаем фигуру, используя набор команд MyList
 glCallList(MyList);
 // Добавляем значение к углу поворота
 Angle := (Angle + 5) mod 360;
 // Вызываем второй набор команд, который хранится по MyList + 1
 // В результате к GL_MODELVIEW добавляется перенос
 glCallList(MyList + 1);
 // Добавим к GL_MODELVIEW-матрице еще и поворот на угол Angle
 glRotate(Angle, 0.0, 0.0, 1.0);
 // Устанавливаем красный цвет для фигур
 glColor(1.0, 0.0, 0.0, 1);
 // Создаем фигуру, используя набор команд MyList
 // Поскольку GL_MODELVIEW-матрица у нее другая, то и распалагается
 // она уже иначе, чем первая фигура
 glCallList(MyList);
 // Выводим на экран подготовленную сцену
 SwapBuffers(DC);
end;
Ну и в MyOpenGL.glKill стоит добавить
 glDeleteLists(MyList, 2); // удаляем все элементы списка :-)
;Примечание: устоявшееся понятие "список отображения" - список, в котором все наборы команд - это рисование фигуры (шаблон). См. Народный учебник, Глава 12.
=== 10 Собранный пример WinAPI без таймера для отрисовки ===
Взято у Jan Horn (http://www.sulaco.co.za, http://home.global.co.za/~jhorn)
Как обычно, в папке проекта есть MYOpenGL.pas и 1.bmp :-)
Идеи таковы: 
* пока приложение не обработает всю свою очередь сообщений, перерисовка выполняться не будет;
* Если нажали кнопку, то в массиве "Статус кнопок" для соответствующего элемента выставляется значение True, которое скинется после обработки этого нажатия в ProcessKey. Такая схема позволяет запретить накопление нажатий.
program a;
uses
 windows,
 messages,
 MyOpenGL in 'MyOpenGL.pas';
var
 MainWnd: hWND;
 FPSCount: Integer; // Число FPS
 keys: array [0..255] of Boolean; // Статусы кнопок
function IntToStr(Num: Integer) : String;
begin
 Str(Num, result);
end;
// Обработка кнопок
procedure ProcessKeys;
begin
 if (keys[VK_UP]) then
 begin
 // Что-то делаем
 //...
 // Сбрасываем нажатие этой кнопки
 keys[VK_UP] := False;
 end;
 if Keys[Ord('H')] then
 begin
 // Что-то делаем
 //...
 // Сбрасываем нажатие этой кнопки
 Keys[Ord('H')] := FALSE;
 end;
end;
function WindowProc(Wnd: HWND; Msg: Integer; wParam: wParam; lParam:lParam):lResult;stdcall;
begin
 case Msg of
 WM_DESTROY:
 begin
 glKill(MainWnd);
 postquitmessage(0);
 Result := 0;
 exit;
 end;
 WM_KEYDOWN:
 begin
 keys[wParam] := True;
 Result := 0;
 end;
 WM_KEYUP:
 begin
 keys[wParam] := False;
 Result := 0;
 end;
 WM_TIMER:
 begin
 FPSCount := Round(FPSCount * 1000/{FPS-интервал - у нас 1с}1000);
 SetWindowText(MainWnd, PChar('FPS: ' + IntToStr(FPSCount)));
 FPSCount := 0;
 end;
 WM_SIZE:
 begin
 glOnResize(loWord(lParam), hiWord(lParam));
 Result := 0;
 end;
 else
 Result := DefWindowProc(Wnd, Msg, wParam, lParam);
 end;
end;
var
 Mesg: TMsg;
 wc: TWndClassEx;
 finished: Boolean = False; // Флаг завершения работы OpenGL
begin
 wc.cbSize := SizeOf(wc);
 wc.style := CS_HREDRAW or CS_VREDRAW;
 wc.lpfnWndProc := @WindowProc;
 wc.cbClsExtra := 0;
 wc.cbWndExtra := 0;
 wc.hInstance := hInstance;
 wc.hIcon := LoadIcon(0, IDI_WINLOGO);
 wc.hCursor := LoadCursor(0, IDC_ARROW);
 wc.hbrBackground := COLOR_BTNFACE + 2;
 wc.lpszMenuName := nil;
 wc.lpszClassName := 'aWnd';
 RegisterClassEx(wc);
 // Создаем окно 400х400
 MainWnd := CreateWindowEx(0, 'aWnd', 'MyOpenGL', WS_OVERLAPPEDWINDOW or WS_VISIBLE, 100, 100, 400, 400, 0, 0, hInstance, nil);
 // Создаем контекст воспроизведения OpenGL и его параметры
 glInit(MainWnd);
 // Задаем матрицы OpenGL для размера окна 400х400
 glOnResize(400, 400);
 // Устанавливаем таймер на 1с для отображения FPS в заголовке окна
 SetTimer(MainWnd, 1, 1000, nil);
 while not finished do
 begin
 // Если очередь сообщений для окна пуста, то проводим
 // расчет и отрисовку сцены, иначе обрабатываем
 // очередь сообщений
 if (PeekMessage(Mesg, 0, 0, 0, PM_REMOVE)) then
 begin
 // Если было получено WM_QUIT, то выход
 if (Mesg.message = WM_QUIT) then
 finished := True
 else
 begin
 TranslateMessage(Mesg);
 DispatchMessage(Mesg);
 end;
 end
 else
 begin
 // Увеличиваем счетчик отрисованных кадров
 Inc(FPSCount);
 // Рисуем-с кадр
 glDraw();
 // Если была до этого нажата ESC, то сразу выходим
 if (keys[VK_ESCAPE]) then
 finished := True
 else
 ProcessKeys;
 end;
 end;
end.
=== 11 Разное ===
:а. Получение текста последней ошибки OpenGL
MessageBox(0, gluErrorString(glGetError), 'OpenGL error', MB_OK);
:б. Восстановление состояния режима
Лампа GL_LIGHT0 будет включена или выключена в зависимости от ее состоянием перед началом блока
glPushAttrib(GL_LIGHT0);
{..}
glEnable(GL_LIGH0);
{..}
glPopAttrib();
=== 12 Вывод текста в OpenGL ===
Код получен объединением кода Jan Horn, NeHe (уроки 13 и 14) и DRKB 
Работа с графикой и мультимедиа > DerectX, OpenGL > OpenGL: Каким обpазом выбиpать pазмеp шpифта.
Итак, можем выводить текст как растровое изображение и как векторное (буквы это 3D объекты). Идея такова, что для алфавита подготавливается список (см 09), а при вводе текст разбивается на буквы и потом последовательно для каждой буквы печатаемой строки выполняется соответствующий набор команд из списка.
Ниже код, который реализует как растровый, так и как векторный вывод. (необходимо лишь закомментировать указанные строки).
var
 CharList: GLuint; // Вот этот список
 gmf: array [0..255] of GLYPHMETRICSFLOAT; // массив требуется для векторного
{----------------------------------------------------------------}
{ Создание списка букв }
{----------------------------------------------------------------}
procedure glBuildFont;
var
 Font: hFont;
begin
 // Список для 256 букв алфавита
 CharList := glGenLists(256);
 // Создаем шрифт, подробнее см. SDK
 Font := CreateFont(-28, 0, 0, 0, FW_BOLD, 0, 0, 0, RUSSIAN_CHARSET,
 OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
 FF_DONTCARE or DEFAULT_PITCH, 'Comic Sans MS');
 // Устанавливаем шрифт для DC
 SelectObject(DC, Font);
 // Создаем команды списка.
 // Выбираем один из двух вариантов
 // Если используем wglUseFontBitmaps то - это растровое изображение
 if not wglUseFontBitmaps(DC, 0, 256, CharList) then
 // или
 // Если используем wglUseFontOutlines то - это векторное изображение
 if not wglUseFontOutlines(DC, 0, 255, CharList, 0, 0.2, WGL_FONT_POLYGONS, @gmf) then
 MessageBox(0, 'Font not create', 'glBuildFont', MB_OK);
 // Удаляем шрифт, так как соответствующий список уже создан
 DeleteObject(Font);
end;
{----------------------------------------------------------------}
{ Вывод строки на экран }
{----------------------------------------------------------------}
procedure glPrint(X, Y: Integer; text: String);
begin
 // Рассчитывается позиция на экране. Требуется только для растрового
 // вывода. То есть при векторном выводе X и Y gLPrint игнорируются,
 // поэтому перед выводом надо будет воспользоваться glTranslatef
 glRasterPos2i(X, Y);
 glPushAttrib(GL_LIST_BIT);
 // Указываем, что первая буква алфавита рисуется 1-ым набором команд
 glListBase(1);
 // Выводим текст
 glCallLists(Length(text), GL_UNSIGNED_BYTE, PChar(text));
 glPopAttrib();
end;
// Где-то перед использованием вывода текста, напр. в MyOpenGL.glInit
// после создания контекста воспроизведения RC
glBuildFont;
// Когда работа с текстом будет уже не нужна, то очистим список
// Напр. в MyOpenGL.glKill перед освобождением RC
glDeleteLists(CharList, 256);
Добавление теста на сцену немного различается. Далее добавляемый код в процедуру построения сцены (напр. в MyOpenGL.glDraw).
Для растрового текста
 // Устанавливаем цвет букв
 glColor3f(1.0, 0.0, 1.0);
 // Добавляем текст на сцену
 glPrint(-150, 0, 'Даст ист фантастиш');
Вывод векторного шрифта несколько сложнее, поскольку команды списка создают 3D объекты с определенными размерами (все буквы маленького размераа типа 0.01) (для растровых шрифтов величина букв определяется только первым параметром функции CreateFont в glBuildFont) и поэтому, если у матрица проекций работает не с такими размерами (у меня она работает с размерами как напр. 100), то надо увеличить текст glScale. Так же надо подобрать режимы, чтобы текст смотрелся 
по-настоящему 3D.
 // Подбираем нужный режим
 glEnable(GL_LIGHT0); // Включаем стандартную лампу
 glDisable(GL_LIGHT1); // Отключаем пользовательскую лампу
 glDisable(GL_NORMALIZE); // Отключаем режим нормалей
 glLoadIdentity; // Загружаем в GL_MODELVIEW единичную матрицу
 glTranslatef(-150, 0.0, 0.0); // Добавим перенос от центра
 glScale(100, 100, 1.0); // Растягиваем текст на 100 по ОХ и OY
 glRotate(45, 1.0, 1.0, 0.0); // Добавим поворот
 glColor(1.0, 0.0, 1.0); // Установим цвет букв
 glPrint(0, 0, 'Даст ист фантастиш'); // Добавим текст на сцену
 // Возвращаем все как было
 glDisable(GL_LIGHT0); // Выключаем стандартную лампу
 glEnable(GL_NORMALIZE); // Включаем режим нормалей
 glEnable(GL_LIGHT1); // Включаем пользовательскую лампу
=== 13 Компонент для работы с OpenGL ===
Напр. на http://glscene.org - GL_Scene - версии для Delphi5-7.
Умеет много чего, в частности импорт 3DS файлов.
=== 14 Загрузка объектов под отрисовку из файла ===
Почти всегда хочется, чтобы в функции создании сцены (у меня это MyOpenGL.glDraw) объекты под отрисовку загружались с файла. Обычно реализуют свой формат хранения фигур и функции для работы с ним. Я ленивый, потому предлагаю свой способ - хранить не объекты, а набор gl-комманд ему соответствующий, который будет загружаться в список. Например рассмотрим такой файл.
 2
 
 glNewList
 glBegin 4
 glVertex3f 0 0 0
 glVertex3f 100,4 0 0
 glVertex3f 50 100 0
 glEnd
 glEndList
 
 glNewList
 glRotate 10 0 1 0
 glEndList
2 - это то что в списке 2 набора команд. Соответственно в самой программе это будет использоваться так (на примере MyOpenGL)
Примечания: 
# дробная часть указывается через запятую!
# Для glBegin значения констант смотреть в OpenGL.pas
uses MyGLU;
var
 List: GLuint;
 Count: Integer;
begin
 // Где-то до использования создания сцены, напр. в MyOpenGL.glInit
 Count := gluLoadList('1.gls', List); // моя функция загрузки скрипта из файла из MyGLU.pas
 // Где-то в функции создания сцены, напр. в MyOpenGL.glDraw
 for i := 0 to Count - 1 do
 begin
 glCallList(List + i); // Вызываем i-ый набор команд; нумерация с нулевого
 end;
По-моему замечательная идея ;-) Возможно у нее уже есть реализация. 
А теперь черновой код MyGLU.pas (поддерживаются не все функции gl, да и контроль ошибок минимальный). Дописывать я его уже наверно не буду; если вам понравилась идея, то доработать его думаю труда не составит - все прозрачно.
unit MyGLU;
interface
uses windows, OpenGL, SysUtils;
// Функция создает список, используя команды из текстового файла
// Возвращаемое значение - число элементов созданного списка
function gluLoadList(FileName: String; var List: GLuint): Integer;
implementation
var
 Param: array [1..10] of Extended;
// Получение из входной строки S подслов, которые конвертируется в Float
// и помещаются в соответствующий Param[i]. S начинает обрабатываться
// с S[Pos].
function GetParam(S: String; Pos: Integer): Integer;
var
 i, j: Integer;
 Value: String;
begin
 for i := 1 to 10 do Param[i] := 0;
 j := 1;
 Value := ;
 for i := Pos + 1 to Length(S) do
 if (S[i] = ' ') then
 begin
 Param[j] := StrToFloat(Value);
 if j = 10 then
 begin
 Result := 10;
 MessageBox(0, PChar('Too many params!'), 'GetParam', MB_OK);
 exit;
 end
 else j := j + 1;
 Value := ;
 end
 else Value := Value + S[i];
 if Value <> then Param[j] := StrToFloat(Value);
 Result := j;
end;
function gluLoadList(FileName: String; var List: GLuint): Integer;
var
 F: TextFile;
 S, Command: String;
 Count, CurrList, i: Integer;
 Obj: GLUquadricObj;
begin
 if not FileExists(FileName) then
 begin
 MessageBox(0, PChar('File '+FileName + ' not found'), 'glLoadList', MB_OK);
 end;
 AssignFile(F, FileName);
 Reset(F);
 readln(F, S);
 Count := StrToInt(S);
 CurrList := 0; // сдвиг; иначе номер набора команд.
 List := glGenLists(Count);
 Obj := gluNewQuadric;
 // читаем из файла, если видим знакомую команду, то выполняем
 while not EOF(F) do
 begin
 Command := ;
 ReadLn(F, S);
 for i := 1 to Length(S) do
 if S[i] = ' ' then
 break
 else
 Command := Command + S[i];
 GetParam(S, i);
 Command := UpperCase(Command);
 if Command = 'GLNEWLIST' then
 glNewList(List + CurrList, GL_COMPILE);
 if Command = 'GLENDLIST' then
 begin
 glEndList;
 CurrList := CurrList + 1;
 end;
 if Command = 'GLBEGIN' then
 glBegin(round(Param[1]));
 if Command = 'GLEND' then
 glEnd;
 if Command = 'GLROTATE' then
 glRotate(Param[1], Param[2], Param[3], Param[4]);
 if Command = 'GLSCALE' then
 glScale(Param[1], Param[2], Param[3]);
 if Command = 'GLTRANSLATEF' then
 glTranslatef(Param[1], Param[2], Param[3]);
 if Command = 'GLVERTEX3F' then
 glVertex3f(Param[1], Param[2], Param[3]);
 if Command = 'GLCOLOR' then
 glColor(Param[1], Param[2], Param[3], Param[4]);
 if Command = 'GLLOADIDENTITY' then
 glLoadIdentity;
 if Command = 'GLPUSHMATRIX' then
 glPushMatrix;
 if Command = 'GLPOPMATRIX' then
 glPopMatrix;
 if Command = 'GLUSPHERE' then
 gluSphere(Obj, Param[1], Round(Param[2]), Round(Param[3]));
 if Command = 'GLUCYLINDER' then
 gluCylinder(Obj, Param[1], Param[2], Param[3], Round(Param[4]), Round(Param[5]));
 if Command = 'GLUDISK' then
 gluDisk(Obj, Param[1], Param[2], Round(Param[3]), Round(Param[4]));
 end;
 glEndList;
 gluDeleteQuadric(Obj);
 CloseFile(F);
 Result := Count;
end;
end.
=== 15 Bmp - формат ===
Вообщем-то к OpenGL это никакого отношения не имеет, но загрузку текстур все-таки зачастую приходится производить из него, так что полезно представлять что это такое. Описание самое простейшее, многое выкинуто, но для общего понимания и загрузки текстур из bmp-файлов - этого будет достаточно.
В начале каждого bmp-файла идет служебная информация - общая информация о файле TBitmapFileHeader и потом общая информация о bmp-изображении - TBitmapInfoHeader.
Определения типов из windows.pas (комментарии мои)
type
 tagBITMAPFILEHEADER = packed record
 bfType: Word; // тип файла - для Bmp = BM ~ $42$4D
 bfSize: DWORD; // размер файла в байтах
 bfReserved1: Word; // зарезервировано
 bfReserved2: Word; // зарезервировано
 bfOffBits: DWORD; // номер байта; со следующего начнется "изображение"
 end;
 TBitmapFileHeader = tagBITMAPFILEHEADER;
 tagBITMAPINFOHEADER = packed record
 biSize: DWORD; // размер структуры BitmapInfoHeader, байт
 biWidth: Longint; // ширина изображения
 biHeight: Longint; // высота изображения; отрицательна, если изображение
 // надо перевернуть
 biPlanes: Word; // число плоскостей изображения, всегда 1
 biBitCount: Word; // число бит на точку; ОЧЕНЬ ВАЖНЫЙ ПАРАМЕТР
 biCompression: DWORD; // Флаг компрессии - обычно BI_RGB (=0)
 biSizeImage: DWORD; // Размер изображения в байтах
 biXPelsPerMeter: Longint; // разрешение точек на метр по OX И по OY
 biYPelsPerMeter: Longint;
 biClrUsed: DWORD; // используемое количество цветов
 biClrImportant: DWORD; // Обычно = biClrUsed
 end;
 TBitmapInfoHeader = tagBITMAPINFOHEADER;
 end;
Код поясняющий это
// Для простоты определим тип, состоящий из двух нужных нам
type
 TBitmapHeader = record
 FH: TBitmapFileHeader;
 IH: TBitmapInfoHeader;
 end;
var
 Bmp: TBitmapHeader;
 tmp: Cardinal;
 F: hFile;
begin
 // Для доступа к файлу, определяемого FileName, я использую WinAPI-функцию
 // Примерный смысл параметров, думаю, ясен
 F := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
 // Читаем данные из файла F, данные помещаются в Bmp, количество байт которых
 // хотим прочитать - это третий параметр, количество прочитанных байт будет
 // помещено в tmp
 ReadFile(F, Bmp, sizeof(TBitmapHeader), tmp, nil);
 //
 // Ширина изображения для примера
 MessageBox(0, PChar(IntToStr(Bmp.IH.biWidth)), 'Width', MB_OK);
 // Закрываем файл
 CloseHandle(F);
После этого заголовка (в байтах это Bmp.FH.bmOffBits) хранится изображение. Следует различать две группы bmp-изображений 1, 4, 8-битные и 16, 24, 32-битные. 
Для первой группы сначала идет палитра - то есть перечисление цветов в формате Red-Green-Blue-Alpha (TRGBQuad), содержащая biClrUsed-цветов (обычно это равно 2 ^ biBitCount), потом идут точки изображения - каждая точка пишется в соответствующее количество бит (1, 4, 8) - это просто номер цвета из палитры.
Для второй группы палитры нет, а сразу идут точки - для 24 и 32-битных изображений - в формате RGB и RGBA (по байту на составляющую, что и дает соответствующую битность); для 16-битных несколько хитрее, потому опущу описание.
При загрузке текстуры передаются - ширина, высота изображения и массив RGB точек. Для 24- и 32- битных bmp файлов - массив получается элементарно. Для 1, 4 и 8-битных необходимо этот массив формировать на основе палитры.
Примечания:
* Точки изображения пробегаются с нижнего левого угла.
* Цвета в BMp-файле хранятся в последовательности BGR, потому при загрузке в текстуру надо менять R и B цвета местами.
* На самом деле, есть еще такое понятие как байтовое выравнивание в точках, при ширине изображения кратной 4 (для текстур это разумеется верно) об этом можно не беспокоится.
Пример загрузки 8- и 24-битных изображений можно посмотреть в MyOpenGL.glLoadTexture. 
Author: Zoobastik
Source: http://forum.sources.ru
ID: 03950
Category:Графика
Category:OpenGL
Category:Статьи