Post screen shaders

C'est un sujet réellement intéressant car il traite d'une fonctionnalité très puissante. Une fois que vous saurez ce qu'est un post screen shader vous serez d'accord avec moi. Donc voici l'explication. Vous savez déjà ce qu'un effet de shader standard. Si ce n'est pas le cas, lisez le chapitre qui en parle. Si vous ne savez pas comment écrire des shaders vous ne pourrez pas écrire de post screen shader, et si vous ne savez pas ce que c'est, vous ne pouvez pas les utiliser non plus. Les effets de shader normaux sont géniaux, parce qu'ils peuvent être utilisés pour de nombreux effets vraiment corrects. Cependant, leurs possibilités sont limitées, parce qu'ils peuvent être appliqués seulement à des matériaux des modèles. C'est un désavantage que les post screen shaders n'ont pas. Ils ne sont pas appliqués à des matériaux particuliers ou à des modèles, mais à l'écran tout entier. Après qu'Ultimate 3D ait rendu la scène en entier, le post screen shader est appliqué au rendu. Cela les rends puissants.

Un exemple simple pour cela est que vous souhaitez que tout soit noir et blanc pour certaines raisons. Vous pouvez le faire en créant des copies noires de toutes les textures et en changeant toutes les couleurs des matériaux en niveaux de gris. Vous pouvez utiliser un effet de pixel shader pour absolument tout ce qui est visible. Ce n'est pas non plus souhaitable, parce que cela implique de changer chacun des objets de votre scène. Avec un post screen shader, cela peut être réalisé plus facilement. Vous écrivez un simple (très simple) post screen shader qui implémente l'effet noir et blanc. Ensuite vous l'appliquez et vous avez fini, sans toucher à un seul objet de la scène. Le post screen shader prend seulement l'image rendue, modifie la couleur de chacun des pixels et l'affiche à l'écran.

C'est un exemple où le post screen shader vous épargne beaucoup de travail, mais il ne fait rien qui ne soit impossible sans lui. Voici donc un autre exemple, qui ne pourrait être réalisé sans les post screen shaders: un simple effet de blur. Imaginez que le personnage principal de votre jeu a été empoisonné. Vous avez donc besoin d'un effet sympa pour montrer au joueur que le personnage ne sent pas bien. Un effet de blur qui s'applique à l'écran tout entier, est parfait pour cela. Avec les effets de shader usuels, cela ne pourrait pas être implémenté, mais avec les post screen shaders, cela ne pose aucun problème.

Le nombre d'effets possibles est très grand. Bloom, blur et différents effets de motion blur sont des usages communs des post screen shaders. De plus, les post screen shaders peuvent être utilisés pour des effets de déformation. Divers effets exotiques peuvent aussi être réalisés. Par exemple vous pouvez inverser les couleurs, changer le gamma, doubler l'image (pour les personnages principaux soûls ;) ), assombrir ou éclaircir des zones particulières de l'écran, surligner les contours pour donner un effet de bande dessinée, ou bien vous pouvez créer un post screen shader qui donne l'impression que tout est comme dans un vieux film en noir et blanc qui a été regardé des milliers de fois. Ils peuvent aussi être utilisés pour les effets de profondeur de champ, mais ce sont des techniques très avancées, et requérant du temps de calcul intensif.


La façon dont les post screen shaders fonctionnent

C'est un court chapitre et les processus qui seront décrits ici sont réalisés automatiquement par Ultimate 3D. Néanmoins, il est important de connaître cela, parce que sinon, vous ne pourrez pas comprendre pourquoi les post screen shaders doivent ressembler à ce qu'ils sont censés êtres. Lorsque vous commencez à utiliser un post screen shader, Ultimate 3D n'affichera plus la scène directement à l'écran. À la place, il l'affichera à une texture. Une fois que c'est fait, cette texture contient tout ce que vous voyez habituellement à l'écran. Ensuite, cela affiche un rectangle qui couvre tout l'écran et qui utilise cette texture. Ce rectangle utilise votre post screen shader. De cette façon, vous pouvez définir comment la texture, qui contient le rendu de la scene, sera affichée à l'écran.

