web-dev-qa-db-fra.com

Comment obtenir la couleur actuelle d'un fragment?

J'essaie de faire le tour des shaders dans GLSL, et j'ai trouvé des ressources et des tutoriels utiles, mais je continue de me heurter à un mur pour quelque chose qui devrait être fondamental et trivial: comment mon fragment shader récupère-t-il la couleur de le fragment actuel?

Vous définissez la couleur finale en disant gl_FragColor = whatever, mais il s'agit apparemment d'une valeur de sortie uniquement. Comment obtenez-vous la couleur d'origine de l'entrée pour pouvoir effectuer des calculs dessus? Cela doit être dans une variable quelque part, mais si quelqu'un là-bas connaît son nom, il ne semble pas l'avoir enregistré dans un didacticiel ou une documentation que j'ai parcouru jusqu'à présent, et cela me fait monter le mur.

31
Mason Wheeler

Le fragment shader reçoit gl_Color et gl_SecondaryColor comme attributs de sommet. Il obtient également quatre variables variables: gl_FrontColor, gl_FrontSecondaryColor, gl_BackColor, et gl_BackSecondaryColor sur lequel il peut écrire des valeurs. Si vous voulez passer directement les couleurs d'origine, vous feriez quelque chose comme:

gl_FrontColor = gl_Color;
gl_FrontSecondaryColor = gl_SecondaryColor;
gl_BackColor = gl_Color;
gl_BackSecondaryColor = gl_SecondaryColor;

Une fonctionnalité fixe dans le pipeline suivant le vertex shader les fixera ensuite à la plage [0..1] et déterminera si le sommet est orienté vers l'avant ou vers l'arrière. Il interpolera ensuite la couleur choisie (avant ou arrière) comme d'habitude. Le fragment shader recevra alors les couleurs choisies, serrées et interpolées comme gl_Color et gl_SecondaryColor.

Par exemple, si vous avez dessiné le "triangle de la mort" standard comme:

glBegin(GL_TRIANGLES);
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3f(-1.0f, 0.0f, -1.0f);
    glColor3f(0.0f, 1.0f, 0.0f);
    glVertex3f(1.0f, 0.0f, -1.0f);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3d(0.0, -1.0, -1.0);
glEnd();

Ensuite, un vertex shader comme celui-ci:

void main(void) {
    gl_Position = ftransform();
    gl_FrontColor = gl_Color;
}

avec un fragment shader comme celui-ci:

void main() {
    gl_FragColor = gl_Color;
}

transmettra les couleurs, comme si vous utilisiez le pipeline à fonctionnalité fixe.

14
Jerry Coffin

Si vous souhaitez effectuer un rendu en plusieurs passes, c'est-à-dire si vous avez effectué un rendu sur le framebuffer et que vous souhaitez effectuer une deuxième passe de rendu où vous utilisez le rendu précédent, la réponse est:

  1. Rendre le premier passage à une texture
  2. Lier cette texture pour la deuxième passe
  3. Accédez au pixel rendu en privé dans le shader

Code de shader pour 3.2:

uniform sampler2D mytex; // texture with the previous render pass

layout(pixel_center_integer) in vec4 gl_FragCoord;
// will give the screen position of the current fragment

void main()
{
  // convert fragment position to integers
  ivec2 screenpos = ivec2(gl_FragCoord.xy);
  // look up result from previous render pass in the texture
  vec4 color = texelFetch(mytex, screenpos, 0);
  // now use the value from the previous render pass ...
}

Une autre méthode de traitement d'une image rendue serait OpenCL avec OpenGL -> OpenCL interop. Cela permet plus de CPU comme le calcul.

12
Danvil

Si ce que vous appelez "valeur actuelle du fragment" est la valeur de couleur de pixel qui était dans la cible de rendu avant l'exécution de votre shader de fragment, alors non, elle n'est pas disponible.

