Comment rendre les primitives sous forme de wireframes dans OpenGL?

220

Comment rendre les primitives sous forme de wireframes dans OpenGL?


la source
2
Soyez plus précis sur la version d'OpenGL que vous utilisez.
Vertexwahn
Avec ModernGL, vous pouvez simplement utiliser l' attribut filaire de la classe Context
Szabolcs Dombi
utilisez GL_LINES pour dessiner des wireframes
oxine

Réponses:

367
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

allumer,

glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

pour revenir à la normale.

Notez que des éléments tels que le mappage de texture et l'éclairage seront toujours appliqués aux lignes filaires s'ils sont activés, ce qui peut sembler étrange.


la source
2
Je vous aime bien. 4 de plus à parcourir.
Jo So
37

Depuis http://cone3d.gamedev.net/cgi-bin/index.pl?page=tutorials/ogladv/tut5

// Turn on wireframe mode
glPolygonMode(GL_FRONT, GL_LINE);
glPolygonMode(GL_BACK, GL_LINE);

// Draw the box
DrawBox();

// Turn off wireframe mode
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_FILL);
John Millikin
la source
75
effectuer deux appels est redondant. utiliser GL_FRONT_AND_BACK
shoosh
6
En complément au commentaire de @ shoosh, le Livre rouge déclare que GL_FRONT et GL_BACK ont été dépréciés et supprimés d'OpenGL 3.1 et versions ultérieures. Maintenant, vous pouvez toujours les utiliser via l'extension de compatibilité, mais si vous avez le choix entre la compatibilité ascendante et la compatibilité descendante, je recommanderais d'opter pour la première.
fouric
1
Le lien dans cette réponse renvoie 404.
Ondrej Slinták
1
@ OndrejSlinták C'est corrigé maintenant.
MaxiMouse
24

En supposant un contexte de compatibilité ascendante dans OpenGL 3 et supérieur, vous pouvez soit utiliser glPolygonModecomme mentionné précédemment, mais notez que les lignes avec une épaisseur supérieure à 1px sont désormais obsolètes. Ainsi, bien que vous puissiez dessiner des triangles en fil de fer, ils doivent être très fins. Dans OpenGL ES, vous pouvez utiliser GL_LINESavec la même limitation.

Dans OpenGL, il est possible d'utiliser des shaders de géométrie pour prendre les triangles entrants, les désassembler et les envoyer pour la pixellisation sous forme de quads (des paires de triangles vraiment) émulant des lignes épaisses. Assez simple, vraiment, sauf que les shaders de géométrie sont connus pour leur mauvaise mise à l'échelle des performances.

Ce que vous pouvez faire à la place, et ce qui fonctionnera également dans OpenGL ES, c'est d'utiliser un fragment shader. Pensez à appliquer une texture de triangle filaire au triangle. Sauf qu'aucune texture n'est nécessaire, elle peut être générée de manière procédurale. Mais assez parlé, codons. Shader de fragment:

in vec3 v_barycentric; // barycentric coordinate inside the triangle
uniform float f_thickness; // thickness of the rendered lines

void main()
{
    float f_closest_edge = min(v_barycentric.x,
        min(v_barycentric.y, v_barycentric.z)); // see to which edge this pixel is the closest
    float f_width = fwidth(f_closest_edge); // calculate derivative (divide f_thickness by this to have the line width constant in screen-space)
    float f_alpha = smoothstep(f_thickness, f_thickness + f_width, f_closest_edge); // calculate alpha
    gl_FragColor = vec4(vec3(.0), f_alpha);
}

Et vertex shader:

in vec4 v_pos; // position of the vertices
in vec3 v_bc; // barycentric coordinate inside the triangle

out vec3 v_barycentric; // barycentric coordinate inside the triangle

uniform mat4 t_mvp; // modeview-projection matrix

void main()
{
    gl_Position = t_mvp * v_pos;
    v_barycentric = v_bc; // just pass it on
}

