Sites


Blender pour le jeu video

Matériaux dynamiques et textures animées

Dans ce chapitre, nous allons voir trois méthodes pour faire varier les matériaux et textures dans le temps. Ces trois méthodes sont très différentes les unes des autres, mais sont regroupées ici parce qu'elles concernent la modification en direct de l'apparence de nos objets.

Nous verrons premièrement une technique bien connue des créateurs de jeux 2D, c'est l'utilisation de sprites pour rendre des effets d'animation. La deuxième passe par un processus de remplacement, à la volée, dans la carte graphique elle-même, des images attachées aux textures. Et la dernière méthode concerne la fabrication d'un matériau qui se modifie en fonction d'un évènement particulier activé en cours de jeu.

en-tête representant le concept d'utiliser une video sur un objet 3D.

Textures animées par sprites

L'effet de transparence est souvent utilisé avec les sprites animés. Il s'agit de réduire  les ressources en utilisant une seule image regroupant les quelques frames d'une animation courte.

Il vous faut considérer l'image comme une texture normale au niveau du matériau.

Le réglage de l'animation se fait dans l'UV/Image Editor.

Texturez un simple plan avec l'image correspondant à collection des étapes de l'animation.

Elle doit être formée de tuiles de taille identique. Chaque tuile correspondant à une frame de l'animation.
Affichez l'image dans l'UV/Image Editor.

Les UVs de la face du plan doivent recouvrir l'ensemble de l'image.

Faire un reset des UVs est un moyen simple de s'en assurer.

Affichez le panneau Game Properties dans la colonne des propriétés de l'image. Afin de recouper le nombre et la disposition des tuiles, dans la colonne des propriétés de l'image, il faut définir un nombre de colonnes avec la valeur x et de lignes avec la valeur y de l'encart Tiles.
Cochez le Clamp en x et en y selon l'aspect de votre image mais laissez l'option Tiles désactivée pour l'animation. Pour indiquer la tuile de départ et celle de la fin de l'animation, précisez les valeurs Start et End de l'encart Animated. Les tuiles sont indexées de gauche à droite, de haut en bas. Pour que l'animation se lance dans le Game Engine, cochez la case Animated du panneau Game Properties. L'animation tourne en boucle lorsque vous pressez la touche P. À ce stade, il reste à adapter la vitesse de défilement des tuiles lorsque vous lancez le jeu. Pour cela, le plus simple est de faire des aller-retours successifs entre le lancement du jeu et modification de la valeur Speed.

Textures animées par remplacement

La technique présentée dans cette partie est très différente. Nous allons remplacer une texture dans la carte graphique par une image ou un flux d'image de provenance variée : fichier image, fichier vidéo, webcam, bloc mémoire ou un mélange de ce qui précède. Le terme fichier est à prendre au sens large : il peut s'agit d'un fichier ou d'un flux vidéo accessible sur internet. Il suffit pour cela de spécifier une URL plutôt qu'un nom de fichier.

En plus nous pouvons appliquer un effet sur l'image avant qu'elle ne soit envoyée au GPU : incrustation vidéo de type écran vert, gamme de gris, normal map. Cette technique avancée n'est accessible que par Python via le module bge.texture. La magie derrière ce module est la librairie libre FFmpeg. La plupart des formats et des codecs supportés par FFmpeg sont utilisables dans ce module. En voici une liste non exhaustive :

  • AVI
  • Ogg
  • Xvid
  • Theora
  • dv1394 camera
  • carte d'acquisition compatible avec video4linux (cela inclut de nombreuses webcams)
  • jpg
  • flux RTSP

Le principe est simple. Nous identifions une texture sur un objet existant en utilisant la fonction bge.texture.materialID. Ensuite nous créons un objet texture dont le contenu sera modifié dynamiquement suivant l'une ou l'autre méthode évoquée plus haut et nous échangeons les deux textures dans le GPU. Le moteur graphique du BGE n'est pas averti de la substitution et continue à afficher les objets comme d'habitude, sauf que nous avons maintenant le contrôle de la texture. Quand l'objet texture est détruit, l'ancienne texture est restaurée.

