7
votes

OpenGL: Comment rendre un gradient rectangulaire parfait?

Je peux rendre un gradient triangulaire avec simplement un triangle et utiliser GLColor pour chaque coin.

Mais comment rendre un gradient rectangulaire parfait? J'ai essayé avec un quad, mais le milieu deviendra la laide. J'ai également essayé avec une texture de taille 2x2, c'était comme si cela devrait être fait: un mélange correct de chaque coin, mais la précision de l'échantillonnage de la texture devient sans précédent lorsqu'il est trop étiré (j'ai commencé à voir des pixels plus grands que la taille 1x1).

Y a-t-il un moyen de calculer cela dans un shader peut-être?

-

edit: Le lien vers des images a été brisé (enlevé).


5 commentaires

Quelle version de OpenGL utilisez-vous? Contexte avant ou compatible?


J'aimerais que cela soit compatible avec la version aussi basse que possible.


Cela ressemble à deux triangles plutôt qu'à un seul quad (le centre est interpolé entre deux coins, pas tous les quatre.


@ben, oui ... Quad est formé de deux triangles, comment puis-je le faire sans triangles dans OpenGL ...? Au fait, j'ai utilisé Gl_quads là-bas.


@Ben Voigt, regarde ma nouvelle image maintenant, ça ne marche pas.


3 Réponses :


0
votes

Tous vos sommets ont-ils la valeur de la même profondeur (Z) et sont tous vos triangles complètement à l'écran? Si tel est le cas, vous ne devriez pas avoir de problème à obtenir un gradient de couleurs «parfait» sur une quad à partir de deux triangles avec GLColor. Sinon, il est possible que votre implémentation OpenGL traite des couleurs mal.

Cela me conduit à soupçonner que vous puissiez avoir une implémentation très ancienne ou étrange OpenGL. Je vous recommande de nous dire quelle plate-forme utilisée et quelle version de OpenGL vous avez ...?

Sans plus d'informations, je vous recommande de tenter d'écrire un shader et d'éviter de dire OpenGL que vous voulez une "couleur". Si possible, dites-le que vous voulez un "Texcoord" mais que vous le traitez comme une couleur de toute façon. Cette astuce a travaillé dans certains cas où la précision des couleurs est trop faible.


5 commentaires

Oui, c'est 2D. Oui, ils sont complètement à l'écran (pourquoi cela compte-t-il de toute façon? Bien que j'ai vu OpenGL rendu de manière moche sans conducteur si les triangles sont partiellement hors de l'écran, mais je le fais pour les personnes avec des pilotes OpenGL)


HMM, en traitant le Texcoord sous forme de couleur, vous voulez dire que j'utiliserais la valeur numérique de celui-ci pour mélanger les couleurs en utilisant les 4 couleurs d'angle? Pourriez-vous donner des conseils comment devrais-je faire cela? Je n'ai aucune idée, je sais seulement comment obtenir la valeur de la couleur de la texture ou la valeur GLColor ... mais je ne sais pas comment envoyer 4 valeurs de couleur dans le shader de fragment (ni je suis sûr que cela devrait être fait) .


J'ai ajouté des images du problème maintenant.


@Rookie: Je ne vois aucune image. Vous n'avez pas assez de représentant pour les ajouter directement à votre question, de sorte que coller des liens et l'un de nous peut ajouter l'image.


Argh, ça ne devrait pas me le montrer alors si personne d'autre ne peut le voir ... Ajout d'un lien maintenant.



8
votes

Le problème est que vous utilisez un quad. Le quad est dessiné à l'aide de deux triangles, mais les triangles ne sont pas dans l'orientation que vous avez besoin.

