Рисунок куба вершина грань

Как построить куб в перспективе: пошаговый метод

Здравствуйте, подписчики моего блога!

Многие сталкивались со сложностью в рисовании, когда хотели изобразить сложный по форме объект. Дело в том, что в основе всех сложных структур скрываются простые формы. И куб – одна из таких форм. Сегодня мы подробно разберем, как построить куб.

Перспектива

Куб – это геометрическое тело, состоящее из 6 плоскостей. Знаете, чем отличается куб от квадрата? Куб – это объемная фигура. А при рисовании любых объемных фигур нужно помнить о перспективе.

Из-за законов перспективы стороны куба будут сокращаться, иными словами становиться меньше.

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

Линия горизонта – это уровень глаз художника. На ней будут располагаться две точки схода. В каждую из этих точек придёт по 4 линии. Наглядно такой рисунок будет выглядеть следующим образом:

Рисуем пошагово

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

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

Шаг 1 — Композиция

Композиция – это начало любой работы. От точного нахождения композиции зависит 50% успеха работы.

Следует оставить чуть больше расстояния сверху, чем снизу. Это придаст ощущение весомости.

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

Шаг 2 — Сокращение

Плоскости в кубе заметно удаляются в силу перспективного сокращения. Чтобы верно определить это сокращение на листе, необходимо определить углы наклона ребер.

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

Шаг — 3 — Линейная перспектива

Чтобы куб «лег» в пространство, ребра, удаленные от нас, мы рисуем выше и меньше. Этот прием лежит в основе линейной перспективы. Найдите, где заканчиваются эти ребра и обозначьте их точками. Сечение куба по трем точкам – это верных способ при построении.

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

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

Шаг 4 — Проверка пропорций

Также можно использовать метод визирования. Вы наверняка замечали, как художники вытягивают руку и измеряют пропорции? Это и есть метод визирования.

Для того, чтобы проверить пропорции, закройте один глаз, возьмите карандаш и на вытянутой руке сопоставьте его кончик с верхом переднего ребра куба. Большой палец передвиньте в то место, где ребро заканчивается.

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

С опытом художники измеряют пропорции «на глаз», это значит, что они обходятся без метода визирования. Их глаз настолько натренирован, что видит размеры без измерений.

Итак, линейное построение подошло к концу, а значит, мы переходим к воздушной перспективе или, проще говоря, к штрихованию.

Штрихование

Основная задача рисунка – это передать объем. Внимательно посмотрите на предмет, определите для себя тональность каждой из сторон.

Штриховать следует по форме куба. Внимательно подходите к штриховке, не стоит излишне чернить, все-таки мы рисуем гипсовый куб.

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

Используйте карандаши разной мягкости. Для освещённой стороны отлично подойдет твердый карандаш – H, для полутона – HB, а для теневой стороны – B.

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

Падающая тень

Как построить тень от куба? Чтобы падающая тень была убедительной, давайте рассмотрим основные принципы построения тени на поверхности листа.

  1. Определяем местонахождения источника света.
  2. Проводим перпендикуляр от источника света к плоскости, где находится наш предмет.
  3. От точки, где перпендикуляр пересекается с плоскостью, проводим линии в касание с углами куба.
  4. Проецируем воображаемые лучи от источника света, которые проходят по вершинам 5 куба. Намечаем точки соприкосновения лучей и плоскости.
  5. Соединяем найденные точки на плоскости и получаем конфигурацию тени.

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

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

Заключение

Мы поговорили о сечении куба плоскостью, о том, как правильно построить куб с натуры в перспективе. Рисование простых геометрических тел весьма полезно для начинающих и тех, кто хочет овладеть академическим рисунком.

Геометрия и расположение плоскостей в пространстве очень хорошо тренирует зрительное восприятие.

Если у вас нет гипсового куба, не беда, смастерите куб из бумаги. Для первых упражнений главное – это понять основные принципы при построении. А затем, можно будет перейти к гипсовым фигурам.

Удачи в творчестве! Рисуйте то, что любите!

Если вам понравилась статья, поделитесь ей с друзьями!

Источник

Рисунок куба вершина грань

В этой статье мы научимся рисовать простой 3D объект — куб. Также мы запрограммируем анимацию куба и виртуальную камеру, позволяющую взглянуть на куб глазами зрителя.

Содержание

Вершины куба

В чём OpenGL измеряет расстояния в трёхмерном мире? В 2D графике всё понятно: у нас есть пиксели. На трёхмерной проекции объекты одинакового размера могут дать разные проекции из-за перспективного искажения: чем дальше поверхность или линия, тем она меньше. Поэтому OpenGL измеряет расстояния в единицах измерения, не уточняя, в каких именно. Вы можете измерять в метрах, попугаях или в футах — на картинке это не имеет разницы, потому что исходные размеры не совпадают с проекциями.

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

