29 votes

Rendu d'objets multiples avec OpenGL ES 2.0

J'essaie d'apprendre OpenGL ES 2.0 pour développer des jeux sur iPhone. J'ai lu de nombreux tutoriels et une partie de la spécification OpenGL ES 2.0. Tous les exemples que j'ai vus ont créé un maillage unique, l'ont chargé dans une mémoire tampon de sommets et l'ont ensuite rendu (avec la translation, la rotation, le gradient, etc. attendus).

Ma question est la suivante : comment rendre de multiples objets dans votre scène qui ont des maillages différents et qui se déplacent indépendamment ? Si j'ai une voiture et une moto par exemple, puis-je créer 2 vertex buffers et garder les données de maillage pour les deux à chaque appel de rendu, puis envoyer des matrices différentes pour le shader de chaque objet ? Ou dois-je traduire les maillages d'une manière ou d'une autre, puis les combiner en un seul maillage afin de pouvoir effectuer le rendu en une seule passe ? Je cherche davantage la stratégie de haut niveau / la structure du programme plutôt que des exemples de code. Je pense que je n'ai pas la bonne conception mentale de la façon dont cela fonctionne.

Merci !

11voto

Tony Points 119

Le meilleur moyen que j'ai trouvé pour y parvenir est d'utiliser des VAO en plus des VBO.

Je vais d'abord répondre à votre question en utilisant uniquement les VBO.

Tout d'abord, supposons que les deux maillages de vos deux objets sont stockés dans les tableaux suivants :

GLuint _vertexBufferCube1;
GLuint _vertexBufferCube2;

où :

GLfloat gCubeVertexData1[36] = {...};
GLfloat gCubeVertexData2[36] = {...};

Et vous devez également utiliser des tampons vertix :

GLuint _vertexBufferCube1;
GLuint _vertexBufferCube2;

Maintenant, pour dessiner ces deux cubes (sans VAOs), vous devez faire quelque chose comme ça : dans la fonction draw (du modèle OpenGLES) :

//Draw first object, bind VBO, adjust your attributes then call DrawArrays
glGenBuffers(1, &_vertexBufferCube1);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube1);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData1), gCubeVertexData1, GL_STATIC_DRAW);

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));

glDrawArrays(GL_TRIANGLES, 0, 36);

//Repeat for second object:
glGenBuffers(1, &_vertexBufferCube2);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube2);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData2), gCubeVertexData2, GL_STATIC_DRAW);

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));
glUseProgram(_program);

glDrawArrays(GL_TRIANGLES, 0, 36);

Cela répondra à votre question. Mais maintenant pour utiliser les VAOs, le code de votre fonction draw est beaucoup plus simple (ce qui est bien car c'est la fonction répétée) :

Vous allez d'abord définir les VAO :

GLuint _vertexArray1;
GLuint _vertexArray2;

et ensuite vous ferez toutes les étapes précédemment effectuées dans la méthode draw, vous le ferez dans la fonction setupGL mais après la liaison avec le VAO. Ensuite, dans votre fonction de dessin, il vous suffit de vous lier au VAO de votre choix.

VAO est ici comme un profil qui contient beaucoup de propriétés (imaginez un profil de dispositif intelligent). Au lieu de changer la couleur, le bureau, les polices, etc. à chaque fois que vous souhaitez les modifier, vous le faites une fois et l'enregistrez sous un nom de profil. Ensuite, il suffit de changer de profil.

Vous le faites donc une fois, dans setupGL, puis vous passez de l'un à l'autre dans draw.

Bien sûr, vous pouvez dire que vous auriez pu mettre le code (sans VAO) dans une fonction et l'appeler. C'est vrai, mais les VAO sont plus efficaces selon Apple :

http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html#//apple_ref/doc/uid/TP40008793-CH107-SW1

Passons maintenant au code :

Dans le setupGL :

glGenVertexArraysOES(1, &_vertexArray1); //Bind to first VAO
glBindVertexArrayOES(_vertexArray1);

glGenBuffers(1, &_vertexBufferCube1); //All steps from this one are done to first VAO only
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube1);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData1), gCubeVertexData1, GL_STATIC_DRAW);

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));