Si je les sommets définis quad comme:

  • A : sommet en bas à gauche
  • B : sommet en bas à droite
  • C : sommet en haut à droite
  • D : haut sommet gauche

    Je dirais que le quad est composé par les triangles suivants:

    • A B D
    • D B C

      Les couleurs assignées à chaque sommet sont les suivants:

      • A : jaune
      • B : rouge
      • C : jaune
      • D : rouge

        Compte tenu de la géométrie (les deux triangles), les pixels entre D et B sont le résultat de l'interpolation entre le rouge et rouge: en effet, rouge

        La solution serait la géométrie de deux triangles, mais orientés d'une manière différente:

        • A B C
        • A C D

          Mais sans doute vous n'obtenir le gradient exact, car en milieu de quad, vous obtiendrez un jaune plein, au lieu d'un jaune mélangé avec du rouge. Donc, je suppose que vous pouvez obtenir le résultat exact à l'aide de 4 triangles (ou un ventilateur de triangle), dans lequel le sommet est centré sur l'interpolation entre le jaune et le rouge.


          wooop! Effetively le résultat est pas ce que j'attendais. Je pensais que le gradient a été produit par interpolation linéaire entre les couleurs, mais est sûrement pas (je vraiment besoin de configurer l'espace couleur LCD!). En effet, la solution la plus évolutive est rendu en utilisant les shaders fragment.

          Gardez la solution proposée par Bahbar . Je vous conseille de commencer la mise en œuvre d'un pass-through sommet / fragment shaders (spécifiant les sommets et les couleurs que vous devriez obtenir le résultat précédent); puis, commencer à jouer avec mix fonction et la texture coordonnée transmis au vertex shader.

          Vous avez vraiment besoin de comprendre le pipeline de rendu avec les shaders programmables : vertex shader est appelé une fois par sommet, shaders fragment est appelé une fois par fragment (sans multiéchantillonnage, un fragment est un pixel, avec multiéchantillonnage, pixel aa est composé par un grand nombre de fragments qui sont interpolés pour obtenir la couleur de pixel).

          Le sommet nuanceur prendre les paramètres d'entrée (uniformes et entrées; uniformes sont constantes pour tous les sommets délivrés entre glBegin / glEnd;. Entrées sont caractéristiques de chaque instance de nuanceur de sommets (4 sommets, 4 instances de vertex shaders)

          Un fragment shader prend en entrée les sorties nuanceur vertex qui a produit le fragment (en raison de la rastérisation de triangles, lignes et points). Dans le Bahbar répondre à la seule sortie est uv variable (commune aux deux sources de shaders).

          En vous cas, les sorties vertex shader sommet texture coordonnées UV (passé "comme-sont"). Ces coordonnées UV sont disponibles pour chaque fragment, et elles sont calculées par interpolation des valeurs délivrées par le vertex shader en fonction de la position du fragment.

          Une fois que vous avez ces coordonnées, vous avez seulement besoin de deux couleurs: le rouge et le jaune dans votre cas (en Bahbar réponse correspond à color0 et color1 < / em> uniformes). Ensuite, mélanger les couleurs en fonction des coordonnées UV du fragment spécifique. (*)

          (*) est ici la puissance de shaders: vous pouvez spécifier différentes méthodes d'interpolation en modifiant simplement la source de shaders. Linéaire, bilinéaire ou interpolation Spline sont mis en oeuvre en spécifiant uniformes supplémentaires pour le fragment shader.

          Bonne pratique!


2 commentaires

J'ai essayé une quantité de triangles, même celle que vous avez suggérée "4 coins et 1 point supplémentaire au milieu", mais les résultats sont toujours les mêmes que d'utiliser deux triangles (sauf que la forme change légèrement, mais elle montre toujours des "coutures" et je ne l'aurais toujours pas parfaitement comme je veux). Pourriez-vous aider à faire cela avec un shader, peut-être? Je pensais que je pouvais calculer moi-même le gradient moi-même, mais le problème que j'ai: comment envoyer les 4 valeurs de couleur d'angle pour le shader fragment (et si cela est même comme ça que je devrais le faire ?, J'ai donc besoin de conseils à ce sujet. ..)