Триангуляция куба

После того, как выписали список вершин куба, следует разпределить вершины по треугольникам. У куба 6 квадратных граней, их можно описать 12-ю треугольниками. При этом вершины каждого треугольника следует перечислять по часовой стрелке для наблюдателя, смотрящего снаружи куба на этот треугольник. В противном случае треугольник станет поверхностью, видимой изнутри куба, что нарушает физические законы.

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

Теперь, глядя на иллюстрацию, можно перечислить все 12 треугольников, составляющих 6 граней.

Рисование в Immediate Mode

Для непосредственного рисования граней куба мы применим не самый производительный метод, который к тому же устарел в современных версиях OpenGL: рисование в блоке glBegin/glEnd, также известное как OpenGL Immediate Mode.

Настройка фиксированной камеры

В OpenGL при использовании фиксированного конвейера есть ровно две матрицы, относящихся к трансформациям точек и объектов:

  • GL_PROJECTION моделирует ортографическое или перспективное преобразование от трёхмерной усечённой пирамиды (т.е. от области видимости камеры) к трёхмерному кубу с длиной ребра, равной 2 (т.е. к нормализованному пространству).
  • GL_MODELVIEW сочетает в себе два преобразования: от локальных координат объекта к мировым координатам, а также от мировых координат к координатам камеры.

За рамками фиксированного конвейера можно использовать столько матриц, сколько захочется — но сейчас мы ограничены. Кроме того, нам приходится смешивать матрицы:

  • поведение камеры описывается как ортографическим или перспективным преобразованием, так и положением камеры в мировом пространстве, то есть для моделирования камеры нужны GL_PROJECTION и GL_MODELVIEW одновременно
  • c другой стороны, для трансформаций над телами — например, вращения куба с помощью умножения координат на матрицу — нам нужна матрица GL_MODELVIEW.

Обычно при программировании действуют так:

  • матрицу GL_PROJECTION обычно настраивают один раз для перспективного преобразования
  • матрицу GL_MODELVIEW постоянно модифицируют, когда локальная система координат очередного объекта не совпадает с мировой системой координат

Начнём настройку камеры с GL_MODELVIEW: зададим матрицу так, как будто бы камера смотрит с позиции eye на точку center , при этом направление “вверх” камеры задаёт вектор up :

Для перспективного преобразования достаточно создать матрицу с помощью функции glm::perspective . Она принимает на вход несколько удобных для программиста параметров преобразования: горизонтальный угол обзора камеры (англ. field of view), соотношение ширины и высоты (англ. aspect), а также две граничных координаты для отсечения слишком близких к камере и слишком далёких от камеры объектов. Для лучшего понимания взгляните на иллюстрацию:

Решение проблем невидимых поверхностей

При программном рисовании трёхмерного мира, состоящего из объемных тел с непрерывной поверхностью (англ. solid bodies), возникает вопрос: как не нарисовать невидимые поверхности? Например, на рисунке горы, покрытой лесом, мы не должны увидеть ни деревьев на противоположном к нам склоне, ни поверхности самого склона.

У художников для решения задачи есть свой алгоритм: сначала они рисуют композицию заднего фона, затем сверху покрывают средний фон, и, наконец, выводят передний фон:

Этот алгоритм так и называется — “алгоритм художника” (англ. painter’s algorithm). В компьютерной графике он иногда применим, но с модификацией: вместо деления объектов на три группы (задний фон, средний фон и передний фон) придётся отсортировать все объекты и вывести их в порядке приближения. К сожалению, не всегда объекты воможно отсортировать: это известно как “проблема художника” (англ. painter’s problem).

Для решения проблемы художника в OpenGL сделано следующее:

  • все поверхности представляются как базовые примитивы: в ранних версиях OpenGL это были точки, линии, треугольники и четырёхугольники, а в более поздних остались только треугольники
  • используется тест буфера глубины, т.е. модификация алгоритма художника, работающая с неделимыми фрагментами фигур
  • используется отсечение граней, развернувшихся невидимой стороной после умножения на матрицы GL_MODELVIEW и GL_PROJECTION

Тест буфера глубины

Буфер глубины OpenGL — это двумерная матрица дополнительных данных, где каждому пикселю соответствует одно значение float: глубина фрагмента примитива, оказавшегося ближе к пикселю, чем остальные фрагменты примитивов, проецируемые на тот же пиксель. Это позволяет реализовать попиксельный алгоритм художника: после приведения в нормализованное пространство каждая примитивная фигура будет разбита на фрагменты, для которых будет проведён тест глубины.

