Projekt okładki i stron tytułowych Grzegorz Laskowski

Ilustracja na okładce Luckyokneshakul / Shutterstock

Wydawca Łukasz Łopuszański

Redaktor Joanna Cierkońska

Koordynator produkcji Anna Bączkowska

Skład wersji elektronicznej na zlecenie Wydawnictwa Naukowego PWN Michał Nakoneczny / 88em.eu

Książka, którą nabyłeś, jest dziełem twórcy i wydawcy. Prosimy, abyś przestrzegał praw, jakie im przysługują. Jej zawartość możesz udostępnić nieodpłatnie osobom bliskim lub osobiście znanym. Ale nie publikuj jej w internecie. Jeśli cytujesz jej fragmenty, nie zmieniaj ich treści i koniecznie zaznacz, czyje to dzieło. A kopiując jej część, rób to jedynie na użytek osobisty.

Szanujmy cudzą własność i prawo

Więcej na www.legalnakultura.pl

Polska Izba Książki

Copyright © by Wydawnictwo Naukowe PWN SA

Warszawa 2014

ISBN 978-83-01-18047-8

eBook został przygotowany na podstawie wydania papierowego z 2014 r. (wyd. I)

Warszawa 2016

Wydawnictwo Naukowe PWN SA

tel. 22 69 54 321, faks 22 69 54 288

infolinia 801 33 33 88

e-mail: pwn@pwn.com.pl

www.pwn.pl

Spis treści

Wstęp

Rozdział 1. Inicjowanie okna w tradycyjnym OpenGL

Kilka ustaleń

Profil zgodności i profil rdzenny

Tworzenie projektu i dostosowanie go do współpracy z OpenGL

Tworzenie okna za pomocą funkcji WinAPI. Klasa okna

Przystosowanie okna do współpracy z OpenGL za pomocą funkcji biblioteki WGL. Klasa OknoGL

Informacje o używanej wersji OpenGL i karcie graficznej

Przygotowanie i rysowanie sceny

Kolory

Zabawy kamerą

Tryb pełnoekranowy

Przypisy

Rozdział 2. Wektory i macierze kwadratowe

Wektory

Zapis za pomocą wersorów

Iloczyn skalarny

Iloczyn wektorowy

Przykład

Macierze kwadratowe

Czym są macierze?

Iloczyn macierzy oraz mnożenie wektora przez macierz

Przykład

Mnożenie macierzy nie jest przemienne

Macierz jednostkowa

Macierz odwrotna

Wyznacznik macierzy

Przykład

Sprytny zapis iloczynu wektorowego

Obliczanie macierzy odwrotnej

Przykład

Macierz transponowana

Operator gwiazdka

Macierz ortonormalna

Macierz przekształcenia układu współrzędnych

Iloczyn skalarny wektorów i macierze

Rzut wektora na kierunek wyznaczony przez inny wektor. Składowa wektora w kierunku wyznaczonym przez inny wektor

Macierze 4 × 4

Zadania

Przypisy

Rozdział 3. Macierze w OpenGL i współrzędne jednorodne

Przekształcenia układów współrzędnych w potoku renderowania OpenGL

Macierz rzutowania

Macierz model-widok oraz macierze świata i widoku

Przypisy

Rozdział 4. Tworzenie kontekstu renderowania. Profil zgodności. Rozszerzenia

Korzystanie z rozszerzeń ARB

Zawirowania z tworzeniem kontekstu

Użycie glext.h i wglext.h

Użycie biblioteki GLEW zarządzającej rozszerzeniami

Użycie biblioteki DLL

Dołączenie kodu źródłowego GLEW do projektu aplikacji

Przypisy

Rozdział 5. Profil rdzenny. Bufory

Bufory werteksów

Tablica indeksów i bufor indeksów

Nawijanie i ukrywanie tylnych powierzchni

Konsolidacja buforów

Klasa werteksu

Przełączenie na profil rdzenny i liczenie strat

Przypisy

Rozdział 6. Shadery

Podstawy GLSL

Najprostsze shadery

Kompilacja shadera

Kolor i indeksy atrybutów werteksu

Trochę zabawy

Parametry shadera

Macierze świata, widoku i rzutowania

Kolumny i wiersze

Plan awaryjny

Zadania

Przypisy

Rozdział 7. Macierze używane w grafice 3D

Macierze rzutowania

Macierz rzutowania izometrycznego

Macierz rzutowania perspektywicznego

Różnice funkcji glFrustum i gluPerspective

Współrzędne viewportu

Przykład

Macierz świata

Translacja

Skalowanie i odbicia względem płaszczyzn układu współrzędnych

Obroty

Obrót wokół dowolnej osi

Macierze obrotu a kwaterniony jednostkowe

