Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: sf::RenderTexture et gestion de la transparence  (Lu 7723 fois)

0 Membres et 1 Invité sur ce sujet

PetitPoney

  • Newbie
  • *
  • Messages: 44
    • Voir le profil
sf::RenderTexture et gestion de la transparence
« le: Septembre 17, 2013, 02:14:49 pm »
Bonjour !

J'essaie actuellement d'implémenter un shader de type "Glow/Blur".
Cependant j'ai besoin de faire deux passages dans mon shader avant d'afficher le résultat final (un flou horizontal puis un flou vertical). Dans cet optique j'utilise donc une sf::RenderTexture (qui fait la taille de l'objet sur lequel le shader va être appliqué). Cependant au lieu de m'afficher un cercle (la texture appliqué à mon sf::CircleShape est un cercle avec de la transparence autour), j'ai un joli carré  :).
Je peux corriger ce problème directement dans le shader, mais j'aimerais comprendre si le problème vient de sf::RenderTexture ou de mon code.

En parlant de code ..

Voilà la fonction que j'utilise pour faire le premier passage
avec en membre de ma classe :
_ping -> un sf::RenderTexture
_internLight -> un sf::Shader

void    GStarShip::precomputeShader(void)
{
  sf::RenderStates      states;

  _ping.clear(sf::Color(0, 0, 0, 0));
  _internLight.setParameter("vertical", 0);
  states.shader = &_internLight;
  _ping.draw(_intern, states);
  _internLight.setParameter("vertical", 1);
}
 

Pour le deuxième passage (lors de l'affichage final de mon objet)
void    GStarShip::draw(sf::RenderTarget &target,
                        sf::RenderStates states, Player *player) const
{
  sf::Sprite    pong;

  pong.setTexture(_ping.getTexture());  //  <-- Ici j'ai l'impression que _ping contient une texture qui ne gère pas la transparence... ??
  pong.setOrigin(_ping.getTexture().getSize().x / 2.0, _ping.getTexture().getSize().y / 2.0);
  pong.setPosition(player->getX(), player->getY());
  states.shader = &_internLight;
  target.draw(pong, states);
}
 

Pour préciser la chose, cela ne vient pas du code du shader (qui est fonctionnel et applique simplement un effet de dégradé pour le moment).

Merci d'avoir pris le temps de me lire.

Cordialement.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::RenderTexture et gestion de la transparence
« Réponse #1 le: Septembre 17, 2013, 02:42:27 pm »
Première chose, je ne vois pas de _ping.display(). Mais bon, ça n'explique pas ton problème.

Donc seconde chose : essaye de reproduire le problème dans un code complet minimal, ce sera beaucoup plus simple à debugger, à la fois pour nous et pour toi.

Et au fait, ceci :

sf::RenderStates states;
states.shader = &_internLight;
_ping.draw(_intern, states);


... est identique à cela :

_ping.draw(_intern, &_internLight);
Laurent Gomila - SFML developer

PetitPoney

  • Newbie
  • *
  • Messages: 44
    • Voir le profil
Re : sf::RenderTexture et gestion de la transparence
« Réponse #2 le: Septembre 17, 2013, 05:51:12 pm »
Citer
Première chose, je ne vois pas de _ping.display(). Mais bon, ça n'explique pas ton problème.

Est-ce vraiment nécessaire ? Cela inverse la texture  :-\.
D'ailleurs, si vous avez quelques minutes pour m'expliquer en quoi l'appel à draw est obligatoire j'en serai ravi  :)

Citer
Donc seconde chose : essaye de reproduire le problème dans un code complet minimal, ce sera beaucoup plus simple à debugger, à la fois pour nous et pour toi.

Certe. Je vais faire ça tout de suite !

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::RenderTexture et gestion de la transparence
« Réponse #3 le: Septembre 17, 2013, 06:07:18 pm »
Citer
Est-ce vraiment nécessaire ? Cela inverse la texture
Normalement ça inverse la texture si tu ne l'appelles pas, justement.