glGenVertexArraysOES(1, &_vertexArray2); // now bind to the second
glBindVertexArrayOES(_vertexArray2);

glGenBuffers(1, &_vertexBufferCube2); //repeat with the second mesh
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferCube2);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData2), gCubeVertexData2, GL_STATIC_DRAW);

glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));

glBindVertexArrayOES(0);

Et enfin dans votre méthode de dessin :

glBindVertexArrayOES(_vertexArray1);
glDrawArrays(GL_TRIANGLES, 0, 36);

glBindVertexArrayOES(_vertexArray2);    
glDrawArrays(GL_TRIANGLES, 0, 36);

10voto

TaylorP Points 596

Vous maintenez des tampons de vertex/index séparés pour différents objets, oui. Par exemple, vous pourriez avoir une classe RenderedObject, et chaque instance aurait son propre tampon de sommets. Un RenderedObject pourrait prendre ses vertices d'un maillage de maison, un autre pourrait venir d'un maillage de personnage, etc.

Pendant le rendu, vous définissez la transformation, la rotation et l'ombrage appropriés pour le tampon de sommets avec lequel vous travaillez :

void RenderedObject::render()
{
    ...
    //set textures/shaders/transformations

    glBindBuffer(GL_ARRAY_BUFFER, bufferID);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
    ...
}

Comme mentionné dans l'autre réponse, le bufferID n'est qu'un GLuint et non le contenu complet du tampon. Si vous avez besoin de plus de détails sur la création de tampons de sommets et leur remplissage avec des données, je suis heureux de les ajouter également.

4voto

MikeyE Points 76

Je me rends compte que c'est un ancien message, mais j'essayais de trouver des instructions sur la façon de rendre de multiples objets dans un même fichier. OpenGL . J'ai trouvé un excellent tutoriel, qui décrit comment rendre des objets multiples et qui pourrait être facilement étendu pour rendre des objets de différents types (par exemple, un cube, une pyramide).

Le tutoriel que je mets en ligne décrit également comment rendre des objets à l'aide de GLKit . J'ai trouvé qu'il était utile et j'ai pensé que je le reposterais ici. J'espère qu'il vous aidera aussi !

http://games.ianterrell.com/opengl-basics-with-glkit-in-ios5-encapsulated-drawing-and-animation/

3voto

Kornel Kisielewicz Points 26556

Si les maillages sont différents, vous les conservez dans des tampons de vertex différents. S'ils sont similaires (par exemple, animation, couleur), vous passez des arguments au shader. Vous devez uniquement conserver les poignées des VBO, et non les données de sommet elles-mêmes si vous ne prévoyez pas d'animer l'objet du côté de l'application. Animation côté appareil es possible.

0voto

user3056041 Points 1

J'espère contribuer à cet article plus ancien, car j'ai entrepris de résoudre ce problème d'une manière différente. Comme l'auteur de la question, j'ai vu beaucoup d'exemples "d'un seul objet". J'ai entrepris de placer tous les sommets dans un seul VBO, puis de sauvegarder l'offset de la position de cet objet (par objet), plutôt qu'un handle de tampon. Cela a fonctionné. L'offset peut être donné comme paramètre à glDrawElements comme ci-dessous. Cela semble évident rétrospectivement, mais je n'étais pas convaincu jusqu'à ce que je le voie fonctionner. Veuillez noter que j'ai travaillé avec un "vertex pointer" plutôt qu'avec le plus courant "vertex attribute pointer". Je m'oriente vers ce dernier pour pouvoir utiliser les shaders. Tous les objets se "lient" au même tampon de vertex, avant d'appeler "draw elements".

        gl.glVertexPointer( 3, GLES20.GL_FLOAT, 0, vertexBufferOffset );

        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, indicesCount,
                GLES20.GL_UNSIGNED_BYTE, indexBufferOffset
        );

Je n'ai trouvé nulle part d'explication sur le but de cette compensation, alors j'ai tenté ma chance. De plus, il y a un hic : vous devez spécifier le décalage en octets, pas en vertices ou en flottants. C'est-à-dire qu'il faut multiplier par quatre pour obtenir la position correcte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X