Фрагмент — это атомарная, то есть неделимая, часть фигуры. Если видеокарта не совершает сглаживание, то один фрагмент станет одним пикселем фигуры.

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

Как и любой другой буфер OpenGL, буфер глубины следует очищать. Для этого вызов glClear должен получать ещё один флаг GL_DEPTH_BUFFER_BIT :

Также нужно после создания контекста включить режим теста глубины командой glEnable:

Отсечение задних граней

OpenGL рассчитан на дополнительное отсечение невидимых поверхностей, построенное по принципу отсечения задних граней. По умолчанию включён режим, аналогичный вызову glFrontFace(GL_CCW) , и OpenGL делит примитивы на две группы:

  • те, вершины которых перечисляются против часовой стрелки (GL_CCW), становятся передними гранями (GL_FRONT)
  • те, вершины которых перечисляются по часовой стрелке (GL_CW), становятся задним гранями (GL_BACK)

Вызов glFrontFace(GL_CW) изменит классификацию на обратную: перечисление по часовой даст переднюю грань, перечисление против часовой даст заднюю.

Независимо от того, в каком порядке были заданы исходные вершины, если после всех преобразований грань объёмного тела повёрнута к нам лицевой стороной — порядок обхода сохранится, а если её перекроют другие грани — порядок обхода сменится на противоположный.

Режим отсечения граней можно включить командой glEnable(GL_CULL_FACE) , после чего можно выбрать способ отсечения: убирать задние грани (GL_BACK), передние грани (GL_FRONT) или оба вида граней (GL_FRONT_AND_BACK).

Соберём всю инициализацию состояния OpenGL в метод OnWindowInit, который будет вызываться один раз поле инициализации окна

Чтобы метод OnWindowInit был вызван своевременно, его можно объявить виртуальным в классе CAbstractWindow и вызывать в методе Show:

Диагностика проблем

При выводе трёхмерных тел встречается ряд типовых ошибок. Если что-то не работает, пройдитесь по следующему чеклисту:

  • вы не забыли вызвать glBegin/glEnd до и после вызова glColor/glVertex?
  • тело не выпадает из порта просмотра из-за матрицы GL_PROJECTION?
  • тело не обрезается дальней и ближней плоскостями отсечения из-за матрицы GL_PROJECTION?
  • в массиве индексов нумерация вершин начинается с нуля?
  • включён тест глубины и режим отсечения задних граней?
  • в массиве индексов вершины примитивов (треугольников и четырёхугольников) перечислены по часовой стрелке для внешнего наблюдателя?

Если всё нормально, то вы получите статичное изображение куба без возможности поменять положение камеры:

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

Трёхмерная система координат

В OpenGL используется правосторонняя система координат, в которых пользователь может задавать вершины примитивов, из которых состоят трехмерные объекты. Правосторонней система координат называется потому, что ее можно образовать при помощи большого, указательного и среднего пальцев правой руки, задающих направления координатных осей X, Y и Z соответственно.

Система координат задаётся точкой отсчёта и координатными осями, которые, в свою очередь, задают направления и длины трёх единичных векторов (1, 0, 0), (0, 1, 0) и (0, 0, 1). Как точка отсчёта, так и координатные оси могут меняться при переходе из одной системы координат в другую.

Например, представьте себе систему координат комнаты, где в качестве центра взята точка в геометрическом центре пола, а ось z указывает вверх, и расстояния измеряются в метрах. Тогда точки головы человека в комнате всегда будут иметь координату z, большую нуля, обычно в диапазоне (1.6; 1.8) . Если же перейти в другую систему отсчёта, где центром служит точка в геометрическом центре потолка, то голова человека в комнате будет иметь отрицательную координату z.

В любой трёхмерной сцене есть система координат, которую можно считать мировой системой коодинат. Это очень удобно: любая точка или вектор в мировой системе координат представляется однозначно. Благодаря этому, если у нас есть несколько локальных систем координат (например: комната, салон автомобиля, камера), и мы знаем способ преобразования из любой системы в мировые координаты, то мы можем спокойно перейти, например, от системы координат комнаты к системе координат камеры:

О локальных системах координат можно сказать следующее:

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

Самый удивительный факт: любое элементарное трёхмерное преобразование, а также их комбинацию можно представить в виде матрицы 4×4! Чтобы понять, как это происходит, разберёмся с однородным представлением точек и векторов.

Единый тип данных для точек и векторов