Złożenie obrotów i translacji – obrót wokół dowolnego punktu

Pochylenie

Rzut na płaszczyznę

Macierz widoku

Przykład złożenia macierzy widoku, świata i rzutowania

Zadania

Przypisy

Rozdział 8. Implementacje wektorów i macierzy

Wektory

Projekt interfejsu

Szablon TWektor

Specjalizacje szablonu dla wektora trój- i czteroelementowego

Testy

Macierze

Szablon TMacierzKwadratowa

Szablon TMacierzGrafika3D

Klasa MacierzOpenGL

Użycie obiektów macierzy w programie

Zadania

Przypisy

Rozdział 9. Sterowanie kamerą

Kontrola obrotów kamery za pomocą klawiszy

Tryby działania kamery

Kontrola orientacji kamery za pomocą myszy

Przesuwanie kamery myszą

Użycie rolki myszy

Swobodne obroty i ich powolne wygaszanie

Zadania

Przypisy

Rozdział 10. Aktorzy. Rysowanie brył. Animacja

Abstrakcja

Kwadrat

Zmiany w klasie okna

Pierwsza bryła

Optymalizacja rysowania wielu ciągów

Bufor głębi i pomijanie tylnych powierzchni

Kwadryki

Sfera

Aktor z buforem indeksów

Sfera z buforem indeksów

Końcowy zestaw brył: sfera, walec i sześcian

Animacja sceny

Zadania

Przypisy

Rozdział 11. Oświetlenie. Model Phonga

Model Phonga

Model Lamberta światła rozproszonego

Model rozbłysku Phonga

Uśrednianie normalnych

Definiowanie normalnych

Implementacja oświetlenia w shaderze werteksów

Aktualizacja położenia kamery

Ustawianie parametrów oświetlenia i materiału

Macierz normalnych

Osłabienie oświetlenia wraz z odległością

Obliczanie oświetlenia

Cel-shading

Zadania

Przypisy

Rozdział 12. Cienie rzucane

Implementacja metody rzutowania cieni

Materiał jako źródło informacji o kolorze aktorów

Przypisy

Rozdział 13. Odwzorowanie tekstur

Współrzędne teksturowania

Filtrowanie tekstur i mipmapy

Zmiany w klasie werteksu i klasach aktorów

Wczytanie tekstur

Przygotowanie zestawu tekstur

Implementacja teksturowania w shaderze fragmentów

Tworzenie i usuwanie tekstur

Zawijanie tekstur

Tekstury w służbie realizmu

Zadanie

Przypisy

Rozdział 14. Użycie wielu tekstur

Dwie tekstury

Kombinacje tekstur

Globus i odwzorowywanie rozbłysku

Zadania

Przypisy

Dodatek. Biblioteki GLFW i GLM

Biblioteka GLM

Dodanie biblioteki GLM do projektu

Przykład użycia funkcji GLM

Biblioteka GLFW

Dodanie biblioteki GLFW do projektu

Inicjowanie okna z użyciem GLFW

Pętla główna

Obsługa zdarzeń

Przypisy

Wstęp

Oto podręcznik podstaw programowania grafiki trójwymiarowej za pomocą biblioteki OpenGL. Zaplanowałem go jako aktualizację książki Grafika. Fizyka. Metody numeryczne wydanej przez WN PWN w 2010 roku. Składała się z kilku części: pierwsza dotyczyła właśnie grafiki trójwymiarowej OpenGL, kolejne – fizyki klasycznej punktów materialnych i brył sztywnych oraz metod numerycznych używanych w symulacjach ciał miękkich i brył sztywnych.

Cztery lata, które minęły od wydania tamtej książki, w najmniejszym stopniu nie wpłynęły na aktualność opisanej w niej fizyki. Niestety część dotycząca grafiki zdążyła się bardzo zestarzeć. Niedługo po wydaniu książki, w marcu 2010 roku pojawiły się nowe wersje OpenGL o numerach 3.3 i 4.0, które w tej książce będę nazywał nowoczesnym OpenGL. Problem polega na tym, że te wersje nie dodają do poprzednich nowych funkcji, rozbudowujących je o nowe możliwości. Mamy tu raczej do czynienia z nowym otwarciem albo wręcz rewolucją. Wiele funkcji tradycyjnych, często wykorzystywanych, uznano za przestarzałe i są one obsługiwane wyłącznie w mniej wydajnym profilu zgodności. Profil rdzenny pozbawiony jest takich udogodnień, jak natychmiastowe przesyłanie werteksów, obsługa macierzy czy domyślne shadery implementujące oświetlenie i teksturowanie. To sporo rzeczy, które teraz należy obsłużyć w inny sposób – korzystając z bibliotek znalezionych w internecie lub zaimplementować samodzielnie. Wysiłek, jaki wiąże się z przejściem do nowoczesnego OpenGL w profilu rdzennym, jest na tyle duży, że wielu programistów nie chce go podejmować. W rezultacie sporo nowych aplikacji nadal korzysta z OpenGL w profilu zgodności, nie wykorzystując pełnych możliwości współczesnych kart graficznych.