Il est également possible d'utiliser plusieurs post screen shaders. Ils seront appliqués l'un après l'autre. Si vous utilisez deux post screen shaders, Ultimate 3D rendra la scène complète dans une texture. Ensuite, cela calculera le rendu d'un rectangle qui utilise cette texture de scène dans une seconde texture utilisant le premier post screen shader que vous avez défini. À l'étape suivante, cela rendra cette seconde texture à l'écran en utilisant le second post screen shader que vous avez défini. S'il y avait eu un troisième post screen shader, le second aurait été rendu dans la première texture à nouveau et cette première texture aurait été affichée à l'écran en utilisant le troisième post screen shader. Cela signifie qu'il n'y aura jamais plus de deux textures pour tous les post screen shaders, en terme d'utilisation de mémoire les post screen shaders sont donc vraiment économiques.

Une autre chose que vous devez remarquer à propos des post screen shaders est que l'ordre dans lequel vous les appliquez a une incidence. Voici un exemple simple pour cela. Admettons que vous avez une scène dans laquelle tout est bleu pour une raison quelconque (irréaliste, mais c'est un bon exemple ici). Le canal de rouge et de vert sont toujours à zero. Et disons que vous avez deux post screen shaders. Le premier transforme tout en niveaux de gris en prenant la signification arithmétique des trois canaux, et le second ignore les canaux vert et bleu et donne aux trois canaux la valeur du canal de rouge. Si vous appliquez le post screen shader qui donne la valeur du canal rouge à tout les canaux avant le post screen shader chargé de tout transformer en niveaux de gris, le premier post screen shader donnera une image noire. Si vous utilisez le post screen shader qui donne la valeur du canal rouge à tout les canaux après le post screen shader chargé de tout transformer en niveaux de gris, le premier post screen shader appliqué donnera une version plus sombre du canal bleu aux trois canaux et le second post screen shader appliqué aura quelque données positives à afficher. Ainsi les résultats diffèrents selon l'ordre des effets. Pour cette raison, vous pouvez définir une priorité pour chacun des post screen shaders.

L'utilisation des post screen shaders

Cela peut sembler un peu étrange, mais les post screen shaders sont représentés par des objets Game Maker au même titre que les modèles, les primitifs et les terrains. La raison de ce choix est qu'il rend l'utilisation des post screen shaders très confortable. Vous avez simplement un objet pour chaque effet de post screen shader et lorsque vous en avez besoin d'un, vous créez simplement une instance de l'objet correspondant. Le reste est fait par l'objet automatiquement. Aussi il arrive très souvent que le même post screen shader doit être utilisé plusieurs fois pour atteindre un effet suffisamment fort. Dans ce cas, il vous suffit de créer de multiples instances de l'objet du post screen shader. Et si une room dans Game Maker utilise toujours un post screen shader particulier, il vous suffit de placer une instance de l'objet du post screen shader dans cette room et vous en avez fini. Pas de programmation élaborée.

Donc, la seule chose que vous devez savoir maintenant, c'est comment créer ces objets de post screen shader. C'est très facile. La partie compliquée est d'écrire les shaders, mais si c'est trop difficile pour vous, vous pouvez toujours utiliser des shaders faits par d'autres. La création de post screen shader valides sera abordée plus bas. La création d'objet de post screen shader est similaire à la création de tous les autres objets Ultimate 3D. À la première étape, vous devez définir un ensemble de variables. Ensuite, vous appellez une fonction pour créer le post screen shader. Après quoi, vous pouvez appeller quelques autres fonctions pour définir d'autres options pour le post screen shader. Finalement vous devez mettre un appel au script Destroy() dans l'évènement destroy. La fonction Step() n'est pas requise. Voici une liste des variables qui sont disponibles pour les post screen shaders. Il n'y en a que quatre:  

vertex_shader
Le vertex shader qui appartient à ce post screen shader. Cette variable doit être une chaîne, par exemple "effects/BlurPSS.vsh".

pixel_shader
Le pixel shader qui appartient à ce post screen shader. Cette variable doit être une chaîne, par exemple "effects/BlurPSS.psh". Remarquez que la création d'effets de post screen shader échouera sans afficher la moindre erreur si la version des pixel shaders n'est pas supportée. Pour cette raison vous devez appeller GetSupportedPSVersion() avant de créer le post screen shader, afin d'être sûr que la version des pixel shaders requise est supportée. 

camera_index (optional)
Chaque post screen shader s'applique à l'image rendue par un objet caméra particulier. Si ce n'est pas la caméra par défaut, vous devez définir la variable à l'indice de la caméra en question, qui est la valeur de la variable number de cette caméra (dans la fourchette de 1 à 31, étant donné que 0 se réfère à la caméra par défaut), et non pas l'id de l'objet.

