Aujourd'hui, je vais montrer comment ouvrir une fenêtre et créer un contexte OpenGL. Il s'agit d'une tâche étonnamment difficile, OpenGL ne dispose toujours pas d'outils multiplateformes officiels pour créer le contexte, nous allons donc compter sur des bibliothèques tierces (dans ce cas GLFW et content). Il y a déjà beaucoup de mondes bonjour similaires sur Internet, mais je n'aime pas tout ce que j'ai vu: soit c'est très sophistiqué, soit les images dans les exemples sont très primitives (
ou les deux !). Un grand merci à tous les auteurs, mais je vais télécharger un autre tutoriel :)
Aujourd'hui, nous allons dessiner ceci:

Ce modèle a été peint par l'artiste
Samuel Sharit (arshlevon) , merci beaucoup de m'avoir permis de l'utiliser dans le cadre de mon cours magistral sur l'infographie!
Étape 0: lecture de l'étain
D'une manière générale, il est préférable (mais pas nécessaire) de donner cette conférence après avoir lu tout
mon cours sur le
ferblantier . Pour ceux qui ne parlent pas anglais, ce cours est disponible
sur le hub , bien que je ne supporte plus la version russe. Dans le cadre de ce cours magistral, j'ai montré comment vous pouvez dessiner cette image en seulement cinq cents lignes de code, et même en interdisant complètement les bibliothèques tierces:

Étonnamment, beaucoup de mes étudiants ne comprennent pas que ce rasterizer logiciel n'est pas seulement un jouet, mais une véritable introduction au fonctionnement d'OpenGL. Par conséquent, aujourd'hui, je vais montrer comment rendre le diabète avec une accélération matérielle, et à bien des égards, j'utiliserai le code du référentiel de rasterizer logiciel.
Attention, je ne me donne pas pour tâche d'expliquer chaque ligne de code, car je compte sur le fait que la lecture de la documentation est le meilleur moyen de tout comprendre. Mon code n'est nécessaire que pour savoir quoi exactement dans la documentation à lire, et dans quel ordre. De plus, je n'expliquerai pas ce que sont les shaders et je n'expliquerai pas comment lire des cartes normales . J'ai passé beaucoup de temps sur tinyrenderer, où tout est réglé.Première étape, la plus difficile: créer une fenêtre
Le référentiel entier vit
ici ; créé un commit pour chaque étape du tutoriel, car le github donne une visionneuse très pratique de toutes les modifications apportées. Nous commençons
ici , notre objectif est d'obtenir cette fenêtre:

Le code est compilé à l'aide de CMake; J'ai vérifié sous Linux (g ++) et Windows (Visual Studio 2017). Sous Linux, la dernière version du code se compile comme ceci:
git clone --recurse-submodules https://github.com/ssloy/hellOGL.git cd hellOGL mkdir build cd build cmake .. make
Utilisez `git checkout` si vous voulez compiler un commit séparé, pas la dernière version. Ce code se charge heureux et GLFW, crée une fenêtre avec le rappel de clavier nécessaire et charge les vertex et pixel shaders vides à partir du disque.
Étape deux: chargement d'un modèle 3D
Modifications apportées au projet à ce stade,
voir ici . À ce stade, notre objectif est d'analyser le fichier du modèle 3D et de dessiner les premiers triangles sans se soucier de l'éclairage pour le moment:

Veuillez noter que le modèle lui-même et la bibliothèque pour travailler avec des vecteurs, et j'ai pris l'analyseur de modèle entièrement de tinyrenderer. Peut-être que le logiciel de rendu n'est pas si inutile?
L'idée de base dans OpenGL moderne est très simple. Nous avons d'abord téléchargé un modèle 3D, puis je crée un tableau de sommets de taille 3 * 3 * (le nombre de triangles). Chaque triangle a trois sommets, non? Chaque sommet est décrit par trois nombres (x, y, z). Au total, 3 * 3 * model.nfaces () nous suffit pour décrire l'ensemble du modèle:
std::vector<GLfloat> vertices(3*3*model.nfaces(), 0); for (int i=0; i<model.nfaces(); i++) { for (int j=0; j<3; j++) { for (int k=0; k<3; k++) vertices[(i*3+j)*3 + k] = model.point(model.vert(i, j))[k]; } }
Et puis nous disons à OpenGL qu'il s'agit d'un tableau, draw, natif!
while (!glfwWindowShouldClose(window)) { [...]  glDrawArrays(GL_TRIANGLES, 0, vertices.size()); [...] }
Le vertex shader ne fait rien d'intéressant, il passe simplement les données au fragment shader tel quel:
#version 330 core
Eh bien, le
shader de fragments est également sans prétention. Il dessine simplement le cornichon actuel en rouge:
#version 330 core
La chose la plus difficile est faite, maintenant c'est une question de technologie!
Troisième étape: éclairage diffus
Modifications apportées au projet à ce stade,
voir ici . Nous devrions obtenir cette image:

L'éclairage diffus dans le modèle Phong, comme vous le savez, est un simple produit scalaire entre
vecteur normal et vecteur d'éclairage. Par conséquent, en plus du tableau des sommets, j'ai ajouté un autre tableau de normales. Sans regarder le code, dites-moi quelle est sa taille?
La chose la plus intéressante se produit dans le fragment shader, dans le fichier principal .cpp, seules les données sont chargées:
#version 330 core
Quatrième étape: matrices de transformation
Modifications apportées au projet à ce stade,
voir ici . À ce stade, j'ai codé les matrices Modèle, Vue et Projection. Au tout début, ils ne sont que simples, mais si vous appuyez sur la barre d'espace, le modèle commencera à tourner: chaque fois que je dessine une image, je fais pivoter la matrice du modèle autour de l'axe z de 0,01 radians:
{
Ici, la fonction rot_z () renvoie la matrice de rotation autour de l'axe z d'un angle donné. Depuis OpenGL ne sait rien de ma classe de matrice, j'ai dû ajouter la matrice export void export_row_major () à un simple pointeur sur un flotteur.

Étape cinq: Cartes normales
Modifications apportées au projet à ce stade,
voir ici . À ce stade, nous apprendrons comment superposer des textures. Puisque la texture diffuse habituelle est ennuyeuse, j'appliquerai immédiatement la carte normale, et même dans un espace tangent. Les cartes normales ressemblent à ceci:

Les calculs correspondants, pour le moins, ne sont pas évidents, alors encore une fois,
lisez les explications dans tinyrenderer . En termes de données, vous devez ajouter plusieurs tampons: coordonnées uv et tableaux de vecteurs tangents et bi-tangents.

Cinquième étape: texture diffuse
Eh bien, si nous savons déjà compter les cartes normales, alors appliquer une texture diffuse normale est tout simplement trivial. Modifications apportées au projet à ce stade,
voir ici .

Étape six: Éblouissement
Modifications apportées au projet à ce stade,
voir ici . L'étape finale, ajoutez une autre texture qui nous permettra de simuler l'éblouissement de l'éclairage des surfaces brillantes:

Conclusion
Il y a beaucoup de choses qui peuvent être améliorées dans ce code, et les effets visuels peuvent être tordus à l'infini. Mais ce n'est pas mon objectif, mon objectif est de montrer qu'absolument toutes les techniques que j'ai abordées dans le rendu logiciel sont applicables dans le contexte OpenGL actuel. Et personnellement, je pense toujours que vous devez commencer à vous familiariser avec les graphiques 3D en dessinant des images sans utiliser la magie des bibliothèques graphiques.
En guise d'extension, essayez, par exemple, d'
ajouter des ombres , ou de compter
l'éclairage global , ou, enfin, de créer une carte lumineuse: après tout, les yeux et le cristal sur le front de Diablo devraient briller!