Давайте представим, что нам поставили задачу: создать единый тип данных, способный хранить как трёхмерную точку, так и трёхмерный вектор. Этот тип данных должен поддерживать операции вращения, перемещения и масштабирования, причём по-разному для векторов и точек: например, перемещение вектора никак не меняет его, а перемещение точки изменяет эту точку.

Если использовать процедурную парадигму программирования, получится нечто такое:

Объектно-ориентированный подход предлагает такой вариант:

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

Проблема решается с помощью математического приёма — однородного представления точек и векторов.

Однородное представление точек и векторов.

Давайте будем считать, что трёхмерная точка (x, y, z) хранится как четырёхкомпонентный вектор (x, y, z, 1) . А вектор хранится как (x, y, z, 0) . Всего лишь одно флаговое значение в конце позволяет избежать любых ветвлений в алгоритмах трансформации векторов и точек. Это происходит благодаря свойствам алгебры матриц.

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

Как ни странно, умножение 4-х компонентного вектора на матрицу 4×4 тоже возможно! Для этого достаточно считать 4-х компонентный вектор матрицей 4×1. После умножения получится новый 4-х компонентный вектор.

Ещё более удивительно, что любую комбинацию трёхмерных перемещений, поворотов, вращений (и не только их!) можно представить как всего лишь одну матрицу 4×4, называемую матрицей трёхмерной трансформации. При этом умножение матрицы на трёхмерную точки или вектор, записанный в однородном представлении, даёт новую точку или вектор именно так, как этого требуют правила преобразования точек и векторов. Никаких ветвлений, и никакой магии!

Класс CAnimatedCube

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

  • умножение матрицы преобразования на вектор или точку в однородном представлении даёт преобразованный вектор или точку
  • можно легко составить базовую матрицу, представляющих одно элементарное аффинное преобразование
  • умножение матрицы A на матрицу B даёт новую матрицу, которая описывает новую трансформацию, созданную путём применения трансформации B, а затем A (именно в таком порядке)
  • умножение матриц не коммутативно: вы не можете заменить A*B на B*A
  • инвертирование матрицы (например, с помощью детерминанта матрицы) даёт матрицу обратной трансформации, которая вернёт точку или вектор в исходное состояние — в идеальном мире. В дискретном мире компьютеров обратное преобразование может быть чуть-чуть неточным из-за особенностей представления типов float и double.

Для демонстрации этих правил расширим класс CIdentityCube из предыдущего урока. Новый класс будет называться CAnimatedCube, и он будет демонстрировать работу трёх базовых аффинных трансформаций. Смена и продвижение анимации будет выполняться в методе Update, вызываемом периодически снаружи.

С учётом перечисленных выше правил, мы можем написать метод Draw для анимированного куба. При этом вместо glLoadMatixf следует применить glMultMatrixf, чтобы вместо замены уже существующей трансформации всего лишь модифицировать её. Если мы заменим матрицу GL_MODELVIEW, камера будет работать некорректно.

Функции для работы с аффинными трансформациями (и не только!) можно найти в GML:

Единичная матрица

Единичная матрица (матрица идентичности, англ. identity matrix) задает преобразование, при котором точки и векторы остаются без изменений, отображаясь сами в себя. Посмотрите сами на перемножение этой матрицы и точки/вектора:

Если мы не хотим возвращать из метода GetAnimationTransform() какую-либо преобразующую трансформацию, мы можем просто вернуть единичную матрицу. Именно такую матрицу создаёт конструктор по умолчанию класса glm::mat4. Теперь мы можем заложить каркас метода GetAnimationTransform:

Матрица перемещения

Матрица перемещения воздействует на точку, но вектор сохраняет неизменным. Действует она так:

В GLM есть функция glm::translate, умножающая переданную матрицу на матрицу перемещения. Чтобы анимировать куб, будем вычислять смещение по оси Ox в каждый момент времени. После этого получение матрицы перемещения будет очень простым:

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

В процессе анимации от 0% до 100% куб должен один или несколько раз прыгнуть в сторону и затем вернуться обратно. Для этого воспользуемся делением с остатком, а также формулой расчёта расстояния на основе начальной скорости и противоположно направленного ускорения. Можно написать так:

Матрица масштабирования

Матрица масштабирования воздействует как на точку, так и на вектор, изменяя соответственно удалённость точки от начала координат и длину вектора. Действует так:

В GLM есть функция glm::scale, умножающая переданную матрицу на матрицу масштабирования, имеющую потенциально разные коэффициенты масштабирования для трёх разных компонентов вектора.

