Comment rendre offscreen sur OpenGL? [dupliquer]

cette question a déjà une réponse ici:

mon but est de rendre la scène OpenGL sans fenêtre, directement dans un fichier. La scène peut être plus grande que ma résolution d'écran.

Comment faire cette?

je veux pouvoir choisir la taille de la zone de rendu à n'importe quelle taille, par exemple 10000x10000, si possible?

31
demandé sur Rookie 2012-08-28 14:46:28

5 réponses

tout commence par glReadPixels , que vous utiliserez pour transférer les pixels stockés dans un tampon spécifique sur le GPU vers la mémoire principale (RAM). Comme vous le remarquerez dans la documentation, il n'existe aucun argument pour choisir tampon. Comme D'habitude avec OpenGL, le tampon courant à lire est un État, que vous pouvez définir avec glReadBuffer .

donc une méthode très basique de rendu hors écran serait quelque chose comme ce qui suit. J'utilise c++ pseudo-code donc il contiendra probablement des erreurs, mais devrait rendre le flux général clair:

//Before swapping
std::vector<std::uint8_t> data(width*height*4);
glReadBuffer(GL_BACK);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,&data[0]);

cela Lira le tampon de retour courant (habituellement le tampon vers lequel vous dessinez). Vous devriez l'appeler avant de changer les tampons. Notez que vous pouvez également lire parfaitement le tampon arrière avec la méthode ci-dessus, le vider et dessiner quelque chose de totalement différent avant de l'échanger. Techniquement, vous pouvez aussi lire le tampon frontal, mais cela est souvent déconseillé en théorie. les implémentations ont été autorisées à faire certaines optimisations qui pourraient rendre votre tampon frontal inutile.

il y a quelques inconvénients à cela. Tout d'abord, nous ne faisons pas vraiment de rendu offscreen, n'est-ce pas? Nous rendons aux tampons d'écran et lisons à partir de ceux-ci. Nous pouvons émuler le rendu offscreen en ne changeant jamais le tampon arrière, mais cela ne semble pas juste. Ensuite, les tampons avant et arrière sont optimisés pour afficher les pixels, pas pour les relire. C'est là Framebuffer Objets entrent en jeu.

essentiellement, un FBO vous permet de créer un framebuffer non par défaut (comme les buffers avant et arrière) qui vous permet de tirer sur un tampon mémoire au lieu des buffers écran. En pratique, vous pouvez dessiner sur une texture ou sur un renderbuffer . La première est optimale lorsque vous voulez réutiliser les pixels dans OpenGL lui-même comme une texture (par exemple une naïve "caméra de sécurité" dans un jeu), la seconde si vous voulez juste pour rendre/lecture en arrière. Avec cela le code ci-dessus deviendrait quelque chose comme ceci, encore une fois le pseudo-code, alors ne me tuez pas si mal écrit ou oublié quelques déclarations.

//Somewhere at initialization
GLuint fbo, render_buf;
glGenFramebuffers(1,&fbo);
glGenRenderbuffers(1,&render_buf);
glBindRenderbuffer(render_buf);
glRenderbufferStorage(GL_RENDERBUFFER, GL_BGRA8, width, height);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER​,fbo);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, render_buf);

//At deinit:
glDeleteFramebuffers(1,&fbo);
glDeleteRenderbuffers(1,&render_buf);

//Before drawing
glBindFramebuffer(GL_DRAW_FRAMEBUFFER​,fbo);
//after drawing
std::vector<std::uint8_t> data(width*height*4);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,&data[0]);
// Return to onscreen rendering:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER​,0);

ceci est un exemple simple, en réalité vous voulez probablement aussi du stockage pour le tampon de profondeur (et de pochoir). Vous pourriez aussi vouloir rendre à la texture, mais je vais laisser cela comme un exercice. Dans tous les cas, vous allez maintenant effectuer un rendu réel hors écran et il pourrait fonctionner plus rapidement que la lecture de la tampon d'arrière-plan.

enfin, vous pouvez utiliser pixel buffer objects pour rendre les pixels de lecture asynchrones. Le problème est que glReadPixels bloque jusqu'à ce que les données du pixel soient complètement transférées, ce qui peut retarder votre CPU. Avec PBO, l'implémentation peut revenir immédiatement car elle contrôle de toute façon le buffer. Ce n'est que lorsque vous cartographiez la zone tampon que le pipeline bloquera. Cependant, PBO peut être optimisé pour amortir les données uniquement sur la RAM, de sorte que ce bloc pourrait prendre beaucoup moins de temps. Le code de pixels lu deviendra quelque chose comme ceci:

//Init:
GLuint pbo;
glGenBuffers(1,&pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, width*height*4, NULL, GL_DYNAMIC_READ);

//Deinit:
glDeleteBuffers(1,&pbo);

//Reading:
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glReadPixels(0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,0); // 0 instead of a pointer, it is now an offset in the buffer.
//DO SOME OTHER STUFF (otherwise this is a waste of your time)
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); //Might not be necessary...
pixel_data = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);

la partie en capuchons est essentielle. Si vous envoyez juste un glReadPixels à un PBO, suivi d'un glMapBuffer de ce PBO, vous n'avez rien gagné sauf beaucoup de code. Bien sûr le glReadPixels pourrait revenir immédiatement, mais maintenant le glMapBuffer va décrocher parce qu'il doit en toute sécurité mapper les données de la zone de lecture au PBO et à un bloc de mémoire dans la RAM principale.

