Post-traitement personnalisé¶
Introduction¶
Godot fournit de nombreux effets de post-traitement prêts à l'emploi, notamment Bloom, DOF et SSAO. Parfois, vous voulez écrire votre propre effet personnalisé. Voici comment vous pouvez le faire.
Les effets de post-traitement sont des shaders appliqués à une image après que Godot l'ait rendue. Vous voulez d'abord rendre votre scène dans un Viewport, puis rendre le Viewport
dans un ViewportTexture et le montrer à l'écran.
La façon la plus simple de mettre en œuvre un shader de post-traitement personnalisé est d'utiliser la capacité intégrée de Godot à lire la texture de l'écran. Si vous n'êtes pas familier avec cela, vous devriez lire le Screen Reading Shaders Tutorial d'abord.
Note
Au moment où nous écrivons ces lignes, Godot ne supporte pas le rendu vers plusieurs tampons en même temps. Votre shader de post-traitement n'aura pas accès aux normales ou aux autres passes de rendu. Vous n'avez accès qu'à la trame rendue.
Monopasse post-traitement¶
Vous aurez besoin d'un Viewport
pour rendre votre scène, et d'une scène pour rendre votre Viewport
à l'écran. Vous pouvez utiliser un ViewportContainer pour afficher votre Viewport
sur l'écran entier ou à l'intérieur d'un autre Control nœud.
Note
Le rendu à l'aide d'un Viewport
vous donne le contrôle sur la façon dont la scène est rendue, y compris la fréquence d'images, et vous pouvez utiliser le ViewportContainer
pour rendre des objets 3D dans une scène 2D.
Pour cette démo, nous allons utiliser un Node2D avec un ViewportContainer
et enfin un Viewport
. Votre onglet Scene devrait ressembler à ça :
A l'intérieur du Viewport
, vous pouvez avoir tout ce que vous voulez. Ceci contiendra votre scène principale. Pour ce tutoriel, nous utiliserons un champ de boites aléatoires :
Ajouter un nouveau ShaderMaterial au ViewportContainer
, et lui assigner une nouvelle ressource de shader. Vous pouvez accéder à votre Viewport
rendu avec la TEXTURE
uniforme intégré.
Note
Vous pouvez choisir de ne pas utiliser un ViewportContainer
, mais si vous le faites, vous devrez créer votre propre uniforme dans le shader et passer la texture Viewport
manuellement, comme ça :
// Inside the Shader.
uniform sampler2D ViewportTexture;
Et vous pouvez passer la texture dans le shader depuis GDScript comme ça :
# In GDScript.
func _ready():
$Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture())
Copiez le code suivant sur votre shader. Le code ci-dessus est un filtre de détection de bord à passe unique, un Filtre Sobel.
shader_type canvas_item;
void fragment() {
vec3 col = -8.0 * texture(TEXTURE, UV).xyz;
col += texture(TEXTURE, UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
col += texture(TEXTURE, UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz;
col += texture(TEXTURE, UV + SCREEN_PIXEL_SIZE.xy).xyz;
col += texture(TEXTURE, UV - SCREEN_PIXEL_SIZE.xy).xyz;
col += texture(TEXTURE, UV + vec2(-SCREEN_PIXEL_SIZE.x, SCREEN_PIXEL_SIZE.y)).xyz;
col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, -SCREEN_PIXEL_SIZE.y)).xyz;
COLOR.xyz = col;
}
Note
Le filtre Sobel lit les pixels dans une grille 9x9 autour du pixel courant et les additionne en utilisant le poids. Ce qui le rend intéressant, c'est qu'il attribue des poids à chaque pixel ; +1 pour chacun des huit autour du centre et -8 pour le pixel central. Le choix des poids est appelé "kernel". Vous pouvez utiliser différents kernels pour créer des filtres de détection de bords, de contours et toutes sortes d'effets.
Post-traitement multi-passe¶
Certains effets de post-traitement comme le flou sont gourmands en ressources. Cependant, si vous les décomposez en plusieurs passes, vous pouvez les faire éxecuter beaucoup plus vite. Dans un materiel en plusieurs passes, chaque passe prend le résultat de la passe précédente comme entrée et le traite.
Pour faire un shader de post-traitement multi-passes, vous empilez des nœuds Viewport
. Dans l'exemple ci-dessus, vous avez rendu le contenu d'un objet Viewport
dans la racine Viewport
, par l'intermédiaire d'un nœud ViewportContainer
. Vous pouvez faire la même chose pour un shader multi-passes en rendant le contenu d'un Viewport
dans un autre et ensuite en rendant ce dernier Viewport
dans le Viewport
racine.
Votre hiérarchie de scène devrait ressembler à cela :
Godot rendra le nœud Viewport
du bas en premier. Donc si l'ordre des passes est important pour vos shaders, assurez-vous d'assigner le shader que vous voulez appliquer en premier au ViewportContainer
le plus bas dans l'arbre.
Note
Vous pouvez également rendre vos Viewports séparément sans les imbriquer comme ceci. Il suffit d'utiliser deux Viewports et de les rendre l'un après l'autre.
En dehors de la structure des nœuds, les étapes sont les mêmes que pour le shader de post-traitement à un seul passage.
Par exemple, vous pouvez écrire un effet de flou gaussien plein écran en attachant les morceaux de code suivants à chacun des ViewportContainers. L'ordre dans lequel vous appliquez les shaders n'a pas d'importance :
shader_type canvas_item;
// Blurs the screen in the X-direction.
void fragment() {
vec3 col = texture(TEXTURE, UV).xyz * 0.16;
col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(TEXTURE, UV + vec2(-SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.15;
col += texture(TEXTURE, UV + vec2(2.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(TEXTURE, UV + vec2(2.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.12;
col += texture(TEXTURE, UV + vec2(3.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(TEXTURE, UV + vec2(3.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.09;
col += texture(TEXTURE, UV + vec2(4.0 * SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
col += texture(TEXTURE, UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
COLOR.xyz = col;
}
shader_type canvas_item;
// Blurs the screen in the Y-direction.
void fragment() {
vec3 col = texture(TEXTURE, UV).xyz * 0.16;
col += texture(TEXTURE, UV + vec2(0.0, SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(TEXTURE, UV + vec2(0.0, -SCREEN_PIXEL_SIZE.y)).xyz * 0.15;
col += texture(TEXTURE, UV + vec2(0.0, 2.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(TEXTURE, UV + vec2(0.0, 2.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.12;
col += texture(TEXTURE, UV + vec2(0.0, 3.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(TEXTURE, UV + vec2(0.0, 3.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.09;
col += texture(TEXTURE, UV + vec2(0.0, 4.0 * SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
col += texture(TEXTURE, UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
COLOR.xyz = col;
}
En utilisant le code ci-dessus, vous devriez vous retrouver avec un effet de flou plein écran comme ci-dessous.
Pour plus d'informations sur le fonctionnement des nœuds Viewport
, voir le Tutoriel sur les Viewports.