Citer
D'ailleurs, si vous avez quelques minutes pour m'expliquer en quoi l'appel à draw est obligatoire j'en serai ravi
Tu veux dire l'appel à display() ? Il faut l'appeler parce qu'il y a toujours des trucs à faire après avoir dessiné ;)
Laurent Gomila - SFML developer

PetitPoney

  • Newbie
  • *
  • Messages: 44
    • Voir le profil
Re : sf::RenderTexture et gestion de la transparence
« Réponse #4 le: Septembre 17, 2013, 06:42:56 pm »
Bon alors ce code fonctionne (j'ai fais sauter toute les vérifications pour le simplifier). Donc le problème doit venir d'ailleurs dans mon projet.
Est-ce que j'applique correctement le principe en "ping / pong" me permettant d'appliquer plusieurs shader avant le rendu (avec la sf::RenderTexture) ?

#include <SFML/Graphics.hpp>

void loadShader(sf::Shader &shader)
{
  const std::string sVert = "void main()" \
    "{"                                                         \
    "gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"\
    "gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;" \
    "gl_FrontColor = gl_Color;" \
    "}";

  const std::string sFrag = "uniform sampler2D texture;" \
    "void main()" \
    "{" \
    "vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);" \
    "gl_FragColor.r = gl_Color * pixel;" \
    "}";

     shader.loadFromMemory(sVert, sFrag); // <- Avec ça ce sera déjà mieux ...
}

void defCircle(sf::CircleShape &circle)
{
  circle.setFillColor(sf::Color::Blue);
  circle.setRadius(50);
  circle.setOrigin(25, 25);
  circle.setPosition(250, 250);
}

int main(void)
{
  sf::RenderWindow      window(sf::VideoMode(500, 500), "Minimal");
  sf::RenderTexture     buff;

  // Creation de la sf::RenderTexture "tampon"
  buff.create(500, 500);

  // Chargement des shaders
  sf::Shader            shader;
  loadShader(shader);

  // Creation du sf::Shape avec sa texture
  sf::Texture           texture;
  sf::CircleShape       circle;
  texture.loadFromFile("./texture.png");
  defCircle(circle);
  circle.setTexture(&texture);  // Avec ça aussi ça ira mieux ..
 
 while (window.isOpen())
    {
      // Events
      sf::Event event;
      while (window.pollEvent(event))
            if (event.type == sf::Event::Closed)
              window.close();

      // On clear tout
      window.clear();
      buff.clear();

      // On applique la première couche dans le sf::RenderTexture
      buff.draw(circle, &shader);
      buff.display();

      // On récupère la texture précédente
      sf::Sprite sprite;
      sprite.setTexture(buff.getTexture());

      // On applique une deuxième couche sur la texture
      // (du moins le sprite, précédent)
      window.draw(sprite, &shader);
      window.display();
    }
  return 0;
}
     

Citer
Tu veux dire l'appel à display() ? Il faut l'appeler parce qu'il y a toujours des trucs à faire après avoir dessiné ;)
Oui display() pardon  :). Enfin au pire je pourrais toujours aller voir dans le code ^^
« Modifié: Septembre 17, 2013, 08:55:53 pm par PetitPoney »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::RenderTexture et gestion de la transparence
« Réponse #5 le: Septembre 17, 2013, 08:52:43 pm »
Oui ça me paraît impec :)
Laurent Gomila - SFML developer

PetitPoney

  • Newbie
  • *
  • Messages: 44
    • Voir le profil
Re : sf::RenderTexture et gestion de la transparence
« Réponse #6 le: Septembre 17, 2013, 09:52:29 pm »
Bon ça y est j'ai réussi à reproduire mon problème a partir du code minimal
Le voici

#include <SFML/Graphics.hpp>