Zadaniem, które sobie postawiłem, pisząc tę książkę, było wprowadzenie Czytelnika w świat nowoczesnego, ale jednocześnie bardziej wymagającego OpenGL, a na jego przykładzie – w świat nowoczesnej grafiki trójwymiarowej, nie uciekając się do gotowych rozwiązań. Nie chodzi mi zatem o to, żeby ominąć problem, korzystając z gotowych bibliotek, a o samodzielną implementację całego potrzebnego kodu. Dla przykładu: w przypadku macierzy można by użyć popularnej biblioteki GLM, która nie wymaga znajomości algebry macierzy i umiejętności jej samodzielnej implementacji, a przy tym jest już dobrze przetestowana i wobec tego wiarygodna. Ale wówczas nie wykorzystalibyśmy tej szansy, jaką daje nowoczesny OpenGL – szansy pełnego zrozumienia podstaw grafiki 3D, a w tym macierzy (przynajmniej w zakresie, w jakim są potrzebne w grafice). Idąc tym tropem, przedstawiam też kod, który korzystając wyłącznie z funkcji WinAPI, umożliwia tworzenie okien gotowych do wyświetlania grafiki generowanej z użyciem OpenGL w profilu rdzennym, wraz z obsługą klawiatury i myszy. Bibliotekę GLFW, która mogłaby nas w tym wyręczyć, podobnie jak bibliotekę GLM, omawiam w dodatku do książki.

Takie podejście powoduje, że w wielu miejscach książka ma bardzo „techniczny” charakter. Opisuję w niej każdą zmianę kodu, starając się unikać niedopowiedzeń – każdy zmodyfikowany fragment jest zaznaczony i omówiony. Jedną z konsekwencji takiego podejścia jest spora różnorodność omawianych zagadnień. Mimo że głównym tematem pozostaje oczywiście grafika 3D, omawiam także zagadnienia związane z WinAPI, takie jak tworzenie okna, obsługa urządzeń wejścia czy implementacja wektorów i macierzy w C++. Korzystam również z języka GLSL pozwalającego na bezpośrednie programowanie karty graficznej. Należy się wobec tego spodziewać sporej liczby listingów, które trzeba będzie analizować.

Choć książka pomyślana jest jako podręcznik akademicki, w którym nie stronię od wzorów (w części rozdziałów jest ich nawet całkiem sporo) i który pozwoli studentowi lub osobie zainteresowanej programowaniem grafiki 3D na stosunkowo pełne zrozumienie podstaw tej dziedziny, poziom trudności nie jest wyśrubowany. Wzory nie wykraczają poza proste operacje na macierzach.

Książkę tę można traktować również jako opis prostej platformy do tworzenia aplikacji korzystających z grafiki 3D w OpenGL. Wszystkie elementy zamknięte są w klasy (np. Okno lub Aktor), których można swobodnie używać w swoich projektach, w postaci opisanej w książce lub dowolnie przekształconych.

A zatem Czytelnik trzyma w ręku podręcznik podstaw grafiki trójwymiarowej omawianych na przykładzie OpenGL. Zasadne jest pytanie, dlaczego wybrałem OpenGL, skoro świat gier komputerowych jest dziś zdominowany przez konkurencyjny DirectX Microsoftu. Owa dominacja jest niezaprzeczalna i niewiele zmienia fakt, że OpenGL znajduje obecnie nowe przyczółki: jest jedynym API pozwalającym na tworzenie aplikacji 3D w systemie Android (mam na myśli OpenGL ES), obiecujące jest także włączenie go do HTML5 (moduł WebGL). Najważniejszym powodem wyboru OpenGL było jednak coś innego. Zaimponowało mi śmiałe posunięcie grupy Khronos zarządzającej rozwojem OpenGL, która zdecydowała się na odświeżenie tego interfejsu programistycznego, dostosowując go do możliwości współczesnych kart graficznych, ale jednocześnie rezygnując ze zgodności ze starszymi wersjami. Biorąc pod uwagę model dystrybucji nowych wersji, mocno związany z zakupem nowego sprzętu, to było bardzo odważne posunięcie. Wprawdzie nie do końca udało się je konsekwentnie utrzymać – konieczny był kompromis w postaci profilu zgodności, ale profil rdzenny jest coraz częściej używany i powoli staje się standardem OpenGL, choć uczciwie trzeba przyznać, że nadal daleko mu do zdobycia dominacji na rynku gier.

Jacek Matulewski, maj 2014 rok