S'il vous plaît noter également que J'utilise GL_BGRA partout, c'est parce que de nombreuses cartes graphiques utilisent en interne ce format comme format de rendu optimal (ou la version GL_BGR sans alpha). Il devrait être le format le plus rapide pour les transferts de pixels comme celui-ci. Je vais essayer de trouver l'article de nvidia que j'ai lu il y a quelques mois.

lorsque vous utilisez OpenGLES 2.0, GL_DRAW_FRAMEBUFFER peut ne pas être disponible, vous devez simplement utiliser GL_FRAMEBUFFER dans ce cas.

78
répondu KillianDS 2013-04-18 19:14:35

je supposerai que la création d'une fenêtre factice (vous ne la rendez pas; c'est juste là parce que L'API vous demande d'en créer une) dans laquelle vous créez votre contexte principal est une stratégie de mise en œuvre acceptable.

voici vos options:

Pixel tampons

un tampon de pixel, ou pbuffer (qui n'est pas un objet tampon de pixel ), est d'abord et avant tout un contexte OpenGL . Fondamentalement, vous créez une fenêtre comme d'habitude, puis choisissez un format de pixel à partir de wglChoosePixelFormatARB (les formats de pbuffer doivent être obtenus d'ici). Ensuite, vous appelez wglCreatePbufferARB , en lui donnant le HDC de votre fenêtre et le format de tampon de pixel que vous voulez utiliser. Oh, et une largeur/hauteur, vous pouvez interroger la mise en œuvre du maximum de la largeur/hauteur.

le framebuffer par défaut pour pbuffer n'est pas visible sur l'écran, et la largeur/hauteur maximale est ce que le matériel veut laissez vous utilisez. Vous pouvez donc le rendre et utiliser glReadPixels pour le relire.

vous devrez partager votre contexte avec le contexte donné si vous avez créé des objets dans le contexte de la fenêtre. Sinon, vous pouvez utiliser le contexte pbuffer entièrement séparément. Ne détruisez pas le contexte de la fenêtre.

l'avantage ici est un plus grand soutien à la mise en œuvre (bien que la plupart des pilotes qui ne prennent pas en charge les alternatives sont également de vieux pilotes pour le matériel qui n'est pas plus être pris en charge. Ou est-Intel).

les inconvénients sont ceux-ci. Les Pbuffers ne fonctionnent pas avec les contextes core OpenGL . Ils peuvent fonctionner pour la compatibilité, mais il n'y a aucun moyen de donner des informations wglCreatePbufferARB sur les versions et les profils OpenGL.

Framebuffer Objects

Framebuffer Objets sont plus "propre" à l'écran rendertargets que pbuffers. Les organisations confessionnelles sont à l'intérieur d'un contexte, tandis que les pbuffers sont sur la création de nouveaux contextes.

FBOs sont juste un conteneur pour les images que vous rendez. Les dimensions maximales autorisées par l'implémentation peuvent être interrogées; vous pouvez supposer qu'il s'agit de GL_MAX_VIEWPORT_DIMS (assurez-vous qu'un FBO est lié avant de vérifier cela, car il change selon qu'un FBO est lié ou non).

comme vous n'êtes pas en train d'échantillonner des textures à partir de celles-ci (vous n'êtes qu'en train de lire les valeurs en arrière), vous devriez utiliser des renderbuffers au lieu de texture. Leur taille maximale peut être supérieure à celles des textures.

L'avantage est la facilité d'utilisation. Plutôt que d'avoir à traiter avec les formats de pixels et tel, vous venez de choisir un format d'image pour votre appel glRenderbufferStorage .

le seul inconvénient réel est la bande plus étroite du matériel qui les supporte. En général, tout ce que AMD ou NVIDIA fait qu'ils supportent encore (en ce moment, GeForce 6xxx ou mieux) nombre de x], et N'importe quelle carte Radeon HD) auront accès à ARB_framebuffer_object ou OpenGL 3.0+ (où c'est une fonctionnalité de base). Les pilotes plus anciens ne peuvent avoir que le support EXT_framebuffer_object (qui a quelques différences). Intel hardware est potluck; même s'ils prétendent 3.x ou 4.x, il peut encore échouer à cause de bogues.

14
répondu Nicol Bolas 2012-08-28 12:32:10

si vous avez besoin de rendre quelque chose qui dépasse la taille FBO maximale de votre implémentation GL libtr fonctionne assez bien:

la bibliothèque TR (Tile Rendering) est une bibliothèque utilitaire OpenGL pour faire mosaïque de rendu. Le rendu en carreaux est une technique pour générer de grandes images en pièces (tuiles).

TR est économe en mémoire; des fichiers image de grande taille peuvent être générés arbitrairement sans allouer un tampon d'image de taille normale dans la mémoire principale.

3
répondu genpfault 2012-08-28 20:01:08

la façon la plus simple est d'utiliser quelque chose appelé Frame Buffer Objects (FBO). Vous devrez tout de même créer une fenêtre pour créer un contexte opengl (mais cette fenêtre peut être caché).

2
répondu Andreas Brinck 2012-08-28 10:48:11

la façon la plus facile d'atteindre votre objectif est d'utiliser FBO pour faire un rendu hors écran. Et vous n'avez pas besoin de rendre à la texture, puis obtenir le Tex Mage. Il suffit de rendre le tampon et d'utiliser la fonction glReadPixels . Ce lien vous sera utile. Voir Objet Framebuffer Exemples

1
répondu rtrobin 2012-08-29 16:35:25