Ici, les coordonnées barycentriques sont simplement (1, 0, 0), (0, 1, 0)et (0, 0, 1)pour les trois sommets triangulaires (l'ordre n'a pas vraiment d'importance, ce qui facilite potentiellement le compactage en bandes triangulaires).

L'inconvénient évident de cette approche est qu'elle va manger des coordonnées de texture et que vous devez modifier votre tableau de sommets. Pourrait être résolu avec un shader de géométrie très simple, mais je soupçonne toujours qu'il sera plus lent que de simplement alimenter le GPU avec plus de données.

le porc
la source
Je pensais que cela semblait prometteur, mais cela ne semble pas fonctionner avec OpenGL 3.2 (non-ES). Bien qu'il soit possible que j'ai foiré quelque chose, j'ai joué avec cela pendant un bon moment et je ne sais pas vraiment comment cela est même destiné à être utilisé. Toute information supplémentaire sur ce que vous comptez réellement rendre avec ces shaders serait utile; Je ne vois pas comment autre chose qu'un remplissage produirait quelque chose d'utile, mais cela ne fonctionne même pas étant donné que la valeur alpha de gl_FragColor semblait être entièrement ignorée dans OpenGL 3.2 ...
user1167662
2
Bien sûr que oui. Vous pouvez vous référer à l'implémentation de la même idée par quelqu'un dans stackoverflow.com/questions/7361582/… .
le porc
1
Le problème que je rencontre en essayant d'utiliser l'implémentation suggérée est, je pense, que le shader ne dessinera jamais en dehors de l'objet à décrire. Cela va donc dessiner le contour sur l'objet esquissé? En d'autres termes, le contour sera purement contenu dans l'objet d'origine à dessiner?
user1167662
1
@BrunoLevy, vous pouvez obtenir des coordonnées supplémentaires dans webGL, non? Si vous êtes chanceux, vous pourrez peut-être obtenir ces coordonnées à partir de texcoords si vous avez des modèles très simples, mais sinon vous avez juste besoin d'ajouter de nouvelles coordonnées. Ce serait trop sympa si ça marchait sans ça :). Tout ce dont vous avez besoin est de passer un seul scalaire par sommet (puis de le développer en un vecteur à 3 dans le vertex shader).
le porc
2
Ah, maintenant je vois ... f_width () est mon ami, à cet égard. Merci
Neil Gatenby
5

Si vous utilisez le pipeline fixe (OpenGL <3.3) ou le profil de compatibilité que vous pouvez utiliser

//Turn on wireframe mode
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

//Draw the scene with polygons as lines (wireframe)
renderScene();

//Turn off wireframe mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

Dans ce cas, vous pouvez modifier la largeur de la ligne en appelant glLineWidth

Sinon, vous devez changer le mode polygone dans votre méthode de dessin (glDrawElements, glDrawArrays, etc.) et vous pourriez vous retrouver avec des résultats approximatifs car vos données de sommet sont pour des triangles et vous générez des lignes. Pour de meilleurs résultats, envisagez d'utiliser un ombrage géométrique ou de créer de nouvelles données pour le filaire.

Zaphyk
la source
3

Le moyen le plus simple est de dessiner les primitives en tant que GL_LINE_STRIP.

glBegin(GL_LINE_STRIP);
/* Draw vertices here */
glEnd();
Hochester
la source
pas si c'est un tas de GL_TRIANGLES: vous obtiendrez des lignes entre eux. Dans OpenGL 1.x ou hérité, vous utilisez glPolygonMode. Dans OpenGL récent, vous jouez avec le shader de géométrie.
Fabrice NEYRET
C'est une manière ancienne de faire les choses qui doit être laissée de côté.
Benny Mackney
2

Dans Modern OpenGL (OpenGL 3.2 et supérieur), vous pouvez utiliser un Geometry Shader pour cela:

#version 330

layout (triangles) in;
layout (line_strip /*for lines, use "points" for points*/, max_vertices=3) out;

in vec2 texcoords_pass[]; //Texcoords from Vertex Shader
in vec3 normals_pass[]; //Normals from Vertex Shader

out vec3 normals; //Normals for Fragment Shader
out vec2 texcoords; //Texcoords for Fragment Shader

void main(void)
{
    int i;
    for (i = 0; i < gl_in.length(); i++)
    {
        texcoords=texcoords_pass[i]; //Pass through
        normals=normals_pass[i]; //Pass through
        gl_Position = gl_in[i].gl_Position; //Pass through
        EmitVertex();
    }
    EndPrimitive();
}

Avis:

LMD
la source
1

Vous pouvez utiliser des bibliothèques glut comme ceci:

  1. pour une sphère:

    glutWireSphere(radius,20,20);
    
  2. pour un cylindre:

    GLUquadric *quadratic = gluNewQuadric();
    gluQuadricDrawStyle(quadratic,GLU_LINE);
    gluCylinder(quadratic,1,1,1,12,1);
    
  3. pour un cube:

    glutWireCube(1.5);
    
H.Mohcen
la source
0

Un moyen simple et efficace de dessiner des lignes anti-aliasées sur une cible de rendu non anti-aliasée consiste à dessiner des rectangles de 4 pixels de largeur avec une texture 1x4, avec des valeurs de canal alpha de {0., 1., 1., 0.} et utilisez le filtrage linéaire avec désactivation du mip-mapping. Cela rendra les lignes épaisses de 2 pixels, mais vous pouvez changer la texture pour différentes épaisseurs. C'est plus rapide et plus facile que les calculs barymétriques.

Peter Soltesz
la source
0

utiliser cette fonction: void glPolygonMode (GLenum face, GLenum mode);

face: spécifie les polygones auxquels ce mode s'applique. peut être GL_FRONT pour la face avant du polygone et GL_BACK pour son dos et GL_FRONT_AND_BACK pour les deux.

mode: Trois modes sont définis et peuvent être spécifiés en mode:

GL_POINT: les sommets de polygone marqués comme début d'une arête de frontière sont dessinés sous forme de points.

GL_LINE: Les arêtes limites du polygone sont dessinées sous forme de segments de ligne. (votre cible)

GL_FILL: L'intérieur du polygone est rempli.

PS: glPolygonMode contrôle l'interprétation des polygones pour la pixellisation dans le pipeline graphique.

pour plus d'informations, consultez les pages de référence OpenGL dans le groupe khronos: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glPolygonMode.xhtml

omar ghannou
la source
-1

Si c'est OpenGL ES 2.0 que vous traitez, vous pouvez choisir l'une des constantes du mode de dessin dans

GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, pour tracer des lignes,

GL_POINTS (si vous devez dessiner uniquement des sommets), ou

GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN et GL_TRIANGLESpour dessiner des triangles pleins

comme premier argument à votre

glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid * indices)

ou

glDrawArrays(GLenum mode, GLint first, GLsizei count) appels.

Victor
la source