void loadShader(sf::Shader &shader)
{
  const std::string sVert = "void main()" \
    "{"                                                         \
    "gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"\
    "gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;" \
    "gl_FrontColor = gl_Color;" \
    "}";

  const std::string sFrag = "uniform sampler2D texture;" \
    "void main()" \
    "{" \
    "vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);" \
    "float d = 1.0 - distance(gl_TexCoord[0].xy, vec2(0.7, 0.5));" \
    "gl_FragColor.r = pixel.r * d;" \
    "gl_FragColor.g = pixel.g * d;" \
// <- Ici je n'utilise que la couleur de ma texture (qui est blanche) car
// gl_Color.rgb est à 0 (pas de setFillColor(...) dans le code
    "gl_FragColor.b = pixel.b * d;" \
    "gl_FragColor.a = 1.0;"\
    "}";

  shader.loadFromMemory(sVert, sFrag);
}

void defCircle(sf::CircleShape &circle)
{
  circle.setRadius(50);
  circle.setOrigin(25, 25);
  circle.setPosition(250, 250);
}

int main(void)
{
  sf::RenderWindow      window(sf::VideoMode(500, 500), "Minimal");
  sf::RenderTexture     buff;

  // Creation de la sf::RenderTexture "tampon"
  buff.create(50, 50);
// <- Dans mon projet, je ne peux pas créer une texture
// représentant l'entièreté de la map (elle est trop grande, en
// plusieurs milliers de pixels
// C'est ici que je loupe quelque chose je pense !

  // Chargement des shaders
  sf::Shader            shader;
  loadShader(shader);

  // Creation du sf::Shape avec sa texture
  sf::Texture           texture;
  sf::CircleShape       circle;
  texture.loadFromFile("./texture.png");
  defCircle(circle);
  circle.setTexture(&texture);
  circle.setTextureRect(sf::Rect<int>(16, 16, 128, 128));
// <- Ma texture n'est pas un cercle parfait,
// je laisse volontairement des bord tranparent autour
// du cercle pour pouvoir faire mon effet Glow/Rim

  while (window.isOpen())
    {
      // Events
      sf::Event event;
      while (window.pollEvent(event))
        if (event.type == sf::Event::Closed)
          window.close();

      // On clear tout
      window.clear();
      buff.clear();

      // On applique la première couche dans le sf::RenderTexture
      buff.draw(circle, &shader);
      buff.display();

      // On récupère la texture précédente
      sf::Sprite sprite;
      sprite.setTexture(buff.getTexture());
      sprite.setOrigin(25, 25);
      sprite.setPosition(50, 50);

      // On applique une deuxième couche sur la texture
      // (du moins le sprite, précédent)
      window.draw(sprite, &shader);
      window.display();
    }
  return 0;
}
 

Mon problème se situe donc au niveau de la taille de ma sf::RenderTexture.

Ne fonctionne pas (écran noir)
buff.create(50, 50);

Fonctionne (cercle blanc avec effet de lumière)
buff.create(500, 500); // Même taille que la fenêtre

Donc à mon avis il y a quelque chose qui m'échappe  :(

PS : Par rapport au shader, j'ai entendu dire qu'il était possible de faire plusieurs passage directement dans le shader (avec des "path" il me semble), mais je n'ai pas trouvé d'infos là dessus sur le net. Auriez-vous une connaissance plus poussée que moi de la GLSL ?

Cordialement
« Modifié: Septembre 17, 2013, 10:01:55 pm par PetitPoney »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::RenderTexture et gestion de la transparence
« Réponse #7 le: Septembre 17, 2013, 10:16:41 pm »
Pourquoi est-ce que ton cercle est positionné en (250, 250) ? Ca ne te paraît pas normal qu'il n'apparaisse pas sur une target de 50x50 pixels ? :P
Laurent Gomila - SFML developer

PetitPoney

  • Newbie
  • *
  • Messages: 44
    • Voir le profil
Re : sf::RenderTexture et gestion de la transparence
« Réponse #8 le: Septembre 17, 2013, 10:33:26 pm »
D'accord ! Je viens de saisir le truc. Forcément ça ne pouvait pas fonctionner.
Déjà ça de résolu.

Mais du coup, avec l'appel à sf::RenderTexture::display(), ça m'inverse ma texture.

