Я решил написать немного о программном аспекте и о том, как компоненты взаимодействуют друг с другом. Может быть, это поможет пролить свет на некоторые области.
Презентация
Что нужно, чтобы нарисовать на экране то единственное изображение, которое вы разместили в своем вопросе?
Есть много способов нарисовать треугольник на экране. Для простоты предположим, что буферы вершин не использовались. ( Буфер вершин - это область памяти, в которой хранятся координаты.) Предположим, что программа просто сообщила конвейеру графической обработки о каждой отдельной вершине (вершина - это просто координата в пространстве) в строке.
Но прежде чем мы сможем что-то нарисовать, мы должны сначала запустить некоторые строительные леса. Мы увидим почему позже:
// 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-сценой.
Сначала вы должны четко знать, в каком пикселе пользователь щелкнул окно. Затем, принимая во внимание вашу перспективу , вы можете рассчитать направление луча от точки щелчка мыши до вашей сцены. Затем вы можете рассчитать, пересекается ли какой-либо объект в вашей сцене с этим лучом . Теперь вы знаете, нажал ли пользователь на объект.
Итак, как вы делаете это вращаться?
преобразование
Мне известны два типа преобразований, которые обычно применяются:
- Матричное преобразование
- Костная трансформация
Разница в том, что кости влияют на отдельные вершины . Матрицы всегда влияют на все нарисованные вершины одинаково. Давайте посмотрим на пример.
пример
Ранее мы загружали нашу матрицу идентичности перед тем, как нарисовать наш треугольник. Тождественная матрица - это матрица, которая просто не обеспечивает никакого преобразования . Таким образом, все, что я рисую, зависит только от моей точки зрения. Таким образом, треугольник не будет вращаться вообще.
Если я хочу , чтобы повернуть его сейчас, я мог либо сделать математику сам (на CPU) и просто звонить 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 работает аналогично этому аспекту, но у меня никогда не было возможности работать с ним.
Мы действительно лишь слегка коснулись всей темы. Программирование 3D-графики - адское чудовище.
Источник изображения