Image fixe

Ce script Python change la texture d'un objet par une image fixe.

import bge 
from bge import texture
from bge import logic

def createTexture(cont):
obj = cont.owner

# Obtention de l'index de la texture initiale
ID = texture.materialID(obj, '
IMoriginal.png')

# Creation de la nouvelle texture
object_texture = texture.Texture(obj, ID)

# il faut garder une reference permanente
obj.attrDict["tex"] = object_texture

# chemin complet vers le fichier image
url = logic.expandPath("//newtex.png")

# chargeons la texture en memoire
new_source = texture.ImageFFmpeg(url)

# swap de texture
object_texture.source = new_source

# remplacement dans le GPU
object_texture.refresh(False)
def removeTexture(cont): obj = cont.owner try: del obj.attrDict["tex"] except: pass

Voyons ligne par ligne le fonctionnement de ces fonctions.

import bge 
from bge import logic
from bge import texture

Ces 3 lignes nous permettent d'accéder aux modules bge.logic et bge.texture par leurs noms. Nous avons besoin de texture pour créer la texture animée et de logic pour des fonctions d'intendance.

def createTexture(cont):     
obj = cont.owner

La fonction createTexture sera appelée par un controller Python, l'argument est donc la référence du controller. Nous avons besoin de l'objet car c'est lui qui possède la texture que nous voulons modifier.

 # Obtention de l'index de la texture initiale     
ID = texture.materialID(obj, 'IMoriginal.png')

Nous supposons dans cet exemple que l'objet possède un matériel qui utilise une texture original.png. Le préfixe IM signifie que nous cherchons une image et pas un matériel. Nous pouvons directement rechercher un matériel par son nom en utilisant le prefixe 'MA'.

 # Creation de la nouvelle texture     
object_texture = texture.Texture(obj, ID)

Une fois le matériel identifié nous créons un objet Texture en spécifiant l'identifiant de matériel trouvé précédemment. Le premier canal de texture du matériel sera automatiquement remplacé par cette nouvelle texture.

 # il faut garder une reference permanente     
obj.attrDict["tex"] = object_texture

L'objet texture doit impérativement rester en mémoire tant que nous voulons utiliser la nouvelle mémoire, sinon le garbage collector de Python supprimera l'objet dès la fin de la fonction et la texture sera supprimée. La méthode que nous utilisons ici est de conserver la référence l'objet logic en l'ajoutant au dictionnaire attrDict de l'objet qui persiste tant que l'objet n'est pas détruit (ce qui se produit au plus tard à la fin du jeu). Nous aurions pu sauver la référence dans le dictionnaire global de l'objet logic (logic.globalDict) pour le conserver tout au long du jeu même si l'objet est détruit.

 # chemin complet vers le fichier image     
url = logic.expandPath("//newtexture.jpg")

Nous avons besoin du chemin complet de la texture. Cette ligne suppose qu'une image 'newtexture.jpg' est présente dans le répertoire du fichier blend. Les '//' sont remplacés par le chemin complet du blend.

 # chargeons la texture memoire     
new_source = texture.ImageFFmpeg(url)

La fonction ImageFFmpeg() du module bge.texture permet de charger une image fixe. Nous utiliserions une autre fonction pour charger une vidéo ou pour créer un bloc mémoire.

 # swap de texture     
object_texture.source = new_source

Jusqu'à présent, notre texture était vide. En lui donnant une source, on lui donne du contenu, ici notre image préalablement chargée.

 # remplacement dans le GPU     
object_texture.refresh(False)

Le chargement de la nouvelle texture dans le GPU se fait par la fonction refresh. L'argument de refresh est un booléen qui indique si la texture doit être recalculée (et donc ré-envoyée au GPU) au prochain refresh. Dans le cas d'une image fixe, ce n'est pas nécessaire mais pour une vidéo, ce serait indispensable, de même qu'il serait indispensable d'appeler refresh très fréquemment pour que les différentes images de la vidéo soit extraites et envoyées au GPU (tout se passe dans refresh). Pour utiliser ce script nous aurons besoin de deux briques controllers qui appellent la fonction createTextureau début du jeu pour effectuer le remplacement. Et une à la fin qui appelle la fonction removeTexture pour restaurer la texture.

La fonction removeTexture est optionnelle car la  texture sera d'office restaurée à la fin du jeu grâce au fait que nous utilisons le dictionnaire de l'objet pour sauver la référence: ce dictionnaire est effacé à la fin du jeu ou dès que l'objet est détruit.

Comme vous le voyez sur la capture d'écran, nous avons choisi la méthode Module et non pas la méthode Script pour ajouter notre fonctionnalité python. Cela implique donc que nous devions utiliser la syntaxe NomModule.NomFonction pour indiquer à Blender où se trouve le code python à utiliser.

Image vidéo

Dans cet exemple que nous ne détaillerons pas autant, nous remplaçons la texture d'un objet par une vidéo, ici une vidéo en ligne.

import bge 
from bge import texture
from bge import logic

def createTexture(cont):
 obj = cont.owner

 # Obtention de l'index de la texture initiale
ID = texture.materialID(obj, 'IMoriginal.png')

 # Creation de la nouvelle texture
 object_texture = texture.Texture(obj, ID)

 # il faut garder une reference permanente
 obj.attrDict["tex"] = object_texture

 # chargeons la texture en memoire
 new_source = texture.VideoFFmpeg("http://mirrorblender.top-ix.org/movies/sintel-1024-surround.mp4")

# swap de texture
 object_texture.source = new_source

 # demarrons la video new_source.play()
 # remplacement dans le GPU object_texture.refresh(True)

 def runTexture(cont): obj = cont.owner if "tex" in obj: obj.attrDict["tex"].refresh(True) print("1") def removeTexture(cont): obj = cont.owner try: del obj.attrDict["tex"] except: pass
 

 Ce script se distingue du précédent par deux points :
1. L'objet source est de type VideoFFmpeg au lieu de ImageFFmpeg (si nous avions utilisé ImageFFmpeg nous n'aurions obtenu que la première image du film). Cet objet a une API adaptée à la lecture de fichier vidéo (play, pause, stop). Ici nous utilisons play() pour lancer la vidéo.