EDIT : Si je clear ma sf::RenderTexture avec la couleur sf::Color::Transparent, je n'ai plus de problème d'alpha !
{
Aussi j'ai toujours mon problème d'alpha. Je vois bien mon cercle avec mon effet de lumière dessus, mais la partie transparente autour de ce cercle s'affiche quand même.  :o
Dans mon shader pourtant, je fais bien :
gl_FragColor.a = pixel.a
}

PS : A propos de la screenshot, le point brillant est censé suivre la direction de la pointe (en bas à gauche), sans l'appel à sf::RenderTexture::display(), c'est correct, avec, tout s'inverse (comme sur la screenshot)

Merci beaucoup pour votre temps ! :D
« Modifié: Septembre 17, 2013, 10:50:22 pm par PetitPoney »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::RenderTexture et gestion de la transparence
« Réponse #9 le: Septembre 17, 2013, 10:51:41 pm »
Citer
Aussi j'ai toujours mon problème d'alpha. Je vois bien mon cercle avec mon effet de lumière dessus, mais la partie transparente autour de ce cercle s'affiche quand même.
Concentrons-nous maintenant sur ce problème. Peux-tu développer, voire modifier ton code complet minimal pour qu'il reproduise ce problème ?

Citer
PS : A propos de la screenshot, le point brillant est censé suivre la direction de la pointe (en haut à gauche), sans l'appel à sf::RenderTexture::display(), c'est correct, avec, tout s'inverse (comme sur la screenshot)
N'oublie pas que l'axe Y pointe vers le bas, donc si tu as des calculs d'angles ou autres n'oublie pas d'inverser là où il faut.
Laurent Gomila - SFML developer

PetitPoney

  • Newbie
  • *
  • Messages: 44
    • Voir le profil
Re : sf::RenderTexture et gestion de la transparence
« Réponse #10 le: Septembre 17, 2013, 10:59:43 pm »
Citer
Citer
Aussi j'ai toujours mon problème d'alpha. Je vois bien mon cercle avec mon effet de lumière dessus, mais la partie transparente autour de ce cercle s'affiche quand même.
Concentrons-nous maintenant sur ce problème. Peux-tu développer, voire modifier ton code complet minimal pour qu'il reproduise ce problème ?

Je l'ai résolu avec un sf::RenderTexture.clear(sf::Color::Transparent). Mais je ne suis pas sûr de bien comprendre pourquoi.
En effet, avec un clear(sf::Color::Black) par exemple, l'alpha de ma texture n'est pas pris en compte apparement.
Je vais tenter de reproduire ce problème avec le code minimal précédent.

Citer
N'oublie pas que l'axe Y pointe vers le bas, donc si tu as des calculs d'angles ou autres n'oublie pas d'inverser là où il faut.
J'ai du inverser de base les calculs pour que le résultat soit correct (or c'était surement mon code qui était incorrect au final même si le résultat était celui que j'attendais). C'est un problème mathématique que je saurais résoudre quoi qu'il en soit. Je vais faire un peu de géométrie et ça deviendra plus clair  :)

PetitPoney

  • Newbie
  • *
  • Messages: 44
    • Voir le profil
Re : sf::RenderTexture et gestion de la transparence
« Réponse #11 le: Septembre 17, 2013, 11:20:02 pm »
Pour le problème de l'alpha voilà un exemple :

#include <SFML/Graphics.hpp>

void loadShader(sf::Shader &shader)
{
  const std::string sVert = "void main()" \
    "{"                             \
    "gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;"\
    "gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;" \
    "gl_FrontColor = gl_Color;" \
    "}";

  const std::string sFrag = "uniform sampler2D texture;" \
    "void main()" \
    "{" \
    "vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);" \
    "float d = 1.0 - distance(gl_TexCoord[0].xy, vec2(0.7, 0.5));" \
    "gl_FragColor.r = pixel.r * d;" \
    "gl_FragColor.g = pixel.g * d;" \
    // <- Ici je n'utilise que la couleur de ma texture (qui est blanche) car
    // gl_Color.rgb est à 0 (pas de setFillColor(...) dans le code
    "gl_FragColor.b = pixel.b * d;" \
    "gl_FragColor.a = pixel.a;"\  // Je récupère bien l'alpha de ma texture
    "}";

  shader.loadFromMemory(sVert, sFrag);
}

