hellOGL: OpenGL bonjour le monde

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 // Input vertex data, different for all executions of this shader layout(location = 0) in vec3 vertexPosition_modelspace; void main() { gl_Position = vec4(vertexPosition_modelspace, 1); // Output position of the vertex, in clip space } 

Eh bien, le shader de fragments est également sans prétention. Il dessine simplement le cornichon actuel en rouge:

 #version 330 core // Output data out vec3 color; void main() { color = vec3(1,0,0); } 

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 // Output data out vec3 color; // Interpolated values from the vertex shaders in vec3 Normal_cameraspace; in vec3 LightDirection_cameraspace; void main() { vec3 n = normalize(Normal_cameraspace); // Normal of the computed fragment, in camera space vec3 l = normalize(LightDirection_cameraspace); // Direction of the light (from the fragment to the light) float cosTheta = clamp(dot(n,l), 0, 1); // Cosine of the angle between the normal and the light direction, color = vec3(1,0,0)*(0.1 + // ambient lighting 1.3*cosTheta); // diffuse lighting } 

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:

  { // rotate the model around the z axis with each frame Matrix R = rot_z(0.01); if (animate) M = R*M; } 

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!

Source: https://habr.com/ru/post/fr443174/


All Articles