2. Nous devons rafraîchir la texture fréquemment pour que les images du film soient successivement envoyées au GPU. Pour cela, la fonction runTexture doit étre appelée fréquemment. Nous utiliserons pour cela un sensor Always en mode tick:

 

Matériaux dynamiques avec les nodes

Utiliser les nodes ouvre la porte à une multitude d'effets poussés basés sur des paramètres d'entrée (inputs) qui seraient normalement inaccessibles. Comme exemple, fabriquons un matériau qui devient transparent s'il est observé de près : cela peut servir par exemple à voir à travers une partie du décor si la caméra se rapproche trop. En partant du material basique vu sous forme de node que nous avons préparé précédemment, il n'y a que très peu de changements à effectuer.

Pour commencer, il faut activer la transparence du materialen cochant l'option Transparency dans l'onglet Render Pipeline Options du material.

Ensuite, dans leNode Editor, il suffit de jouer avec le paramètreAlpha du node Output. Dans notre cas, nous devons relier cet Alpha à la distance de la caméra, disponible via le node Camera Data, en exploitant l'entrée View Distance. Lorsque cette distance atteint ou dépasse 1, l'alphaprendra sa valeur maximale (opaque). Pour un réglage plus fin, insérer un node Mathen mode Multiply permet de modifier cette distance critique. L'option Clamp est une bonne pratique pour limiter les valeurs de sortie entre 0 et 1, puisque l'alpha va ignorer des valeurs extérieures à cet intervalle.

Il y a une erreur de communication avec le serveur Booktype. Nous ne savons pas actuellement où est le problème.

Vous devriez rafraîchir la page.