priority (optional)
Comme expliqué plus avant, l'ordre dans lequel les post screen shaders sont appliqué a une incidence. C'est pourquoi vous pouvez utiliser cette variable pour assigner des priorités aux objets de post screen shader. Cela peut être n'importe quelle valeur flottante. Plus haut est la valeur de cette variable, plus tôt est appliqué le post screen shader à chaque frame.

Une fois que vous avez fini de définir toutes ces variables, vous pouvez utiliser la fonction CreatePostScreenShader() pour créer le post screen shader. Maintenant vous vous demandez peut être pourquoi les post screen shaders n'utilisent pas le format de fichier d'effet Ultimate 3D (*.ufx). Parce qu'en fait, cela aurait rendu les choses inutilement compliquées. Etant donné que les post screen shaders ne sont pas appliqués aux objets 3D mais à l'écran tout entier, il n'y pas beaucoup d'informations constantes précalculées qui pourraient être définies pour ces derniers. Il n'y a pas de données à propos des matériaux, les transformations des objets de la scene n'influent pas et les sources de lumières sont déjà appliquées. En général, tout ce dont vous avez besoin, c'est quelques informations sur les couleurs, ou quelques ensembles de facteurs pour changer la force de l'effet. Cela peut être défini soit au travers de l'instruction def d'un shader particulier, ou au travers des deux fonctions suivantes:

Cette fonction définie un registre constant d'un vertex shader particulier pour l'objet de post screen shader par lequel elle est appellée. Elle est à même d'écraser les registres constants définis au travers de l'instruction def des vertex shaders.

SetPSSVSConstant(
VSConstantIndex,
Value1, Value2, Value3, Value4
)

VSConstantIndex
L'indice du registre constant de vertex shader que vous voulez modifier. Cela peut être une valeur arbitraire comprise entre 0 et 95.

Value1, Value2, Value3, Value4
Les valeurs que vous voulez définir pour ces registres constants. Value1 sera copié dans l'élément x, Value2 sera copié dans l'élément y, Value3 sera copié dans l'élément z, Value4 sera copié dans l'élément w.

Cette fonction définit un registre constant de pixel shader particulier pour l'objet par lequel il est appellé. Il n'est pas à même d'écraser les registres constants qui ont été définis au travers de l'instruction def des pixel shaders.

SetPSSPSConstant(
PSConstantIndex,
Value1, Value2, Value3, Value4
)

PSConstantIndex
L'indice du registre constant de pixel shader que vous souhaitez modifier. Cela peut être n'importe quelle valeur entière située entre 0 et 7.

Value1, Value2, Value3, Value4
Les valeurs que vous voulez définir pour ce registre constant.

Cependant les constantes de shader ne sont pas les seules choses que vous pouvez définir pour les post screen shaders. Ils peuvent également avoir une texture définie pour chaque niveau de texture. Seule la texture du premier niveau de texture (niveau 0) ne peut être changée car ce niveau utilise toujours la texture de la scene. Les textures de tous les autres niveaux peuvent être définies à l'aide de la fonction suivante.

Cette fonction définit la texture donnée en tant que texture pour le niveau de texture spécifié de l'objet de post screen shader par lequel elle est appellée:

SetPSSTexture(
TextureStage,
TextureIndex
)

TextureStage
L'indice du niveau pour lequel vous voulez définir une autre texture. Etant donné que le premier niveau de texture utilise toujours la texture de la scène et ne peut être modifié, cela doit être une valeur comprise entre 1 et 7.

TextureIndex
L'indice de la texture que vous voulez définir pour ce niveau, qui est la valeur de retour de la fonction de chargement de texture.

Lors de l'écriture de post screen shaders, il arrive assez souvent que vous puissiez tirer un avantage d'un filtre de texture particulier, afin de donner un meilleur aspect au rendu de vos effets. Par exemple, si vous utilisez un filtrage linéaire pour la texture de la scène, cela la rendra un peu floue, ce qui peut être voulu ou non. Pour cette raison, vous pouvez définir un filtre de texture individuel pour chacun de niveau de texture des post screen shaders. Par défaut, le premier niveau utilise le point de filtrage le plus proche et tous les autres niveaux utilisent le filtre de texture qui a été défini au travers de SetFilter(...), qui est le filtrage linéaire par défaut. Si vous voulez définir d'autre filtre, vous pouvez utiliser la fonction suivante:

Cette fonction définit un filtre de texture différent pour un niveau de texture particulier de l'objet de post screen shader qui appelle cette fonction:

SetPSSFilter(
TextureStage,
FilterType
)

TextureStage
Le niveau de texture pour lequel vous voulez définir un nouveau filtre. Cela peut être n'importe quelle valeur comprise entre 0 et 7.

FilterType
Le filtre que vous voulez définir pour ce niveau. Pour obtenir une description de la signification des différentes valeurs que vous pouvez passer à ce paramètre, veuillez vous réferer à la description de la fonction SetFilter(...).

Ecrire des post screen shaders

Les post screen shaders sont écrits dans le même langage assembleur que les effets de shader habituels. Cependant, il sont généralement plutôt différents. Les vertex shaders en particulier, peuvent différer très fortement. La première raison est qu'aucun calcul de matrice n'a besoin d'être effectué, parce que les registres de position des vertex sont déjà dans l'espace de projection. La deuxième raison est que les rectangles, qui sont utilisés pour appliquer les post screen shaders, ne sont pas fait de plus de quatre vertices. Ainsi chaque vertex shader d'un post screen shader ne calcule pas plus de quatre vertices, ce qui n'est presque rien en comparaison des centaines et des milliers qui doivent être calculés pour les shaders utilisés pour les modèles 3D. C'est pourquoi vous n'avez pas besoin de vous soucier de l'optimisation des post screen vertex shaders. Si dix lignes supplémentaires de code de vertex shader peuvent vous épargner une ligne de code de pixel shader, ou si elles rendent l'utilisation de l'effet de post screen shader un peu plus confortable, vous devriez les utiliser.

La chose la plus importante que vous devez savoir, pour être en mesure d'écrire un post screen shader valide, est les registres de données des vertex disponibles. Ce sont v0, v7 et v8. Voici une description de leur contenu:

v0 contient la position du vertex dans l'espace de projection. Etant donné qu'il est déjà dans l'espace de projet, vous pouvez simplement utiliser une instruction mov pour le passer au registre de sortie oPos. Aucune matrice n'est nécessaire.

v7 contient les coordonnées de texture qui seront utilisées pour la texture de la scène.

v8 contient les coordonnées de textures dans la fourchette allant de 0 à 1, qui peut être utilisée pour toutes les autres textures.

La raison pour laquelle v7 et v8 sont différents, est que les textures doivent toujours avoir des dimensions faites de puissances de deux, alors que les cibles de rendu peuvent avoir une taille arbitraire. Ainsi si vous avez une fenêtre de 800*600, la texture de scène aura les dimensions 1024*1024, ainsi les coordonnées de textures de v7 seront dans la fourchette allant de 0 à 800/1024 pour la coordonnée x, et dans la fourchette allant de 0 à 600/1024 pour la coordonnée y.

Après tant de théorie, voici une petit exemple de post screen shader. Il utilise la carte de distorsion des coordonnées de la texture qui sont supposées être définies pour le niveau 1 afin de déformer l'image à l'écran. Ce petit post screen shader exemple est déjà vraiment puissant. Il peut être utilisé pour des effets de distorsions plutôt cools.

Voici le code du vertex shader:

vs.1.1

mov oPos, v0
mov oT0, v7
mov oT1, v8

On ne peut plus simple, pourrait-on :D ? Voici le code du pixel shader:

ps.1.4

texcrd r0.rgb, t0
texld r1, t1

mad r1.rgb, r1, c0, r0

phase

texld r0, r1

Le pixel shader fait les choses suivantes. D'abord il obtient les premières coordonnées de texture pour ce pixel et la donnée de la carte de distorsion. Ensuite il multiplie la donnée de la carte de distorsion par un facteur et ajoute la première coordonnée de texture du pixel. Pour finir, il utilise cette nouvelle coordonnée de texture afin de chercher un texel dans la texture de la scène. Cela sera l'affichage de ce shader. Pour utiliser ce shader pour devez définir une carte de distorsion pour le niveau 1 et un facteur de distorsion pour la première constante de pixel shader. C'est tout.



© Christoph Peters. Certains droits réservés. (Traduction FR 04/2008, Franck Vernel / Damien Buhl).

Creative Commons License XHTML 1.0 Transitional