Я решил написать немного о программном аспекте и о том, как компоненты взаимодействуют друг с другом. Может быть, это поможет пролить свет на некоторые области.
Презентация
Что нужно, чтобы нарисовать на экране то единственное изображение, которое вы разместили в своем вопросе?
Есть много способов нарисовать треугольник на экране. Для простоты предположим, что буферы вершин не использовались. (Буфер вершин - это область памяти, где вы храните координаты.) Давайте предположим, что программа просто сообщила конвейеру обработки графики о каждой отдельной вершине (вершина - это просто координата в пространстве) в строке.
Но, прежде чем мы сможем что-то нарисовать, мы должны сначала запустить некоторые строительные леса. Мы увидим почему позже:
// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Drawing Using Triangles
glBegin(GL_TRIANGLES);
// Red
glColor3f(1.0f,0.0f,0.0f);
// Top Of Triangle (Front)
glVertex3f( 0.0f, 1.0f, 0.0f);
// Green
glColor3f(0.0f,1.0f,0.0f);
// Left Of Triangle (Front)
glVertex3f(-1.0f,-1.0f, 1.0f);
// Blue
glColor3f(0.0f,0.0f,1.0f);
// Right Of Triangle (Front)
glVertex3f( 1.0f,-1.0f, 1.0f);
// Done Drawing
glEnd();
Так, что это сделало?
Когда вы пишете программу, которая хочет использовать видеокарту, вы обычно выбираете какой-то интерфейс для драйвера. Некоторые хорошо известные интерфейсы для драйвера:
Для этого примера мы будем использовать OpenGL. Теперь, ваш интерфейс драйвера является то , что дает вам все инструменты, необходимые , чтобы сделать вашу программу поговорить с видеокартой (или водителем, который затем отвечает на карту).
Этот интерфейс должен дать вам определенные инструменты. Эти инструменты принимают форму API, который вы можете вызвать из своей программы.
Этот API - это то, что мы видим в примере выше. Давайте внимательнее посмотрим.
Леса
Прежде чем вы действительно сможете сделать какой-либо рисунок, вам нужно будет выполнить настройку. Вы должны определить ваш видовой экран (область, которая будет фактически визуализироваться), вашу перспективу (камеру в ваш мир), какое сглаживание вы будете использовать (чтобы сгладить края вашего треугольника)...
Но мы не будем на это смотреть. Мы просто взглянем на то, что вам придется делать в каждом кадре. Подобно:
Очистка экрана
Графический конвейер не собирается очищать экран для каждого кадра. Вы должны будете сказать это. Зачем? Вот почему:
Если вы не очистите экран, вы будете просто рисовать над ним каждый кадр. Вот почему мы вызываем glClear
с набором GL_COLOR_BUFFER_BIT
. Другой бит (GL_DEPTH_BUFFER_BIT
) говорит OpenGL очистить буфер глубины . Этот буфер используется для определения того, какие пиксели находятся впереди (или позади) других пикселей.
преобразование
Источник изображения
Преобразование - это та часть, где мы берем все входные координаты (вершины нашего треугольника) и применяем нашу матрицу ModelView. Это матрица, которая объясняет, как наша модель (вершины) вращается, масштабируется и переводится (перемещается).
Далее мы применяем нашу матрицу проекции. Это перемещает все координаты так, чтобы они правильно смотрели на нашу камеру.
Теперь мы снова преобразуем нашу матрицу Viewport. Мы делаем это, чтобы масштабировать нашу модель до размеров нашего монитора. Теперь у нас есть набор вершин, которые готовы к визуализации!
Мы вернемся к преобразованию чуть позже.
Рисование
Чтобы нарисовать треугольник, мы можем просто сказать OpenGL начать новый список треугольников , вызвав glBegin
с константой GL_TRIANGLES
.
Есть и другие формы, которые вы можете нарисовать. Как треугольная полоса или треугольный веер . Это в первую очередь оптимизация, поскольку они требуют меньше связи между процессором и графическим процессором, чтобы нарисовать одинаковое количество треугольников.
После этого мы можем предоставить список наборов из 3 вершин, которые должны составлять каждый треугольник. Каждый треугольник использует 3 координаты (как в 3D-пространстве). Кроме того, я также предоставляю цвет для каждой вершины, вызывая glColor3f
перед вызовом glVertex3f
.
Тень между 3 вершинами (3 угла треугольника) рассчитывается OpenGL автоматически. Он будет интерполировать цвет по всей поверхности многоугольника.
взаимодействие
Теперь, когда вы нажимаете на окно. Приложение должно только захватить сообщение окна, которое сигнализирует о щелчке. Затем вы можете запустить любое действие в вашей программе, которое вы хотите.
Это становится намного сложнее, когда вы хотите начать взаимодействовать с 3D-сценой.
Сначала вы должны четко знать, в каком пикселе пользователь щелкнул окно. Затем, принимая во внимание вашу перспективу , вы можете рассчитать направление луча от точки щелчка мыши до вашей сцены. Затем вы можете рассчитать, пересекается ли какой-либо объект в вашей сцене с этим лучом. Теперь вы знаете, нажал ли пользователь на объект.
Итак, как вы делаете это вращаться?
преобразование
Мне известны два типа преобразований, которые обычно применяются:
- Матричное преобразование
- Костная трансформация
Разница в том, что кости влияют на отдельные вершины. Матрицы всегда влияют на все нарисованные вершины одинаково. Давайте посмотрим на пример.
пример
Ранее мы загружали нашу матрицу идентичности перед тем, как нарисовать наш треугольник. Тождественная матрица - это матрица, которая просто не обеспечивает никакого преобразования . Таким образом, все, что я рисую, зависит только от моей точки зрения. Таким образом, треугольник не будет вращаться вообще.
Если я хочу повернуть его сейчас, я мог бы сам сделать математику (на процессоре) и просто вызвать glVertex3f
с другими координатами (которые вращаются). Или я мог бы позволить GPU делать всю работу, вызывая glRotatef
перед рисованием:
// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);
amount
, конечно, просто фиксированная стоимость. Если вы хотите анимировать , вам придется отслеживать amount
и увеличивать его каждый кадр.
Итак, подождите, что случилось со всеми матричными разговорами ранее?
В этом простом примере нам не нужно заботиться о матрицах. Мы просто называем glRotatef
и он позаботится обо всем этом для нас.
glRotate
производит вращение angle
градусов вокруг вектора xyz. Текущая матрица (см. GlMatrixMode) умножается на матрицу вращения с продуктом, заменяющим текущую матрицу, как если бы glMultMatrix вызывался со следующей матрицей в качестве аргумента:
x 2 1 - c + cx y z 1 - c - z sx z 1 - c + y s 0 y x 1 - c + z sy 2 1 - c + cy z 1 - c - x s 0 x z 1 - c - y sy z 1 - c + x sz 2 1 - c + c 0 0 0 0 1
Ну, спасибо за это!
Заключение
Становится очевидным, что с OpenGL много говорят. Но это ничего не говорит нам . Где связь?
Единственное, что OpenGL говорит нам в этом примере, это когда это будет сделано. Каждая операция займет определенное количество времени. Некоторые операции занимают невероятно много времени, другие - невероятно быстро.
Отправка вершины в GPU будет настолько быстрой, что я даже не знаю, как это выразить. Отправка тысяч вершин из ЦП в ГП, каждый кадр, скорее всего, не проблема.
Очистка экрана может занять миллисекунду или даже хуже (имейте в виду, что у вас обычно есть только около 16 миллисекунд времени для прорисовки каждого кадра), в зависимости от размера вашего окна просмотра. Чтобы очистить его, OpenGL должен нарисовать каждый пиксель в цвет, который вы хотите очистить, это может быть миллионы пикселей.
Кроме этого, мы можем только спросить OpenGL о возможностях нашего графического адаптера (максимальное разрешение, максимальное сглаживание, максимальная глубина цвета, ...).
Но мы также можем заполнить текстуру пикселями, каждый из которых имеет определенный цвет. Таким образом, каждый пиксель содержит значение, а текстура представляет собой гигантский "файл", заполненный данными. Мы можем загрузить это в графическую карту (создав текстурный буфер), затем загрузить шейдер, сказать этому шейдеру использовать нашу текстуру в качестве входных данных и выполнить некоторые чрезвычайно тяжелые вычисления для нашего "файла".
Затем мы можем "визуализировать" результат нашего вычисления (в виде новых цветов) в новую текстуру.
Вот как вы можете заставить работать графический процессор другими способами. Я предполагаю, что CUDA работает аналогично этому аспекту, но у меня никогда не было возможности работать с ним.
Мы действительно лишь слегка коснулись всей темы. Программирование трехмерной графики - адское чудовище.
Источник изображения