OpenGL obtient le contour de plusieurs objets qui se chevauchent

10

Je viens d'avoir une idée pour mon jeu en cours fait avec opengl en c ++: j'aimerais avoir un grand contour (5-6 pixels) sur plusieurs objets qui se chevauchent lorsque le joueur gagne quelque chose.

Je pensais que la meilleure façon était d'utiliser le tampon de pochoir, mais c'est quelques heures que j'essaie de faire un rendu hors écran du tampon de pochoir et je ne peux obtenir aucun résultat, donc probab. il y a d'autres techniques!

Voici ce que je veux obtenir:

entrez la description de l'image ici

Des idées?

nkint
la source
Utilisez un filtre de détection des bords, remplissez les bords avec des lignes colorées épaisses, puis extrayez les images rendues des formes et superposez-les au-dessus du calque des lignes colorées.
Shotgun Ninja
que voulez-vous dire avec un filtre de détection de bord? un shader? un filtre de traitement d'image? comme opencv (rendre à la texture, appliquer un filtre à la texture, repousser la texture modifiée)?
nkint
Je n'ai aucune idée; Je ne connais pas très bien le rendu 3D pour commencer.
Shotgun Ninja
avez-vous un exemple de tampon de pochoir comme celui-ci? Je pense que l'utilisation du tampon de pochoir serait la manière la plus propre, mais je ne suis pas en mesure de faire fonctionner un tampon de pochoir
nkint

Réponses:

4
  1. Activez et videz le tampon de gabarit.
  2. Dessinez les objets en définissant le tampon du pochoir. Les objets peuvent être semi-transparents, etc.
  3. Maintenant, définissez le mode de gabarit pour écrire uniquement les pixels où le gabarit n'est pas défini.
  4. Et dessinez à nouveau chaque objet, légèrement agrandi, dans la couleur de bordure souhaitée et sans textures.
  5. Désactivez le tampon de gabarit.

Voici le code adapté d'un code de gabarit webGL que je travaille:

// drawing will set stencil stencil
    gl.enable(gl.STENCIL_TEST);
    gl.stencilFunc(gl.ALWAYS,1,1);
    gl.stencilOp(gl.KEEP,gl.KEEP,gl.REPLACE);
    gl.stencilMask(1);
    gl.clearStencil(0);
    gl.clear(gl.STENCIL_BUFFER_BIT);
// draw objects
for(var object in objects)
  objects[object].draw();
// set stencil mode to only draw those not previous drawn
    gl.stencilFunc(gl.EQUAL,0,1);
    gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP);
    gl.stencilMask(0x00);
// draw object halo
for(var object in objects)
  objects[object].drawHalo(1.1,red); // each mesh should be individually scaled e.g. by 1.1
// done
    gl.disable(gl.STENCIL_TEST);

Je pense que j'ai utilisé cette approche dans les jeux RTS pour dessiner des halos autour des unités sélectionnées, mais c'était il y a longtemps et je ne me souviens pas s'il y a des accrochages et toutes les nuances.

Volonté
la source
avez-vous un exemple de tampon de pochoir comme celui-ci? Je pense que l'utilisation du tampon de pochoir serait la manière la plus propre, mais je ne suis pas en mesure de faire fonctionner un tampon de pochoir
nkint
7
Notez que le rendu des objets légèrement agrandis n'entraînera pas une épaisseur de ligne uniforme. Les bords plus éloignés seront plus minces. Si vous en tenez compte lors de la mise à l'échelle des objets, les objets longs qui s'étendent dans la distance auront une épaisseur non uniforme. Il y a un peu plus à cet effet pour rendre les lignes belles et uniformes.
Sean Middleditch
2
Mieux que simplement augmenter l'échelle des objets, il est préférable d'écrire un vertex shader qui décale chaque sommet sur une courte distance le long de sa normale. Cela fonctionne assez bien pour les objets lisses, mais cela générera des fissures sur les bords durs. Vous pouvez essayer de construire le maillage avec un ensemble alternatif de normales lissées partout et voir où cela vous mène.
Nathan Reed
2

Commencez par trouver tous les groupes d'objets, où un groupe d'objets est une collection d'objets qui se chevauchent. La détection de collision standard devrait faire l'affaire. Attribuez à chaque groupe une couleur unique. N'importe quelle couleur ferait l'affaire.

Rendez tous vos objets en couleurs unies, en utilisant la couleur de groupe, à une texture.

Créez une nouvelle texture de contour avec les mêmes dimensions que la cible de rendu. Parcourez chaque texel de la cible de rendu et déterminez s'il s'agit d'une couleur différente des texels environnants. Si c'est le cas, changez le texel correspondant dans la texture de contour en la couleur de ligne souhaitée.

Enfin, prenez cette texture de contour et rendez-la sur le dessus de l'image que vous souhaitez dessiner à l'écran (vous pouvez bien sûr le faire en même temps que la détection de bord dans un fragment shader et éviter de créer la texture de bord dans le premier endroit).

Si vous effectuez cette étape sur le processeur en utilisant une boucle for pour parcourir les texels de la cible de rendu, cela sera assez lent, mais probablement assez bon pour tester et même utiliser dans certains cas. Pour l'utiliser en temps réel, il serait préférable de gérer cela dans un shader.

Un shader de fragment pour effectuer cette détection de bord pourrait ressembler à ceci;

precision mediump float;

uniform sampler2D s_texture;

varying vec2 v_texCoord;

void main()
{
    gl_FragColor = vec4(0.0);

    vec4 baseColor = texture2D(s_texture, v_texCoord);
    gl_FragColor += baseColor - texture2D(s_texture, top);
    gl_FragColor += baseColor - texture2D(s_texture, topRight);
    gl_FragColor += baseColor - texture2D(s_texture, right);
    gl_FragColor += baseColor - texture2D(s_texture, bottomRight);
    gl_FragColor += baseColor - texture2D(s_texture, bottom);
    gl_FragColor += baseColor - texture2D(s_texture, bottomLeft);
    gl_FragColor += baseColor - texture2D(s_texture, left);
    gl_FragColor += baseColor - texture2D(s_texture, topLeft);
}

Où la deuxième valeur dans la recherche texture2D est une coordonnée 2D par rapport à v_texCoord. Vous appliqueriez cela en rendant la première cible de rendu comme texture sur un quadrillage plein écran. Ceci est similaire à la façon dont vous appliqueriez des effets de flou en plein écran tels qu'un flou guassien.

La raison d'utiliser la première cible de rendu avec des couleurs unies est simplement de s'assurer qu'il n'y a pas de bord perçu entre différents objets qui se chevauchent. Si vous avez simplement effectué une détection des bords sur l'image à l'écran, vous constaterez probablement qu'il détecte également les bords aux chevauchements (en supposant que les objets ont des couleurs / textures / éclairages différents).

OriginalDaemon
la source
2
désolé, mais que voulez-vous dire par "parcourir chaque texel"? une boucle pour chaque pixel? dans le cpu? c'est donc quelque chose comme: rendre avec une couleur unie à une texture, transférer l'image sur le processeur, faire le scan, les remettre dans la texture? ou le faire dans un shader?
nkint
De préférence, faites-le dans un shader en rendant un quadrillage plein écran en utilisant la cible de rendu comme texture, de la même manière que pour faire un effet de flou post-traitement, mais vous pouvez le faire fonctionner d'abord sur le processeur avec une boucle for, juste pour voir si cela fonctionne assez bien.
OriginalDaemon