Quelle est la meilleure méthode pour mettre à jour les uniformes de shader?

10

Quelle est la manière la plus acceptée de maintenir à jour les matrices d'un shader et pourquoi?

Par exemple, pour le moment, j'ai une Shaderclasse qui stocke les poignées du programme de shaders GLSL et des uniformes. Chaque fois que je déplace la caméra, je dois passer la nouvelle matrice de vue au shader, puis chaque objet du monde différent, je dois transmettre sa matrice de modèle au shader.

Cela me limite sérieusement car je ne peux rien faire sans avoir accès à cet objet shader.

J'ai pensé à créer une ShaderManagerclasse singleton chargée de tenir tous les shaders actifs. Je peux ensuite accéder à cela de n'importe où et un objet mondial n'aurait pas besoin de savoir quels shaders sont actifs, juste qu'il doit faire ShaderManagerconnaître les matrices souhaitées, mais je ne suis pas sûr que c'est la meilleure façon et il y a probablement des problèmes qui découlera de cette approche.

Lerp
la source
Pourquoi ne pas transmettre les données aux shaders au moment du rendu?
Nick Caplinger
C'est ce que j'ai l'intention mais comment? Si un objet crée un shader, comment les objets mondiaux sont-ils censés connaître l'existence de ce shader et lui transmettre les matrices nécessaires?
Lerp

Réponses:

11

Utilisez des tampons uniformes (c'est- à- dire des tampons constants dans le jargon D3D).

Tant que tous vos shaders s'accordent sur la disposition et le point de liaison de chacun de ces tampons, la mise à jour devient un jeu d'enfant. Les modèles n'ont absolument pas besoin de savoir quoi que ce soit sur les shaders. Ils n'ont qu'à mettre à jour la matrice de vue du modèle dans leur tampon constant et le pipeline de rendu l'utilisera automatiquement.

Au minimum, je dirais que vous devriez avoir deux de ces tampons. Le premier doit stocker votre matrice de projection, la matrice de la caméra, la matrice de projection de la caméra concaténée, les informations de la fenêtre, les détails du frottement et les inverses de la matrice. Vous n'avez besoin de mettre à jour ce tampon qu'une fois par scène.

Donnez ensuite à chaque modèle un autre tampon pour stocker sa matrice de vue modèle, sa matrice normale, ses inverses et ses propriétés de matériau. Ceci est mis à jour une fois pour chaque modèle et peut être effectué dans une passe de mise à jour différente (si / quand cela est approprié). Les informations sur le matériau peuvent / doivent être déplacées vers un troisième tampon spécifique au matériau si vous avez la possibilité de partager des matériaux entre plusieurs objets.

Dans une configuration d'ombrage avant, il est logique de placer toutes les lumières dans un autre tampon et en ombrage différé, il est également judicieux d'utiliser un tampon par lumière pour le passage de la lumière (à la place d'un modèle / tampon de matériau utilisé dans le passe géométrique).

Notez que vous avez besoin d'une version modérément à jour de GL pour utiliser des tampons uniformes (3.1 ou une extension; assez courant aujourd'hui, sauf sur certains ordinateurs portables plus anciens mais toujours en service) et vous avez besoin d'une version assez récente pour être capable de lier des tampons uniformes à des emplacements de liaison spécifiques à partir du code du shader (4.2; toujours rare mais de mieux en mieux) sinon vous devez faire plus de travail dans votre code côté CPU pour configurer les choses (votre code CPU doit connaître la bonne liaison de toute façon, c'est plus une odeur d'API qu'un problème sérieux). OpenGL | ES n'a pas ajouté de tampons uniformes avant la 3.0, qui n'est toujours pas pris en charge sur la plupart des plates-formes mobiles populaires, malheureusement.

Si les tampons ne sont pas une option, vous aurez besoin d'un emplacement global pour stocker les emplacements d'index pour le shader actif. Vous pouvez utiliser glGetUniformLocationaprès avoir chargé votre shader pour trouver les index de noms connus (similaires ModelViewMatrixou similaires), puis stocker ces index. Votre rendu peut mapper des valeurs d'énumération comme MODEL_VIEWpassées à une SetUniformfonction wrapper pour examiner le shader lié, trouver l'index et appeler glUniformcorrectement. Ce n'est pas un énorme changement particulier dans l'utilisation du code client par rapport aux tampons, à part le fait de devoir définir chaque uniforme individuellement si vous obtenez tout bien emballé.

Voir Blocs d'interface GLSL et objets tampons uniformes .

Sean Middleditch
la source
Je savais que ce serait une manière intégrée. Merci!
Lerp
4

La façon la plus simple de le faire est de normaliser simplement les noms uniformes entre vos shaders et d'envoyer simplement toutes les données juste avant de dessiner. Les frais généraux ne sont pas fous, mais vous pouvez les optimiser plus tard pour ne pas toujours envoyer des uniformes moins mis à jour.

La façon dont je fais cela dans mon jeu est que j'ai une Materialclasse abstraite . Les matériaux servent de pont entre le jeu et les shaders. Chacun Materiala une Shaderet diverses propriétés qui peuvent être définies, y compris des textures. Quand il est temps de dessiner un objet, il Materialest lié. La Bindméthode a un GraphicsStateparamètre qui contient tous les états graphiques actuels - matrices, lumières, etc. La Bindméthode lie le shader et les textures et définit tous les uniformes, en utilisant tout ce dont elle a besoin depuis le GraphicsState.

Robert Rouhani
la source