void defCircle(sf::CircleShape &circle)
{
  circle.setRadius(50);
  circle.setOrigin(25, 25);
}

int main(void)
{
  sf::RenderWindow  window(sf::VideoMode(500, 500), "Minimal");
  sf::RenderTexture buff;

  // Creation de la sf::RenderTexture "tampon"
  buff.create(100, 100);

  // Chargement des shaders
  sf::Shader        shader;
  loadShader(shader);

  // Creation du sf::Shape avec sa texture
  sf::Texture       texture;
  sf::CircleShape   circle;
  texture.loadFromFile("./texture.png");
  defCircle(circle);
  circle.setTexture(&texture);
  circle.setTextureRect(sf::Rect<int>(16, 16, 128, 128));
  // <- Ma texture n'est pas un cercle parfait,
  // je laisse volontairement des bord tranparent autour
  // du cercle pour pouvoir faire mon effet Glow/Rim

  while (window.isOpen())
    {
      // Events
      sf::Event event;
      while (window.pollEvent(event))
        if (event.type == sf::Event::Closed)
          window.close();

      // On clear tout
      window.clear(sf::Color(50, 50, 50, 1.0));
      buff.clear();

      // On applique la première couche dans le sf::RenderTexture
      circle.setPosition(25, 25);
      buff.draw(circle, &shader);
      buff.display();

      // On récupère la texture précédente
      sf::Sprite sprite;
      sprite.setTexture(buff.getTexture());
      sprite.setOrigin(25, 25);
      sprite.setPosition(50, 50);

      // On applique une deuxième couche sur la texture
      // (du moins le sprite, précédent)
      window.draw(sprite, &shader);
      window.display();
    }
  return 0;
}
 

La première screenshot est le résultat du code précédent.
La second est le résultat du même code, excepté que je change la ligne
 
      buff.clear();
 
par
      buff.clear(sf::Color::Transparent);

Cependant selon moi ce n'est pas logique qu'on doivent passer par ce fix, car le shader est censé écrire l'alpha de la texture "physique".png dans la sf::RenderTexture grâce à la ligne
    "gl_FragColor.a = pixel.a;"\  // Je récupère bien l'alpha de ma texture

Aurais-je zappé quelque chose ?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::RenderTexture et gestion de la transparence
« Réponse #12 le: Septembre 18, 2013, 07:59:42 am »
Ton shader ne s'applique qu'au cercle que tu dessines, pas à ce qui est autour.
Laurent Gomila - SFML developer

PetitPoney

  • Newbie
  • *
  • Messages: 44
    • Voir le profil
Re : sf::RenderTexture et gestion de la transparence
« Réponse #13 le: Septembre 18, 2013, 09:47:50 am »
Je pense avoir compris le problème en effet.

Lors du remplissage de ma sf::RenderTexture, je ne retrouve pas l'alpha de ma texture "physique".png car le shader ne va pas faire de calculs en dehors du sf::CircleShape. Du coup je n'ai pas d'alpha dans mon rendu final.
En utilisant clear(sf::Color::Transparent) j'ajoute "de force" de l'alpha dans ma sf::RenderTexture. Je la retrouve ainsi dans mon rendu final.

Je ne trouve pas ça très classe comme technique mais c'est surement la plus simple. Je pourrais faire un sf::RectangleShape qui du coup prendrait l'alpha directement sur la texture "physique".png avec le shader sinon. Pensez-vous qu'une solution est plus propre qu'une autre ?

Merci beaucoup Laurent !
« Modifié: Septembre 18, 2013, 10:05:49 am par PetitPoney »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : sf::RenderTexture et gestion de la transparence
« Réponse #14 le: Septembre 18, 2013, 09:57:41 am »
Et au fait, pourquoi dessiner un cercle avec une texture de cercle ? Dessine un rectangle, ce sera bien plus simple et performant.
Laurent Gomila - SFML developer