La raison principale en est que, potentiellement, au moment où votre shader de fragments s'exécute, il n'est pas encore connu. Les shaders de fragments s'exécutent en parallèle, affectant potentiellement (selon le matériel) le même pixel, et un bloc séparé, lisant à partir d'une sorte de FIFO, est généralement responsable de les fusionner plus tard. Cette fusion est appelée "Blending" et ne fait pas encore partie du pipeline programmable. C'est une fonction fixe, mais il a un certain nombre de façons différentes de combiner ce que votre shader de fragment a généré avec la valeur de couleur précédente du pixel.

6
Bahbar

Vous devez échantillonner la texture aux coordonnées de pixels actuelles, quelque chose comme ça

vec4 pixel_color = texture2D (tex, gl_TexCoord [0] .xy);

Remarque, - comme je l'ai vu, texture2D est déconseillé dans la spécification GLSL 4.00 - recherchez simplement des fonctions similaires de texture ... fetch.

Parfois aussi, il est préférable de fournir vos propres coordonnées de pixels au lieu de gl_TexCoord [0] .xy - dans ce cas, écrivez vertex shader quelque chose comme:

variable vec2 texCoord; 
 
 void main (void) 
 {
 gl_Position = vec4 (gl_Vertex.xy, 0.0, 1.0); 
 texCoord = 0,5 * gl_Position.xy + vec2 (0,5); 
}

Et dans le fragment shader, utilisez cette variable texCoord au lieu de gl_TexCoord [0] .xy.

Bonne chance.

4

L'intérêt de votre fragment shader est de décider de la couleur de sortie. La façon dont vous faites cela dépend de ce que vous essayez de faire.

Vous pouvez choisir de configurer les choses de manière à obtenir une couleur interpolée basée sur la sortie du vertex shader, mais une approche plus courante consisterait à effectuer une recherche de texture dans le fragment shader en utilisant les coordonnées de texture transmises dans les interpolants du vertex shader . Vous devez ensuite modifier le résultat de votre recherche de texture en fonction des calculs d'éclairage choisis et de tout ce que votre shader est censé faire, puis l'écrire dans gl_FragColor.

3
Alan

Le pipeline GPU a accès aux informations sur les pixels sous-jacents immédiatement après l'exécution des shaders. Si votre matériau est transparent, l'étape de mélange du pipeline combinera tous les fragments.

Généralement, les objets sont mélangés dans l'ordre où ils sont ajoutés à une scène, sauf s'ils ont été ordonnés par un algo de mise en mémoire tampon z. Vous devez d'abord ajouter vos objets opaques, puis ajouter soigneusement vos objets transparents dans l'ordre à mélanger.

Par exemple, si vous voulez une superposition HUD sur votre scène, vous devez simplement créer un objet quadrillage d'écran avec une texture transparente appropriée, et l'ajouter à votre scène en dernier.

La définition des fonctions de fusion SRC et DST pour les objets transparents vous donne accès au mélange précédent de différentes manières.

Vous pouvez utiliser la propriété alpha de votre couleur de sortie ici pour faire un mélange vraiment sophistiqué. C'est le moyen le plus efficace d'accéder aux sorties du framebuffer (pixels), car il fonctionne en un seul passage (Fig.1) du pipeline GPU.

enter image description here
Fig.1 - Un seul passage

Si vous avez vraiment besoin de multi-passes (Fig.2), vous devez alors cibler les sorties du framebuffer sur une unité de texture supplémentaire plutôt que sur l'écran, et copier cette texture cible sur la prochaine passe, et ainsi de suite, cibler l'écran dans la passe finale. Chaque passage nécessite au moins deux changements de contexte.

La copie supplémentaire et le changement de contexte dégraderont gravement les performances de rendu. Notez que les pipelines GPU multi-threads ne sont pas très utiles ici, car le multi-pass est intrinsèquement sérialisé.

enter image description here
Fig.2 - Multi Pass

J'ai eu recours à une description verbale avec des diagrammes de pipeline pour éviter la dépréciation, car le langage de shader (Slang/GLSL) est susceptible de changer.

1
Dominic Cerisano