XXXIV. Ignorer les objets hors champ▲
Le terme culling signifie la suppression ciblée de l'affichage de certains éléments graphiques dont on est certain qu'ils ne sont pas visibles.
Deux techniques simples sont toujours actives dans le BGE pour cela.
-
frustrum culling. Les objets qui sont entièrement hors du champ de la caméra ne sont pas envoyés au processeur graphique.
- back face culling. Les faces dont les normales ne sont pas dirigées vers la caméra ne sont pas rendues par le processeur graphique (mais elles sont néanmoins envoyées à la carte).
Pour être efficaces, nous devons choisir comme occluders des objets de grande taille susceptibles de cacher beaucoup d'autres objets. Un choix idéal serait les murs et la toiture d'un bâtiment qui cacheraient tous les objets situés à l'intérieur lorsqu'on se trouve à l'extérieur et vice et versa.
Les occluders sont des objets marqués comme tels dans le panneau Physics (Properties > Physics > Physics Type > Occlude). Ce choix désactive toute autre option physique, car les occluders sont d'office des objets sans physique. Pour cette raison les occluders doivent, de préférence, être construits spécialement à cet effet. Par exemple, dans le cas du bâtiment cités plus haut, plutôt que de choisir le mesh du bâtiment, nous créerons un volume simple qui épouse la forme du bâtiment, mais qui est situé au sein des murs de sorte que le bâtiment lui-même soit à la fois à l'intérieur et à l'extérieur de l'occluder, mais que les autres objets soient dedans ou dehors.
Cette technique est nettement plus sophistiquée que les autres méthodes de culling, car le BGE doit faire une sorte de rendu simplifié de la scène pour voir quels objets sont cachés. Pour cette raison il faut faire des tests pour évaluer l'intérêt de la méthode au cas par cas.
L'Occlusion Culling est activée dans le panneau Properties > World > Physics. L'option Resolution spécifie la résolution du rendu simplifié. La valeur par défaut (128) est sans doute adéquate pour la plupart des cas. Une valeur trop basse diminue les calculs, mais augmente les marges ce qui réduit le nombre d'objets occultés. Une valeur trop élevée augmente inutilement le calcul, sans gain supplémentaire d'occultation.
Au minimum un occluder doit être activé dans la scène pour activer cette fonctionnalité.
XXXV. Précalcul des textures et de la lumière▲
Le baking, ou cuisson des textures, est une méthode permettant de simplifier le calcul des textures et des lumières par le moteur de rendu. Cette technique fige la surface des objets telle qu'elle est rendue par le moteur non temps réel de Blender (Internal ou Cycles) et en fabrique une texture. Par exemple, cela permet de diminuer le nombre de lumières dynamiques nécessaires pour illuminer un espace, simplifier la texture ou la géométrie d'un personnage détaillé. De plus, depuis la version 2.71, le baking est capable d'utiliser le rendu de Cycles, ce qui permet un mode d'illumination des scènes très réaliste, et donne à des scènes simples un rendu de grande qualité, impossible à obtenir avec le rendu classique du Game Engine.
Le principe du baking d'un objet commence par le texturage et l'éclairage de la scène en mode rendu Cycle. La cuisson se réalise sur une texture mapée en UV. Ensuite cette texture est utilisée à la place du matériau original. L'éclairage de la scène ainsi que le texturage des objets se réalise avec les précisions souhaitées. Nous allons voir ici comment faire pour l'illumination d'un espace.
Preview d'un rendu Cycle d'une scène comportant 6 lampes de type area, ainsi qu'une sphère dont le matériau émet de la lumière.
XXXV-A. Préparation de la scène▲
La scène doit être installée comme dans un rendu classique. Lorsque l'effet d'éclairage désiré est obtenu, le baking se réalise en ajoutant une carte UV dédiée et assignée à une nouvelle texture.
Ajoutons la texture dans son matériau (fenêtre nodes matériau) pour le baking.
XXXV-B. Baking▲
Sélectionnons notre objet puis dans la fenêtre des nodes de son matériau, sélectionnons la texture qui doit être remplie.
Dans l'onglet Rendu, section Baking, enclenchons le bouton Bake. Il faut être patient, car le rendu n'est pas visible et peut prendre un certain temps. Une fois terminée, la texture prend les couleurs de l'éclairage complexe. Enregistrons-la pour ne pas la perdre.
XXXV-C. Dans le Game Engine▲
Repassons en mode Game Engine et modifions le matériau dans la fenêtre node, en lui ajoutant un input matériau, ainsi qu'un node output.
Enfin, relions la texture du baking à l'entrée couleur de la texture. Le résultat est maintenant visible dans le BGE.
XXXV-D. Autres possibilités▲
Il est également possible de baker des textures procédurales non supportées par le Game Engine.
Dans le node editor, une texture générée par association de différents patterns grâce aux nodes mais non supportée dans le Game Engine est transformée en image bitmap utilisée dans le processus de baking.
Un autre usage est de simplifier la géométrie d'un objet en faisantle baking des reliefs dans une normal map qui sera utilisée sur un modèle de moindre détail. La surface aura l'air d'être détaillée alors qu'elle ne comporte que peu de faces. Ceci est très utile pour les personnages par exemple.
XXXVI. Programmer une surcouche graphique▲
Les hooks (crochets en français) font partie des techniques avancées d'affichage. Ils permettent d'exécuter du code à chaque frame alors que le contexte OpenGL est prêt pour l'affichage. Cela signifie qu'il est possible d'ajouter des éléments ou des effets graphiques dans la fenêtre de jeu par voie de programmation sans passer par le moteur graphique du jeu. Les possibilités sont vastes, mais le prix à payer est qu'il faut passer par OpenGL. Voyons plus en détail comment cela se passe.
XXXVI-A. Anatomie d'un hook▲
Un hook est une fonction Python que nous devons écrire et dans laquelle nous mettrons du code OpenGL, soit directement grâce au module bgl de Blender qui exporte la plupart des fonctions, soit indirectement via notre API Python préférée pour générer du graphisme OpenGL.
Nous devons enregistrer cette fonction auprès de la scène courante :
- Fonctions à exécuter avant le rendu des objets.
scene.pre_draw.append
(
my_function)
- Fonctions à exécuter après le rendu des objets.
scene.post_draw.append
(
my_function)
Les attributs pre_draw et post_draw sont des listes pour permettre à différentes parties du code d'enregistrer leurs fonctions. Pour supprimer une fonction hook existante, il suffit d'utiliser la méthode remove : scene.post_draw.remove(my_function)
Une fois enregistrée auprès de la scène, la fonction sera appelée par le moteur graphique à chaque frame avec les modalités suivantes.
- Pas d'argument.
La fonction est appelée sans argument. Cependant, comme elle est appelée dans le cadre de la scène où elle a été enregistrée, elle a accès à la scène de manière habituelle (via la fonction logic.getCurrentScene()) et si nécessaire à tous les objets qu'elle contient. - Fonctions pre_draw : matrice chargée, frame buffer vide.
Le contexte OpenGL est prêt pour des commandes d'affichage, le frame buffer est vide et les matrices OpenGL actives sont telles que les vertices que nous enverrons à OpenGL devront être en coordonnées world. Nous avons bien sûr la possibilité de charger de nouvelles matrices, mais nous devrons impérativement rétablir l'état OpenGL avant le return. Tout ce que nous mettrons dans le frame buffer sera combiné avec le rendu des objets. - Fonctions post_draw: matrice indéterminée, frame buffer rempli.
Les fonctions post_draw héritent d'un frame buffer contenant le rendu de tous les objets de la scène plus le résultat des éventuels filtres 2D. Elles ne peuvent compter que sur des valeurs particulières de matrices OpenGL, elles devront donc positionner les matrices selon leurs besoins sans nécessité de rétablir l'état OpenGL avant le return. -
En cas deviewport
Les fonctions hook sont compatibles avec les viewports dans les limites suivantes :- Les fonctions pre_draw sont exécutées pour chaque viewport. Elles héritent du contexte OpenGL du viewport courant. Cela permet d'agir dans chaque viewport. Cependant, une fonction qui ne devrait écrire que dans un viewport devrait d'abord tester le viewport avec la fonction OpenGL appropriée (il n'existe aucun moyen de connaître le viewport courant avec l'API du BGE).
- Les fonctions post_draw ne sont exécutées qu'une seule fois par frame à la fin du rendu de tous les viewports. Elles héritent de la totalité de la fenêtre OpenGL.
XXXVI-A-1. Exemple : dessiner un rectangle en OpenGL.▲
Nous allons afficher une sorte de barre rouge et semi-transparente à l'écran. Cette routine doit s'effectuer après le rendu de l'image. Pour être sûr que la routine s'effectuera bien après le rendu de l'image la fonction write() s'enregistre dans la liste post_draw contenue dans l'objet scene récupéré juste avant. post_draw est une liste de fonctions (plus exactement post_draw est une liste de callable) qui seront exécutées les unes après les autres, et cela après chaque rendu.
from
bge import
logic, render
import
bgl
def
init
(
):
scene =
logic.getCurrentScene
(
)
scene.post_draw =
[write]
def
write
(
):
""" Write on screen """
scene =
logic.getCurrentScene
(
)
width =
render.getWindowWidth
(
)
height =
render.getWindowHeight
(
)
# OpenGL setup
bgl.glMatrixMode
(
bgl.GL_PROJECTION)
bgl.glLoadIdentity
(
)
bgl.gluOrtho2D
(
0
, width, 0
, height)
bgl.glMatrixMode
(
bgl.GL_MODELVIEW)
bgl.glLoadIdentity
(
)
# Draw a 2D rectangle to make the fonts stand out
bgl.glEnable
(
bgl.GL_BLEND)# Enable alpha blending
bgl.glBlendFunc
(
bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
view_buf =
bgl.Buffer
(
bgl.GL_INT, 4
)
bgl.glGetIntegerv
(
bgl.GL_VIEWPORT, view_buf)
view =
view_buf
bgl.glMatrixMode
(
bgl.GL_PROJECTION)
bgl.glLoadIdentity
(
)
bgl.gluOrtho2D
(
0
, view[2
], 0
, view[3
])
bgl.glMatrixMode
(
bgl.GL_MODELVIEW)
bgl.glLoadIdentity
(
)
bgl.glBegin
(
bgl.GL_QUADS)
bgl.glColor4f
(
.4
, 0
, 0
, 0.4
)
bgl.glVertex2f
(
5
, (
height/
2
))
bgl.glVertex2f
(
width -
5
, (
height/
2
))
bgl.glVertex2f
(
width -
5
, (
height/
2
) +
21
)
bgl.glVertex2f
(
5
, int(
height/
2
) +
21
)
bgl.glEnd
(
)
def
main
(
cont):
own =
cont.owner
if
not
"init"
in
own:
own["init"
] =
True
init
(
)
Ressource : OpenGL_postdraw.blend
Il est aussi possible d'afficher du texte de la même façon, via le module lf.