This is an old revision of the document!


Laboratoire 02

OpenGL – Données

Si nous essayions de réduire l’ensemble de l’API OpenGL à de grands concepts, ils seraient:

  • données
  • états
  • shaders

Shaders shader sera présenté pendant le cours.
Les états représentent un concept plus large, OpenGL étant en fait un grand automate fini avec beaucoup d'états et de possibilités de passer d'un état à un autre. Dans tous les laboratoires, certains de ces états seront utilisés pour obtenir les effets souhaités.
Les données contiennent les informations qui définissent la scène, ainsi que:

  • objets en trois dimensions
  • propriétés matérielles des objets (plastique, verre, etc.)
  • les positions, orientations et dimensions des objets dans la scène
  • toute autre information nécessaire décrivant les propriétés de l'objet ou de la scène

Par exemple, pour une scène carrée unique, nous avons les données suivantes:

  • les sommets du carré - 4 vecteurs tridimensionnels définissant la position de chaque pic dans l'espace
  • les caractéristiques des sommets
    • si la seule caractéristique d'une pointe en dehors de la position serait la couleur, nous aurions 4 autres vecteurs tridimensionnels (RVB)
  • la topologie du carré, c'est-à-dire la manière dont nous connectons ces sommets

OpenGL est une API graphique en trois dimensions, c'est-à-dire que tous les objets pouvant être définis sont liés à un système de coordonnées cartésiennes en trois dimensions. Cependant, nous pouvons utiliser l'API pour afficher des objets en deux dimensions, même s'ils sont définis par des coordonnées (x, y, z), en plaçant toutes les données dans un seul plan et en utilisant une projection appropriée.
En laboratoire, nous utiliserons la coordonnée Z = 0. Ainsi, tout point tridimensionnel deviendra P (x, y, 0).

Topologie

La primitive de base dans OpenGL est le triangle. Ainsi, comme on peut le voir dans l'image ci-dessus, dessiner un objet doit être spécifié par des triangles.

Le cube décrit ci-dessus est spécifié par la liste des 8 coordonnées de sommets et une liste de 12 triangles décrivant la manière dont les sommets spécifiés dans la liste précédente doivent être joints pour former les faces du cube. En utilisant des sommets et des index, nous pouvons décrire discrètement tout objet en trois dimensions.

Vous trouverez ci-dessous les principales primitives supportées par le standard OpenGL 3.3+.

Comme on peut le constater, il existe plusieurs méthodes permettant de spécifier la géométrie:

  • GL_LINES et GL_TRIANGLES sont les primitives les plus couramment utilisées pour définir la géométrie
  • GL_POINTS il est souvent utilisé pour créer des systèmes de particules
  • Les autres modèles ne représentent que des optimisations des 3 primitives de base, tant du point de vue de la mémoire que de la facilité à spécifier certaines topologies, mais leur utilité est souvent limitée car les objets les plus complexes ne peuvent être spécifiés qu'en utilisant de simples primitives.

Dans le cadre, vous pouvez définir le type de primitive utilisé par un objet à restituer via la fonction. Mesh::SetDrawMode(GLenum primitive)primitive peut être n'importe lequel des primitifs mentionnés dans l'image ci-dessus.

L'ordre de la spécification des sommets

Une observation importante liée à la topologie est l’ordre des sommets dans une primitive solide (pas une ligne, pas un point) avec plus de 2 sommets. Cet ordre peut être horaire ou antihoraire.

Face Culling

L'API OpenGL offre la possibilité de tester l'orientation apparente de chaque triangle à l'écran avant sa lecture et de l'ignorer en fonction du statut de suppression défini.: GL_FRONTGL_BACK. Cette fonctionnalité s'appelle Face Culling et est très important car cela réduit le coût total de traitement.

Exemple: pour un cube 3 faces au maximum peuvent être visibles à tout moment sur les 6 existantes. Dans ce cas, un maximum de 6 triangles seront traités pour être affichés à l'écran au lieu de 12.

Normalement, la sélection du visage est désactivée. Il peut être activé en utilisant la commande glEnable:

glEnable(GL_CULL_FACE);

Pour désactiver la sélection face-à-face, utilisez la commande glDisable:

glDisable(GL_CULL_FACE);

Pour spécifier l'orientation des faces à ignorer, utilisez la commande glCullFace

// GL_FRONT, GL_BACK, and GL_FRONT_AND_BACK are accepted.
// The initial value is GL_BACK.
glCullFace(GL_BACK);

Maillage

Un „maillage” est un objet en trois dimensions défini par des points et des indices. Au laboratoire, vous pouvez télécharger des maillages dans presque tous les formats possibles en classe. Mesh.

Vertex Buffer Object (VBO)

Un vertex buffer object reprezintă un container în care stocăm date ce țin de conținutul vârfurilor precum:

  • poziție
  • normală
  • culoare
  • coordonate de texturare
  • etc…

Un vertex buffer object se poate crea prin comanda OpenGL glGenBuffers:

	GLuint VBO_ID;			// ID-ul (nume sau referinta) buffer-ului ce va fi cerut de la GPU
	glGenBuffers(1, &VBO_ID);	// se genereaza ID-ul (numele) bufferului

Așa cum se poate vedea și din explicația API-ului, funcția glGenBuffers primește numărul de buffere ce trebuie generate cât și locația din memorie unde vor fi salvate referințele (ID-urile) generate.
În exemplul de mai sus este generat doar 1 singur buffer iar ID-ul este salvat în variabila VBO_ID.

Pentru a distruge un VBO și astfel să eliberăm memoria de pe GPU se folosește comanda glDeleteBuffers:

glDeleteBuffers(1, &VBO_ID);

Pentru a putea pune date într-un buffer trebuie întâi să legăm acest buffer la un „target”. Pentru un vertex buffer acest „binding point” se numește GL_ARRAY_BUFFER și se poate specifica prin comanda glBindBuffer:

glBindBuffer(GL_ARRAY_BUFFER, VBO_ID);

În acest moment putem să facem upload de date din memoria CPU către GPU prin intermediul comenzii glBufferData:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), &vertices[0], GL_STATIC_DRAW);
  • Comanda citește de la adresa specificată, în exemplul de sus fiind adresa primului vârf &vertices[0], și copiază în memoria video dimensiunea specificată prin parametrul al 2-lea.
  • GL_STATIC_DRAW reprezintă un hint pentru driver-ul video în ceea ce privește metoda de utilizare a bufferului. Acest simbol poate avea mai multe valori dar în cadrul laboratorului este de ajuns specificarea prezentată. Mai multe informații găsiți pe pagina de manual a funcției glBufferData

Pentru a înțelege mai bine API-ul OpenGL vă rocomandăm să citiți documentația indicată pentru fiecare comandă prezentată. Atunci când se prezintă o nouă comandă, dacă apăsați click pe numele acesteia veți fi redirecționați către pagina de manual a comenzii respective.
De asemenea, documentația oficială și completă a API-ului OpenGL poate fi gasită pe pagina OpenGL 4 Reference Pages

Index Buffer Object (IBO)

Un index buffer object (numit și element buffer object) reprezintă un container în care stocăm indicii vertecșilor. Cum VBO si IBO sunt buffere, ele sunt extrem de similare în construcție, încărcare de date și ștergere.

glGenBuffers(1, &IBO_ID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO_ID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), &indices[0], GL_STATIC_DRAW);

La fel ca la VBO, creăm un IBO și apoi îl legăm la un punct de legatură, doar că de data aceasta punctul de legatură este GL_ELEMENT_ARRAY_BUFFER. Datele sunt trimise către bufferul mapat la acest punct de legatură. În cazul indicilor toți vor fi de dimensiunea unui singur întreg.

Vertex Array Object (VAO)

Într-un vertex array object putem stoca toată informația legată de starea geometriei desenate. Putem folosi un număr mare de buffere pentru a stoca fiecare din diferitele atribute („separate buffers”). Putem stoca mai multe (sau toate) atribute într-un singur buffer („interleaved” buffers). În mod normal înainte de fiecare comandă de desenare trebuie specificate toate comenzile de „binding” pentru buffere sau atribute ce descriu datele ce doresc a fi randate. Pentru a simplifica această operație se folosește un vertex array object care ține minte toate aceste legături.

