{
Добро пожаловать на девятый урок. К этому моменту вы должны хорошо разбираться в OpenGL.
«КЕР: Если нет, то, должно быть, это вина моего переводчика...».
(Майлинг добавил: Мой грех еще тяжелее, ха-ха)
Вы изучили каждую деталь настройки окна OpenGL.
Научитесь отображать и добавлять свет и смешивание цветов (прозрачность) к вращающимся объектам.
Этот урок следует рассматривать как промежуточный урок.
Вы узнаете, как перемещать растровое изображение по 3D-сцене и удалять из растрового изображения черные пиксели (с помощью смешивания цветов).
Далее раскрасьте черно-белые текстуры и, наконец, вы научитесь создавать насыщенные цвета.
И смешивайте текстуры разных цветов друг с другом, чтобы получить простой анимационный эффект.
Модификации будем делать на основе кода из первого урока. Сначала добавьте несколько переменных в начале исходного кода программы.
Я переписал весь код для ясности.
}
Вар
h_RC: HGLRC; // Контекст рендеринга (таблица описания затенения).
h_DC: HDC // Контекст устройства (таблица описания устройства)
h_Wnd: HWND // дескриптор окна;
h_Instance: HINST // Экземпляр программы (экземпляр).
клавиши: Array[0..255] Boolean // Массив для клавиатурных процедур;
{Следующие строки добавлены недавно.
Twinkle и tp являются логическими переменными, что означает, что им можно присвоить только значение TRUE или FALSE.
мерцание используется для отслеживания того, включен ли эффект мерцания.
tp используется для проверки того, нажата или отпущена клавиша «T».
(tp=TRUE при нажатии, tp=FALSE при отпускании).}
мерцание : Boolean // Мерцающие звезды (новое)
tp : Boolean; // Нажата ли «T» (новая)?
{Теперь давайте создадим структуру.
Слово «структура» звучит пугающе, но это не так. (Это тип записи Delphi)
Структура использует набор простых типов данных (а также переменных и т. д.) для выражения большей комбинации похожих данных.
Мы знаем, что следим за звездами.
Вы можете увидеть звезды ниже;
Каждая звезда имеет три целочисленных значения цвета. Один красный (справа), один зеленый (ж) и один синий (б).
Кроме того, каждая звезда находится на разном расстоянии от центра экрана.
И это может быть любой угол в 360 градусов с центром экрана в качестве начала координат.
Число с плавающей запятой для отслеживания расстояния.
Число угла с плавающей запятой отслеживает значение угла звезды.
Поэтому мы использовали набор данных для описания цвета, расстояния и угла расположения звезд на экране.
К сожалению, мы отслеживаем более одной звезды.
Но нет необходимости создавать 50 красных значений, 50 зеленых значений, 50 синих значений, 50 значений расстояния.
и 50 значений угла и просто создайте звездный массив. }
Тип
stars = Record // Создаём структуру для звёзд с именем stars
r, g, b: целое число // цвет звезд;
dist: GLfloat //Расстояние звезды от центра;
угол: GLfloat; //Текущий угол звезды
Конец;
Вар
star : Array[0..49] Of stars // Используйте структуру «звезды» для создания массива «звезда», содержащего 50 элементов;
{Далее мы настраиваем несколько переменных отслеживания:
Переменная расстояния (увеличения) звезды от наблюдателя,
Угол (наклон), под которым мы видим звезды,
и переменное вращение, которое заставляет мерцающую звезду вращаться вокруг оси Z.
Переменная цикла используется для рисования 50 звезд.
текстура[1] используется для хранения черно-белой текстуры.
Если вам нужно больше текстур,
Вам следует увеличить размер массива текстур до количества текстур, которые вы решите использовать.
}
Zoom: GLfloat = -15.0 // Расстояние звезды от наблюдателя;
Tilt : GLfloat = 90.0 // Наклон звезды;
spin : GLfloat // Вращение мерцающей звезды;
цикл: GLuint // Глобальная переменная цикла;
текстура: Array[0..1] Of GLuint // Сохраняем текстуру;
PROcedure glGenTextures (n: GLsizei; Var текстуры: GLuint external);
opengl32;
Процедура glBindTexture (цель: GLenum; текстура: GLuint);
opengl32;
{
Код непосредственно выше — это код, который мы используем для загрузки текстуры.
Я не буду подробно объяснять этот код.
Это точно тот же код, который мы использовали в уроках 6, 7 и 8.
Загруженное на этот раз растровое изображение называется star.bmp.
Здесь мы используем glGenTextures(1, &texture[0]),
для создания текстуры. Текстура использует линейную фильтрацию.
}
Функция LoadTexture: boolean; //Загружаем растровое изображение и преобразуем его в текстуру.
Вар
Статус: логическое значение // Индикатор состояния;
TextureImage : Array[0..1] Of PTAUX_RGBImageRec // Создаём место для хранения текстур;
Начинать
Статус: = ложь;
ZeroMemory(@TextureImage, sizeof(TextureImage)); // Устанавливаем указатель в NULL
TextureImage[0] := LoadBMP('Star.bmp');
Если TextImage[0] <> Nil Тогда
Начинать
Статус := ИСТИНА // Установить статус ИСТИНА;
glGenTextures(1, текстура[0]); // Создаем текстуру
// Создать карту ближайшего фильтра
glBindTexture (GL_TEXTURE_2D, текстура [0]);
// Генерируем текстуру
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // (новое)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // (новое)
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].sizeX,
TextureImage[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
ТекстурноеИзображение[0].data);
Конец;
Если назначено(TextureImage[0]) Тогда // Существует ли текстура
Если присвоено(TextureImage[0].data) Тогда // Существует ли изображение текстуры
TextureImage[0].data := Nil // Освободите память, занятую изображением текстуры;
TextureImage[0] := Nil // Освобождаем структуру изображения;
результат := Статус // Возвращаемый статус;
Конец;
{Установите режим рендеринга OpenGL в glInit(). Мы не собираемся использовать здесь тестирование глубины,
Если вы используете код из урока 1,
Пожалуйста, подтвердите, были ли удалены glDepthFunc(GL_LEQUAL) и glEnable(GL_DEPTH_TEST).
В противном случае результаты, которые вы увидите, будут беспорядочными.
Здесь мы используем текстурное наложение,
Поэтому убедитесь, что вы добавили любой код, который не был включен в первый урок.
Вы заметите, что мы включили наложение текстур путем смешивания цветов.
}
Процедура glInit();
Начинать
If (Not LoadTexture) then // Вызов подпрограммы загрузки текстуры (новая)
выход; // Если не удалось загрузить, выходим (новый)
glEnable(GL_TEXTURE_2D); // Включаем наложение текстур
glShadeModel(GL_SMOOTH); // Включаем сглаживание теней;
glClearColor(0.0, 0.0, 0.0, 0.5); // черный фон;
glClearDepth(1.0); //Устанавливаем буфер глубины
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Действительно тонкая коррекция перспективы
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Устанавливаем функцию смешивания цветов для достижения полупрозрачного эффекта;
glEnable(GL_BLEND); // Включаем смешивание цветов
{Ниже приведен новый код.
Устанавливаются начальный угол, расстояние и цвет каждой звезды.
Вы заметите, насколько легко изменить свойства структуры.
Все 50 звезд будут зациклены.
Чтобы изменить угол star[1], все, что нам нужно сделать, это star[1].angle=определенное значение;
Это так просто! }
Цикл For := от 0 до 49 Do // Создаем цикл для установки всех звездочек
Начинать
star[loop].angle := 0.0 // Все звезды начинаются с нулевого угла
{Расстояние петли-й звезды от центра делится на величину петли на общее количество звезд, а затем умножается на 5,0.
По сути, это делает последнюю звезду немного дальше от центра, чем предыдущая.
Таким образом, когда цикл равен 50 (последняя звездочка), цикл, разделенный на число, равен ровно 1,0.
Причина, по которой нам нужно умножить на 5,0, заключается в том, что 1,0*5,0 равно 5,0.
『КЕР: Чепуха, чушь! Почему этот иностранец похож на Конг Иджи! :)』
5.0 уже совсем близко к краю экрана. Я не хочу, чтобы звезды вылетали за пределы экрана, поэтому 5.0 — лучший выбор.
Конечно, если поместить сцену глубже экрана,
Возможно, вы могли бы использовать значение больше 5,0, но звезды будут выглядеть меньше (и все из-за перспективы).
Вы также заметите, что цвет каждой звезды представляет собой случайное число от 0 до 255.
Вы можете задаться вопросом, почему диапазон значений цвета здесь не является обычным диапазоном OpenGL от 0,0 до 1,0.
Используемая здесь функция настройки цвета — glColor4ub вместо предыдущей glColor4f.
ub означает, что параметр имеет тип Unsigned Byte.
Диапазон значений байта — 0–255.
Кажется, проще использовать байтовое значение для получения случайного целого числа, чем для получения случайного числа с плавающей запятой.
}
star[loop].dist := (Trunc(loop) / 50) * 5.0 // Вычисляем расстояние звезды от центра;
star[loop].r := random(256); // Устанавливаем случайный красный компонент для star[loop]
star[loop].g := random(256); // Устанавливаем случайный красный компонент для star[loop]
star[loop].b := random(256); // Устанавливаем случайный красный компонент для star[loop]
Конец;
Конец;
{
Теперь мы переходим к коду рисования glDraw().
Если вы используете код из урока 1, удалите старый код DrawGLScene и просто скопируйте код ниже.
На самом деле код первого урока состоит всего из двух строк, поэтому вырезать особо нечего.
}
Процедура glDraw();
Начинать
glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT // Очищаем экран и буфер глубины);
glBindTexture(GL_TEXTURE_2D, текстура[0]); //Выбираем текстуру
Цикл for := от 0 до 49 Do // Цикл для установки всех звездочек
Начинать
glLoadIdentity(); // Перед рисованием каждой звезды сбросим матрицу наблюдения модели
glTranslatef(0.0, 0.0, Zoom); // Углубляемся в экран (используя значение масштабирования)
glRotatef(tilt, 1.0, 0.0, 0.0); // Наклон угла обзора (используя значение 'tilt')
{
Теперь давайте переместим звезды.
Звезда начинается в центре экрана.
Первое, что нам нужно сделать, это повернуть сцену по оси Y.
Если повернуть его на 90 градусов, то ось X уже не идет слева направо, а выходит за пределы экрана изнутри наружу.
Чтобы было понятнее, приведем пример. Представьте, что вы стоите посреди дома.
Теперь представьте, что на стене слева от вас написано -x, а на стене перед вами написано -z.
Стена справа — +x, а стена позади вас — +z.
Если весь дом повернут на 90 градусов вправо, но вы не двигаетесь, то стена впереди будет иметь номер -x вместо -z.
Все остальные стены тоже двигаются. -z появится справа, +z появится слева, а +x появится позади вас.
Ты с ума сошел? Вращая сцену, мы меняем ориентацию плоскостей x и z.
Вторая строка кода перемещает положительное значение вдоль оси X.
Обычно положительное значение по оси X означает перемещение к правой стороне экрана (то есть обычное положительное направление оси X).
Но здесь, поскольку мы вращаем систему координат вокруг оси Y, положительное направление оси X может быть в любом направлении.
Если мы повернём его на 180 градусов, левая и правая стороны экрана будут зеркальными.
Поэтому, когда мы движемся вдоль положительной оси X, она может быть влево, вправо, вперед или назад.
}
glRotatef(star[loop].angle, 0.0, 1.0, 0.0); //Поворот на угол текущей нарисованной звезды;
glTranslatef(star[loop].dist, 0.0, 0.0 // Двигаемся вперед по оси X);
{
В следующем коде есть небольшая хитрость.
Звезды на самом деле представляют собой плоскую текстуру.
Теперь вы рисуете плоский квадрат в центре экрана и применяете текстуру, и это выглядит хорошо.
Все так, как вы себе представляли. Но если повернуть на 90 градусов по оси Y,
Единственные две стороны текстуры на экране, обращенные к вам, — это правая и левая стороны. Это просто выглядит как тонкая линия.
Это не то, чего мы хотим. Мы хотим, чтобы звезды всегда были обращены к нам, независимо от того, как повернут или наклонен экран.
Мы достигаем этого, компенсируя любые вращения звезды перед ее рисованием.
Вы можете изменить вращение, чтобы противодействовать вращению. Когда мы наклоняем экран, мы фактически поворачиваем звезду на текущий угол.
Изменяя порядок на противоположный, мы «антивращаем» звезду под текущим углом. То есть звезда поворачивается на отрицательное значение текущего угла.
то есть,
Если мы повернули звезду на 10 градусов, мы повернем ее на -10 градусов, чтобы звезда снова была обращена к экрану по этой оси.
Первая строка ниже отменяет вращение вдоль оси Y. Затем нам также нужно сместить наклон экрана по оси X.
Для этого нам просто нужно еще раз повернуть экран – наклонить.
После отмены вращения по осям X и Y звезда теперь снова полностью обращена к нам.
}
glRotatef(-star[loop].angle, 0.0, 1.0, 0.0 // Отменяем угол текущей звезды
glRotatef(-tilt, 1.0, 0.0, 0.0); // Отмена наклона экрана
{Если мерцание имеет значение TRUE, мы сначала рисуем на экране невращающуюся звезду:
Вычтите текущее количество звезд (цикл) из общего количества звезд (num), а затем вычтите 1.
для извлечения разных цветов каждой звезды (это делается потому, что диапазон цикла составляет от 0 до num-1).
Например, при результате 10 мы используем цвет звезды №10.
Таким образом, цвета соседних звезд всегда различны. Это не очень хорошая идея, но она работает.
Последнее значение — это компонент альфа-канала. Чем меньше значение, тем тусклее звезда.
Поскольку мерцание включено, каждая звезда будет нарисована дважды.
Программа будет работать медленнее, в зависимости от производительности вашего компьютера.
Но цвета звезд, нарисованных в два прохода, сливаются друг с другом и производят великолепный эффект.
При этом, поскольку звезды при первом проходе не вращались, звезды после включения мерцания выглядят как эффект анимации.
(Если вам здесь что-то не понятно, просто зайдите и сами посмотрите эффект работы программы.)
Стоит отметить, что раскрашивать текстуры несложно.
Несмотря на то, что сама текстура черно-белая, она изменится на любой цвет, который мы выбрали перед ее рисованием.
Кроме того, также стоит отметить, что значение цвета, которое мы здесь используем, имеет байтовый тип,
вместо обычных чисел с плавающей запятой. Даже компонент альфа-канала такой. }
If (мерцание) Тогда // Включить эффект мерцания
Начинать
// Укажите цвет, используя байтовое значение
glColor4ub(звезда[(50 - петля) - 1].r, звезда[(50 - петля) - 1].g,
звезда[(50 - петля) - 1].б, 255);
glBegin(GL_QUADS); // Начинаем рисовать четырехугольники с текстурой
glTexCoord2f(0.0, 0.0);
glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0.0);
glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0);
glVertex3f(1.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(-1.0, 1.0, 0.0);
glEnd(); // Конец рисования четырехугольника
Конец;
{
Теперь нарисуйте второй проход звезд.
Единственное отличие от предыдущего кода в том, что звезды на этот раз точно будут нарисованы, и на этот раз звезды будут вращаться вокруг оси Z.
}
glRotatef(spin, 0.0, 0.0, 1.0 // Поворот звезды вокруг оси Z);
// Укажите цвет, используя байтовое значение
glColor4ub(звезда[петля].r, звезда[петля].g, звезда[петля].b, 255);
glBegin(GL_QUADS); // Начинаем рисовать четырехугольники с текстурой
glTexCoord2f(0.0, 0.0);
glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0.0);
glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0);
glVertex3f(1.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0);
glVertex3f(-1.0, 1.0, 0.0);
glEnd(); // Конец рисования четырехугольника
{Приведенный ниже код представляет движение звезд.
Увеличиваем значение вращения, чтобы вращать все звезды (обороты).
Затем увеличьте угол поворота каждой звезды на цикл/число.
Это заставляет звезды, расположенные дальше от центра, вращаться быстрее. Наконец, уменьшите расстояние каждой звезды от центра экрана.
Такое ощущение, что звезды постоянно затягиваются в центр экрана. }
spin := spin + 0.01 // Вращение звезды;
star[loop].angle := star[loop].angle + Trunc(loop) / 50 // Изменяем угол поворота звезды;
star[loop].dist := star[loop].dist - 0.01 // Изменяем расстояние звезды от центра;
{Следующие несколько строк проверяют, коснулась ли звезда центра экрана.
Когда звезда попадает в центр экрана, мы даем ей новый цвет и перемещаем ее на 5 единиц наружу.
Звезда отправится обратно в центр экрана. }
If (star[loop].dist < 0.0) Тогда // Достигла ли звезда центра?
Начинать
star[loop].dist := star[loop].dist + 5.0 // Перемещение на 5 единиц наружу;
star[loop].r := random(256); // Назначаем новый красный компонент
star[loop].g := random(256); // Назначаем новый зеленый компонент
star[loop].b := random(256); // Назначаем новый синий компонент
Конец;
Конец;
Конец;
{
Теперь добавим код для мониторинга клавиатуры.
Перейдите вниз к WinMain(). Найдите строку SwapBuffers(hDC).
После этой строки мы добавим код мониторинга клавиатуры.
Код проверит, была ли нажата клавиша T.
Если нажать и отпустить клавишу T, код в блоке if будет выполнен.
Если мерцание имеет значение ЛОЖЬ, оно станет ИСТИНОЙ.
наоборот. Пока нажата клавиша T, tp становится TRUE.
Это предотвращает повторное выполнение кода внутри блока, если вы продолжаете нажимать клавишу T.
}
If (keys[ord('T')] And Not tp) then // Была ли нажата клавиша T и значение tp ЛОЖЬ
Начинать
tp := TRUE // Если да, установите tp в TRUE;
мерцать := Не мерцать // Инвертировать значение мерцания;
Конец;
{
Код ниже проверяет, была ли отпущена клавиша T.
Если да, установите tp=FALSE.
Если значение tp не ЛОЖЬ,
В противном случае ничего не произойдет, пока вы удерживаете клавишу T. Поэтому эта строка кода важна.
}
If (Notkeys[Ord('T')]) Тогда // Отпущена ли клавиша T?
Начинать
tp := FALSE // Если да, tp имеет значение FALSE;
Конец;
{Остальный код проверяет, нажаты ли клавиши со стрелками вверх и вниз, клавиша страницы вверх или клавиша страницы вниз. }
If (keys[VK_UP]) Тогда // Нажата ли клавиша со стрелкой вверх?
наклон := наклон - 0,5 // Наклон экрана вверх;
If (keys[VK_DOWN]) Тогда // Нажата ли клавиша со стрелкой вниз?
наклон := наклон + 0,5 // Наклон экрана вниз;
If (keys[VK_PRIOR]) Тогда // Нажата ли клавиша перехода вверх?
масштабирование := масштабирование - 0,2 // Уменьшение масштаба;
If (keys[VK_NEXT]) Тогда // Нажата ли клавиша перехода вниз?
масштабирование := масштабирование + 0,2 // Увеличение;
{
В этом уроке я изо всех сил стараюсь объяснить, как загрузить растровую текстуру в оттенках серого.
Удалив цвет фона (используя смешанные цвета), раскрасьте его снова и, наконец, заставьте его двигаться в 3D-сцене.
Я показал вам, как создавать красивые цветовые и анимационные эффекты.
Принцип реализации заключается в наложении копии растрового изображения на исходное растровое изображение.
До сих пор, пока вы хорошо понимаете все, чему я вас научил,
Вы сможете без проблем создать свою собственную 3D-демо.
Все основы раскрыты! }
//========мойлинг:
//Лекции 1-9 переведены. Как сказал NEHE, базовые знания в основном объяснены.
//Я просмотрел следующие руководства, и кажется, что они написаны другими людьми. Если есть хорошие примеры, я буду выборочно им следовать.
//Обновлено, я так устал, вздремну :), увидимся в следующий раз