Давайте используем эту функцию, чтобы реализовать пульсирование куба — сжатие от нормальных размеров к нулевым и обратно:

Матрица поворота

Матрица поворота воздействует и на точку, и на вектор, при этом удалённость точки от начала координат и длина вектора не меняются. Существует Теорема Эйлера, согласно которой любой поворот в трёхмерном пространстве вокруг произвольной оси можно представить как последовательность трёх поворотов вокруг трёх базовых осей системы координат в определённом порядке и на определённые углы:

Перечислим три матрицы поворота на угол “a” вокруг трёх осей системы координат (посмотреть, что получается при перемножении, вы можете самостоятельно):

В GLM есть функция glm::rotate, умножающая переданную матрицу на матрицу поворота вокруг переданного произвольного вектора оси на переданный угол. Как уже было сказано ранее, следует настроить GLM так, чтобы углы выдавались в радианах — иначе вы будете получать предупреждения об использовании устаревшего API. Проверьте, что в stdafx.h перед включением GLM объявлен макрос GLM_FORCE_RADIANS:

Матрицы поворота сложнее масштабирования или перемещения, но применить их для анимации проще: нужно всего лишь задать скорость поворота такой, чтобы за время анимации куб повернулся целое число раз:

Вот так это будет выглядеть к концу урока:

Как создать камеру

В OpenGL в режиме версии 1.x есть две трансформирующих вершины матрицы: GL_MODELVIEW и GL_PROJECTION. Матрица GL_MODELVIEW объединяет к себе как переход от локальной системы координат к мировой (Model), так и переход от мировых координат к системе коодинат камеры (View). Класс CCamera будет возвращать только одну компоненту GL_MODELVIEW: матрицу вида, созданную функцией glm::lookAt.

Правила движения камеры будут следующими:

  • камера всегда смотрит на точку (0, 0, 0), вращается вокруг неё, приближается к ней или отдаляется
  • для вращения камеры служат клавиши “Влево” и “Вправо” либо “A” и “D” на клавиатуре
  • для приближения и отдаления служат клавиши “Вперёд” и “Назад” либо “W” и “S” на клавиатуре
  • камера не может приближаться ближе чем на 1.5f и не может отдаляться дальше чем на 30.f
  • камера не должна двигаться рывками, и даже при неравных интервалах перерисовки кадра движение должно оставаться плавным, т.е. зависит от deltaTime между кадрами

С учётом сказанного, спроектируем следующий интерфейс класса:

Методы Update, OnKeyDown, OnKeyUp должны вызываться извне — например, из класса окна. При этом методы обработки событий возвращают true, если событие было обработано, чтобы класс окна мог не рассылать это событие далее другим объектам.

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

  • пользователь нажал “Влево”, затем “Вправо”, потом отпустил “Влево”; после этого камера должна вращаться вправо
  • пользователь нажал “Влево” и “Вперёд”; после этого камера должна вращаться влево и при этом приближаться
  • пользователь нажал “Вперёд” и “Назад”; при этом камера может не двигаться или двигаться в одном приоритетном направлении — оба варианта хороши

Чтобы отслеживать нажатие только нужных клавиш, создадим функцию-предикат ShouldTrackKeyPressed:

Также подключим заголовок с функциями вращения вектора, введём вспомогательные константы и функции, позволяющие получить скорость поворота и скорость приближения (возможно, нулевые или отрицательные) на основе информации о нажатых клавишах:

После этого с небольшим применением линейной алгебры мы можем реализовать методы класса CCamera:

Изменения в CWindow

Теперь класс CWindow должен хранить три объекта:

Конструктор CCamera требует два аргумента, их можно задать следующим образом:

CWindow::CWindow() : m_camera(CAMERA_INITIAL_ROTATION, CAMERA_INITIAL_DISTANCE)

В методе OnUpdateWindow мы должны вызывать метод Update у всех трёх объектов системы:

В методе Draw немного схитрим: применим вызов glTranslate (вместо нормальной работы с функциями GLM), чтобы развести два куба в стороны:

Метод SetupView станет проще, потому что мы можем не вычислять матрицу GL_MODELVIEW, а получить её начальное (для кадра) значение у камеры.

Наконец, следует перегрузить методы OnKeyDown/OnKeyUp класса CAbstractInputControlWindow в классе CWindow:

Конец!

Теперь вы можете взять полный пример (github.com) или посмотреть, каким будет результат запуска (в виде статичного скриншота):

Чтобы сделать результат более наглядным, была сделана серия скриншотов, которые затем были объединены в gif с помощью GIMP:

Источник

Оцените статью
Юридический портал
Adblock
detector