OpenGL: Où dois-je placer les shaders?

17

J'essaie d'apprendre OpenGL ES 2.0 et je me demande quelle est la pratique la plus courante pour "gérer" les shaders.
Je pose cette question parce que dans les exemples que j'ai trouvés (comme celui inclus dans la démonstration de l'API fournie avec le sdk android), je vois généralement tout dans la classe GLRenderer et je préfère séparer les choses pour que je puisse les avoir, par exemple, un objet GLImage que je peux réutiliser chaque fois que je veux dessiner un quad texturé (je me concentre uniquement sur la 2D pour le moment), tout comme je l'avais fait dans mon code OpenGL ES 1.0. Dans presque tous les exemples que j'ai trouvés, les shaders sont simplement définis comme des attributs de classe. Par exemple:

public class Square {

public final String vertexShader =
        "uniform mat4 uMVPMatrix;\n" +
        "attribute vec4 aPosition;\n" +
        "attribute vec4 aColor;\n" +
        "varying vec4 vColor;\n" +
        "void main() {\n" +
        "  gl_Position = uMVPMatrix * aPosition;\n" +
        "  vColor = aColor;\n" +
        "}\n";

public final String fragmentShader =
        "precision mediump float;\n" +
        "varying vec4 vColor;\n" +
        "void main() {\n" +
        "  gl_FragColor = vColor;\n" +
        "}\n";
// ...
}

Je m'excuse à l'avance si certaines de ces questions sont stupides, mais je n'ai jamais travaillé avec des shaders auparavant.

1) Le code ci-dessus est-il le moyen commun de définir les shaders (propriétés de classe finale publique)?
2) Dois-je avoir une classe Shader séparée?
3) Si les shaders sont définis en dehors de la classe qui les utilise, comment pourrais-je connaître les noms de leurs attributs (par exemple "aColor" dans le morceau de code suivant) pour pouvoir les lier?

colorHandle = GLES20.glGetAttribLocation(program, "aColor");
miviclin
la source

Réponses:

16

J'ai toujours détesté cette façon de définir les shaders (dans une chaîne). Je préfère faire le mien dans un fichier texte et le lire lors du chargement. Le définir dans une chaîne est ennuyeux pour le débogage et il me semble juste désordonné. C'est tellement plus facile de pouvoir le taper et le voir formaté comme il se doit, au lieu d'être à l'intérieur d'une chaîne.

J'ai également une classe distincte qui a des fonctionnalités de shader communes, comme la lecture des shaders et l'impression des informations de journalisation pour le débogage des shaders.

Où que les shaders soient définis, vous pouvez accéder aux noms comme vous le faites dans votre exemple. L'utilisation du littéral de chaîne est acceptable pour les shaders car ces valeurs ne changeront probablement pas une fois que vous les aurez implémentées.

Cependant, à vous de décider. La façon dont vous le faites actuellement fonctionne très bien, si cela ne vous cause aucun problème.

MichaelHouse
la source
2
Le fait d'avoir vos shaders dans des fichiers séparés signifie également que vous pouvez utiliser vos éditeurs de shader pratiques et avoir une mise en évidence complète de la syntaxe, et même les prévisualiser avant de les tester dans votre programme, si votre support le permet.
Raceimaztion
6
La seule vraie raison d'avoir des shaders dans une chaîne est pour des tests rapides et des tutoriels .. où la gestion des fichiers ajoute une masse de "code inutile".
Jari Komppa
2
Vous pouvez également implémenter le rechargement à chaud sur les fichiers de shader - vous permettant de déboguer les modifications sans relancer le jeu. Productivité ++
Liosan
J'aime l'idée de les lire dans un fichier et d'avoir une classe Shader séparée. Les voir toujours dans une chaîne me déroutait parce que je ne savais pas si c'était ainsi qu'ils étaient généralement définis ou simplement à des fins d'apprentissage. Merci!
miviclin
8

La gestion des shaders (et donc du matériel) est un problème assez délicat que vous rencontrez lorsque votre système graphique devient plus complexe et que vous remarquez qu'un codage dur chaque shader entraînerait une duplication de code massive. Voici quelques façons alternatives de le résoudre:

  • Les petits exemples où il n'y a que quelques shaders ont tendance à les coder en dur comme des chaînes pour éviter la gestion des fichiers comme l'a commenté Jari Komppa
  • Il vaut mieux utiliser des fichiers séparés où vous pouvez avoir une coloration syntaxique et une mise en forme appropriée. Vous pouvez également coder un système de débogage simple qui surveille les modifications dans ces fichiers et appliquer le shader modifié à la volée pendant que votre jeu est en cours d'exécution.
  • Lorsque le nombre de matériaux augmente, un générateur de shader devient presque une nécessité. Fondamentalement, vous définissez des extraits de code communs tels que "fragment shader normal mapping snippet" et composez vos shaders complets en incluant les composants souhaités.
    • Vous pouvez utiliser des extraits de chaîne codés en dur, mais cela devient rapidement très compliqué, donc une fois de plus, les fichiers sont une bonne idée.
    • Vous pouvez soit avoir des bits de code dans des fichiers séparés (ou les mêmes), soit utiliser ubershader / supershader où vous utilisez le préprocesseur (ou des drapeaux uniformes) pour activer / désactiver les éléments.

En ce qui concerne les noms d'attribut et uniformes et autres, vous utilisez simplement une dénomination cohérente dans tous les shaders.

Tapio
la source
Je déplacerai certainement mes shaders dans un fichier séparé. Merci!
miviclin