Un vertex array object este creat folosind comanda glGenVertexArrays:

unsigned int VAO;
glGenVertexArrays(1, &VAO);

Este legat cu glBindVertexArray:

glBindVertexArray(VAO);

Înainte de a crea VBO-urile și IBO-ul necesar pentru un obiect se va lega VAO-ul obiectului și acesta va ține minte automat toate legăturile specificate ulterior.

După ce toate legăturile au fost specificate este recomandat să se dea comanda glBindVertexArray(0) pentru a dezactiva legătura către VAO-ul curent, deoarece altfel riscăm ca alte comenzi OpenGL ulterioare să fie legate la același VAO și astfel să introducem foarte ușor erori în program.

Înainte de comanda de desenare este suficient să legăm doar VAO-ul ca OpenGL să știe toate legatările create la construcția obiectului.

Laborator 2

Descriere laborator

În cadrul laboratorului vom învăța să folosim VAO, VBO, IBO și astfel să generăm și încărcăm geometrie simplă.
Laboratorul pune la dispoziție structura VertexFormat ce va fi utilizată ca bază pentru a crea geometria.

struct VertexFormat
{
	// position of the vertex
	glm::vec3 position;		
 
	// vertex normal
	glm::vec3 normal;
 
	// vertex texture coordinate
	glm::uvec2 text_coord;
 
	// vertex color
	glm::vec3 color;
};

Clasa Mesh pune la dispoziție posibilitatea de a încărca geometrie simplă folosind diverse metode:

// Initializes the mesh object using a VAO GPU buffer that contains the specified number of indices
bool InitFromBuffer(unsigned int VAO, unsigned short nrIndices);
 
// Initializes the mesh object and upload data to GPU using the provided data buffers
bool InitFromData(std::vector<VertexFormat> vertices,
		std::vector<unsigned short>& indices);
 
// Initializes the mesh object and upload data to GPU using the provided data buffers
bool InitFromData(std::vector<glm::vec3>& positions,
		std::vector<glm::vec3>& normals,
		std::vector<unsigned short>& indices);

Taste de control pentru cameră

  • W, A, S, D, Q, E - deplasare față, stânga, spate, dreapta, jos, sus
  • MOUSE RIGHT + MOUSE MOVE - rotație cameră

F3 - afișează/ascunde gridul din scenă
Space - desenează primitivele doar prin puncte sau linii (wireframe) sau geometrie opacă

Cerințe laborator

Toate cerințele ce țin de încărcare de geometrie trebuie rezolvate prin intermediul funcției Laborator2::CreateMesh dar puteți folosi metodele Mesh::InitFromData() pentru a verifica validitatea geometriei.

  1. Completați geometria și topologia unui cub: vectorii de vertecși și indecși din inițializare. VertexFormat este o structură pentru vertex cu 2 parametrii (poziție, culoare).
  2. Completați funcția Laborator2::CreateMesh astfel încât să încărcați geometria pe GPU
    • creați un VAO
    • creați un VBO și adăugați date în el
    • creați un IBO și adăugați date în el
    • afișați noul obiect (RenderMesh[cube3]) astfel încât să nu se suprapună cu un alt obiect
  3. Creați o nouă formă geometrică simplă, de exemplu un tetraedru și desenați-l în scenă
  4. Atunci când se apasă tasta F2 faceți toggle între modul de culling GL_BACK și GL_FRONT
    • nu uitați să activați și să dezactivați face culling folosind glEnable() / glDisable()
  5. Creați un pătrat format din 2 triunghiuri astfel încât fiecare triunghi să fie vizibil doar dintr-o parte
    • în orice moment de timp nu trebuie să se vadă decât 1 triunghi
egc/laboratoare/fr/02.1570630975.txt.gz · Last modified: 2019/10/09 17:22 by alexandru.gradinaru
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0