Ajout de l'exemple de version que vous avez suggéré, il ne fonctionne toujours pas comme si cela devrait, regarder l'image (ou le lien que je suis resté au cas où l'image ne fonctionne pas)



9
votes

En effet, le type de gradient que vous souhaitez dépend de 4 couleurs à chaque pixel, où OpenGL n'imprime généralement que l'entrée sur les triangles (donc 3 entrées). Obtenir le gradient parfait n'est pas possible juste avec les interpolants standard.

Maintenant, comme vous l'avez mentionné, une texture 2x2 peut le faire. Si vous avez vu des problèmes de précision, je suggère de changer le format de la texture à quelque chose qui nécessite généralement plus de précision (comme une texture de flotteur). P>

dernier, et comme vous l'avez également mentionné dans votre question, vous pouvez résoudre Ceci avec un shader. Dis que vous passez un attribut supplémentaire par sommet qui correspond à (u, v) = (0,0) (0,1) (1,0) (1,0) (1,0) jusqu'au shader de pixel (avec le shader de sommet Juste faire une passe). P>

Vous pouvez procéder comme suit dans le shader pixel (note, l'idée ici est sonore, mais je n'ai pas testé le code): P>

vertex Shader Snippet: P>

uniform vec3 color0;
uniform vec3 color1;
varying vec2 uv;

// from wikipedia on bilinear interpolation on unit square:
// f(x,y) = f(0,0)(1-x)(1-y) + f(1,0)x(1-y) + f(0,1)(1-x)y + f(1,1) xy. 
// applied here:
// gl_FragColor = color0 * ((1-x)*(1-y) + x*y) + color1*(x*(1-y) + (1-x)*y)
// gl_FragColor = color0 * (1 - x - y + 2 * x * y) + color1 * (x + y - 2 * x * y)
// after simplification:
// float temp = (x + y - 2 * x * y);
// gl_FragColor = color0 * (1-temp) + color1 * temp;
gl_FragColor = mix(color0, color1, uv.u + uv.v - 2 * uv.u * uv.v);


10 commentaires

ne devrait-il pas être gl_fragcolor = mix (...) ? aussi ne pas ces float3 et float2 censé être Vec3 et Vec2 ? Pourriez-vous montrer comment je transmettez les attributs supplémentaires et quels changements dont j'ai besoin dans Vertex Shader aussi? Je ne suis pas tout à fait de suivre ça ...


@Rookie: En effet. Je joue avec trop de langues ... Correction de ceux que vous avez mentionnés et que vous avez ajouté un peu sur le sommet du sommet. Qu'est-ce que vous ne suivez pas exactement? L'idée est de mettre en œuvre le filtrage bilinéaire que vous êtes après sur le shader, en utilisant les UV, nous sommes passés pour déterminer où sont dans la case des pixels d'intérêt.


Si je comprends bien, je dois utiliser GLTEXCOORD2F () avec GLCOLOR3F () et GLVERTEX2F ()? Mais quelle partie du code sait que Uvin signifie les coordonnées de texture ... cette partie que je ne reçois pas


De plus, je ne comprends pas comment je remplisse la couleur0 et la couleur1 avec les valeurs correspondantes de Vertex Shader (ou où autre chose?)


@Rookie: Ah, eh bien, je n'ai pas réalisé que vous n'avez pas eu d'expérience avec des shaders ... Vous pouvez utiliser Gltexcoord pour les UV si vous le souhaitez (les attributs génériques que j'utilise utilisent une API différente, GLGETVERTEXATTRILOCATION + GLVERTEXATTRIB). Dans ce cas, remplacez simplement l'UVIN avec le prédéfini GL_MULTTITEXCOORD0.UV. De même, pour l'uniforme, vous devez utiliser GlgeTuniformlocation + gluniform3f). Vous feriez mieux de vérifier un didacticiel GLSL pour comprendre les concepts.


Oh, Isée, mais que diriez-vous de la couleur0 et de la couleur1 ...? quelle logique sont cueillies?


@Rookie: Comme je l'ai dit, utilisez Glgetuniformlocation + gluniform3f pour les variables uniformes (c'est la couleur0 et la couleur1). Ou vous pouvez utiliser des constantes directement dans le shader.


Oui, mais si j'envoie 2 couleurs par sommet, quelles couleurs je envoie des 4 couleurs de coin ...? cette partie que je ne reçois pas.


@Rookie: Les uniformes sont envoyés par tirage complet (c'est-à-dire avant Glbegin, dans votre cas). D'où le nom "uniforme". Je n'ai que 2 couleurs car il semblait que vous vouliez avoir la même couleur en haut à droite et en bas à gauche, identique pour l'autre 2. Regardez les mathématiques dans les commentaires du code pour voir comment de la formelle F (x, y) qui a 4 Constantes, j'ai déménagé à seulement 2 que Share Color0 et Color1


Oh je vois .. Eh bien, vous auriez dû dire que votre code n'a pas calculé de gradient parfait dans votre réponse (sinon quelqu'un d'autre se demande pourquoi cela ne fonctionne pas). Je vais juste faire mes propres calculs alors ... (je ne peux pas comprendre cette formule vraiment XD)