Я вирішив трохи написати про аспект програмування та про те, як компоненти спілкуються один з одним. Можливо, воно проллє трохи світла на певні ділянки.
Презентація
Для чого потрібно мати навіть те єдине зображення, яке ви розмістили у своєму запитанні, на екрані?
Існує багато способів намалювати трикутник на екрані. Для простоти припустимо, що не використовувалися вершинні буфери. ( Буфер вершин - це область пам’яті, де ви зберігаєте координати.) Припустимо, програма просто повідомила конвеєр обробки графіки про кожну вершину (вершина - це лише координата в просторі) підряд.
Але , перш ніж ми зможемо що-небудь намалювати, спершу треба виконати ліси. Ми побачимо, чому пізніше:
// 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 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 буде так швидко, що я навіть не знав, як це виразити. Відправлення тисяч вершин з процесора в GPU, кожен окремий кадр, швидше за все, взагалі не проблема.
Очищення екрана може зайняти мілісекунд або гірше (майте на увазі, для малювання кожного кадру зазвичай у вас лише близько 16 мілісекунд часу), залежно від того, наскільки великий розмір поля перегляду. Щоб очистити це, OpenGL повинен намалювати кожен піксель у кольорі, який ви хочете очистити, і це може бути мільйони пікселів.
Крім цього, ми можемо лише запитати OpenGL про можливості нашого графічного адаптера (максимальна роздільна здатність, максимум згладжування, максимальна глибина кольору ...).
Але ми також можемо заповнити текстуру пікселями, кожен з яких має певний колір. Кожен піксель, таким чином, має значення, а текстура - це гігантський "файл", наповнений даними. Ми можемо завантажити це у відеокарту (створивши буфер текстури), а потім завантажити шейдер , сказати цьому шейдеру використовувати нашу текстуру в якості вхідного даних та виконати кілька надзвичайно важких обчислень на нашому "файлі".
Потім ми можемо «перетворити» результат нашого обчислення (у вигляді нових кольорів) в нову текстуру.
Ось так ви можете змусити GPU працювати для вас іншими способами. Я припускаю, що CUDA працює аналогічно цьому аспекту, але я ніколи не мав можливості працювати з цим.
Ми дійсно лише трохи торкнулися всієї теми. 3D-графічне програмування - це пекло звіра.
Джерело зображення