<Guide du Rootard GeeXLab/>

Première démo 3D GeeXLab


Dernière mise à jour: 2018.05.31 par JeGX

>> Retour <<


Nous allons aborder notre première démo GeeXLab. Pour cela, nous utiliserons un tout petit framework codé en Lua, appellé Hello!, dont le seul but est de simplifier la programmation GeeXLab tout en conservant l'essentiel, à savoir le contrôle sur le rendu de la scene.

Hello est codé en utilisant l'API Lua de base de GeeXLab. L'API Lua de GeeXLab n'est pas très compliquée à utiliser, mais c'est une API de bas niveau dans le sens où il faut plus d'instructions pour effectuer un tâche particulière par rapport au framework Hello. C'est pour cela qu'il est plus judicieux de commencer par une API de plus haut niveau afin de ne pas être submergé par des détails inutiles.

Pour cette introduction à la programmation GeeXLab, je vous recommende les lectures suivantes:


Voici ce qu'il nous faut pour tester et modifier la démo:


Pour ma part, je travaille sous Windows 10 64-bit et j'utilise Notepad++ comme éditeur de texte.


Avant d'aborder le code de la démo, voyons le contenu du framework Hello:





Le code du framework se trouve dans le fichier hello.lua. La démo, quant à elle, se trouve dans le répertoire demo01/:





Avant d'aller plus loin, lançons notre démo: il suffit de charger dans GeeXLab le fichier main.xml. Vous devriez voir le résultat suivant:





Le point d'entrée de la démo est le fichier main.xml dont le contenu est le suivant:


<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<glsl_hacker>

	<window name="win3d01" title="Hello!" width="700" height="200" />
  
	<script name="init_scene" run_mode="INIT" filename="init.lua" />

	<script name="update_scene" run_mode="FRAME" filename="frame.lua" livecoding="1" />

	<script name="resize_scene" run_mode="SIZE" filename="resize.lua" />
  
	<script name="terminate_scene" run_mode="TERMINATE" filename="terminate.lua" />

</glsl_hacker>


La structure parle d'elle même. GeeXLab va d'abord exécuter le fichier init.lua. Il exécutera ensuite à chaque frame le code contenu dans le fichier frame.lua. Lors d'un redimensionnement de la fenêtre de GeeXLab, le script resize.lua sera exécuté. Puis lorsque l'on quite GeeXLab, le script terminate.lua est exécuté.

Les deux scripts qui nous intéressent ici sont les scripts INIT et FRAME.

Script INIT


local demo_dir = gh_utils.get_demo_dir()
dofile(demo_dir .. "../hello.lua")


hello.init()


ground = hello.object.plane(50, 50)
sphere = hello.object.sphere(5.0)
box = hello.object.box(10.0, 10.0, 10.0)
quad = hello.object.quad(200, 75)


ground_tex = hello.image.load(demo_dir .. "data/Ground_Dirt_1k_d.jpg")
sphere_tex = hello.image.load(demo_dir .. "data/10857.jpg")
box_tex = hello.image.load(demo_dir .. "data/12943-diffuse.jpg")
hello_tex = hello.image.load(demo_dir .. "data/hello2.jpg")


Détaillons un peu ce premier script.

Les deux premières lignes permettent de charger le framework Hello. La première ligne qui initialise la variable demo_dir est présente dans la mojorité des démos.


local demo_dir = gh_utils.get_demo_dir()
dofile(demo_dir .. "../hello.lua")

La ligne suivante initialise le framework Hello:


hello.init()

Les quatre lignes suivantes créent les 4 objets utilisés dans la démo: un plan pour le sol (ground), une sphere, une boite et enfin un quad qui nous servira à afficher le logo Hello.


ground = hello.object.plane(50, 50)
sphere = hello.object.sphere(5.0)
box = hello.object.box(10.0, 10.0, 10.0)
quad = hello.object.quad(200, 75)

Les quatre dernièress lignes chargent 4 images (qui sont en fait des textures dans le jargon de la programmation 3D).


ground_tex = hello.image.load(demo_dir .. "data/Ground_Dirt_1k_d.jpg")
sphere_tex = hello.image.load(demo_dir .. "data/10857.jpg")
box_tex = hello.image.load(demo_dir .. "data/12943-diffuse.jpg")
hello_tex = hello.image.load(demo_dir .. "data/hello2.jpg")

A la fin du script INIT, nous avons donc 4 objects et 4 images. Nous allons maintenant les utiliser pour dessiner la scène 3D dans le script FRAME.

Au lieu de vous montrer le code entier du script FRAME, je vous propose de constuire la scène progressivement.


Script FRAME v1


hello.begin_frame(0.6, 0.6, 0.6)


hello.end_frame()

Le script FRAME v1 génère la scene suivante:





La scene est rendue avec une couleur unie, ici un gris clair. La couleur est définie à l'aide des 3 paramètres de la fonction begin_frame. Ces 3 valeurs représentent une couleur RGB: Red, Green, Blue (rouge, vert et bleu). La valeur de chaque cannal peut varier de 0.0 à 1.0.

Par exemple, le blanc est codé avec 1.0, 1.0, 1.0 et le noir avec 0.0, 0.0, 0.0. Le jaune est donné par: 1.0, 1.0, 0.0.

Si vous avez des couleurs définies avec des valeurs entre 0 et 255, il suffit de diviser chaque cannal par 255.0:


hello.begin_frame(140.0/255.0, 160.0/255.0, 50.0/255.0)

Passons à la suite.


Script FRAME v2


hello.begin_frame(0.6, 0.6, 0.6)

hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

hello.end_frame()

Le script FRAME v2 génère la scene suivante:





Nous venons de dessiner le sol. La fonction draw_3d_with_texture() permet de rendre en 3D un objet avec une texture. Les deux derniers paramètres permettent de gérer le tiling de la texture, c'est à dire combien de fois la texture est répété sur l'axe des X et des Y. Je vous invite à changer les valeurs de ces deux paramètres pour vous faire une idée. Faites un test avec 2.0, 2.0.

Passons à la suite.



Script FRAME v3


hello.begin_frame(0.6, 0.6, 0.6)

hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

hello.object.draw_3d_with_texture(sphere, sphere_tex, 1.0, 1.0)

hello.end_frame()

Le script FRAME v3 génère la scene suivante:





Pas terrible!

En fait si ce résultat est important car nous voyons maintenant la sphère. Mais seulement à moitié. Et si l'on remontait un peu cette sphère le long de l'axe vertical des Y... Oops, je m'aperçois qu'un petit dessin du repère 3D utilisé par GeeXLab serait le bienvenu:





On positionne un objet dans l'espace 3D à l'aide des trois coordonnées suivantes: x, y, z. Pour remonter la sphère, il suffit de modifier la coordonnée y de la position. Ce qui nous donne le script FRAME suivant:

Script FRAME v4


hello.begin_frame(0.6, 0.6, 0.6)

hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

hello.object.set_position(sphere, 0.0, 5.0, 0.0)
hello.object.draw_3d_with_texture(sphere, sphere_tex, 1.0, 1.0)

hello.end_frame()

Le script FRAME v4 dessine la scene suivante:





C'est beaucoup mieux. En utilisant la fonction set_rotation() nous pouvons orienter la sphère autour d'elle même. Le script FRAME v5 nous montre la rotation de la sphère de 90° autour de son axe des X:

Script FRAME v5


hello.begin_frame(0.6, 0.6, 0.6)

hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

hello.object.set_position(sphere, 0.0, 5.0, 0.0)
hello.object.set_rotation(sphere, 90.0, 0.0, 0.0)
hello.object.draw_3d_with_texture(sphere, sphere_tex, 1.0, 1.0)

hello.end_frame()




Accélérons un peu. Nous allons maintenant déplacer la sphère sur la gauche (de -8.0 unités) afin de faire de la place pour dessiner une boîte sur la droite (positionnée en +8.0 unités) :

Script FRAME v6


hello.begin_frame(0.6, 0.6, 0.6)

hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

hello.object.set_position(sphere, -8.0, 5.0, 0.0)
hello.object.set_rotation(sphere, 90.0, 0.0, 0.0)
hello.object.draw_3d_with_texture(sphere, sphere_tex, 2.0, 2.0)

hello.object.set_position(box, 8.0, 5.0, 0.0)
hello.object.draw_3d_with_texture(box, box_tex, 1.0, 1.0)

hello.end_frame()




Bravo, nous avons notre première scène 3D temps réel.

GeeXLab fonctionne en mode immédiat. Chaque instruction a un effet immédiat sur le rendu de la scène. C'est vous, en tant que développeur GeeXLab, qui avez le contrôle sur ce qui doit ou ne doit pas être rendu. GeeXLab ne prend aucune initiative. Il fait ce que vous lui dites de faire.

Affichons maintenant le logo du framework Hello dans la 7ème version du script FRAME:


Script FRAME v7


hello.begin_frame(0.6, 0.6, 0.6)

hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

hello.object.set_position(sphere, -8.0, 5.0, 0.0)
hello.object.set_rotation(sphere, 90.0, 0.0, 0.0)
hello.object.draw_3d_with_texture(sphere, sphere_tex, 2.0, 2.0)

hello.object.set_position(box, 8.0, 5.0, 0.0)
hello.object.draw_3d_with_texture(box, box_tex, 1.0, 1.0)


hello.object.draw_2d_with_texture(quad, hello_tex, 1.0, 1.0)


hello.end_frame()




A la différence des objets précédents qui étaient dessinés en 3D, le logo est un objet dessiné en 2D. C'est un élément d'interface. Voilà le repère 2D utilisé par les objets de GeeXLab:





Par defaut, le quad qui sert à afficher le logo est positionné en 0 ; 0. C'est pour cela qu'il apparaît au centre de l'écran. Le code suivant permet de le dessiner dans le cadran supérieur gauche de l'écran:

Script FRAME v8


hello.begin_frame(0.6, 0.6, 0.6)

hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

hello.object.set_position(sphere, -8.0, 5.0, 0.0)
hello.object.set_rotation(sphere, 90.0, 0.0, 0.0)
hello.object.draw_3d_with_texture(sphere, sphere_tex, 2.0, 2.0)

hello.object.set_position(box, 8.0, 5.0, 0.0)
hello.object.draw_3d_with_texture(box, box_tex, 1.0, 1.0)


w, h = hello.window.size()
hello.object.set_position(quad, -w/2 + 90.0, h/2.0 - 140, 0.0)
hello.object.draw_2d_with_texture(quad, hello_tex, 1.0, 1.0)


hello.end_frame()




La fonction hello.window.size() nous donne les dimensions de la fenêtre 3D. Nous sommes en 2D et le dernier paramètre de la fonction hello.object.set_position() est inutile et peut être mis à zéro.

Le framwork Hello dispose de quelques fonctions permettant d'écrire du texte à l'écran: hello.print_xxxx(). Dans un soucis de simplicité, le positionnement du texte est automatique. Le script FRAME v9 nous montre l'utilisation des fonctions de texte:

Script FRAME v9


hello.begin_frame(0.6, 0.6, 0.6)

hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

hello.object.set_position(sphere, -8.0, 5.0, 0.0)
hello.object.set_rotation(sphere, 90.0, 0.0, 0.0)
hello.object.draw_3d_with_texture(sphere, sphere_tex, 2.0, 2.0)

hello.object.set_position(box, 8.0, 5.0, 0.0)
hello.object.draw_3d_with_texture(box, box_tex, 1.0, 1.0)


w, h = hello.window.size()
hello.object.set_position(quad, -w/2 + 90.0, h/2.0 - 140, 0.0)
hello.object.draw_2d_with_texture(quad, hello_tex, 1.0, 1.0)


hello.print_lib_info()
hello.print_fps()
hello.print("")
hello.print_rgb("Simple Lua API for 3D programming", 0.5, 0.5, 0.5)


hello.end_frame()




Il reste encore un dernière chose à améliorer: le rendu du logo. Il est assez moche. Si on pouvait éliminer le fond blanc du logo...

Il existe une technique toute simple pour y arriver: le color blending. C'est une fonctionnalité bas niveau des API 3D (OpenGL, Direct3D et Vulkan). Le color blending permet de mélanger la couleur de l'objet qui est en train d'être rendu avec la couleur déjà présente dans le framebuffer (le framebuffer est l'endroit dans la mémoire de la carte graphique où sont concrétement dessinés les objets 2D/3D). Je ne vais pas entrer en détail dans le color blending mais le voilà en action dans la dernière version du script FRAME avec les fonctions hello.graphics.blending_xxxxx():

Script FRAME v10


------------------------------------------------
-- Debut de la frame
--
hello.begin_frame(0.6, 0.6, 0.6)

------------------------------------------------
-- Rendu du sol
--
hello.object.draw_3d_with_texture(ground, ground_tex, 1.0, 1.0)

------------------------------------------------
-- Rendu de la sphere sur la gauche
--
hello.object.set_position(sphere, -8.0, 5.0, 0.0)
hello.object.set_rotation(sphere, 90.0, 0.0, 0.0)
hello.object.draw_3d_with_texture(sphere, sphere_tex, 2.0, 2.0)



------------------------------------------------
-- Rendu de la boite sur la droite
--
hello.object.set_position(box, 8.0, 5.0, 0.0)
hello.object.draw_3d_with_texture(box, box_tex, 1.0, 1.0)



------------------------------------------------
-- Activation du color blending
--
hello.graphics.blending.enable(1)
hello.graphics.blending.factors(9, 8)

-- Rendu du logo
--
w, h = hello.window.size()
hello.object.set_position(quad, -w/2 + 90.0, h/2.0 - 140, 0.0)
hello.object.draw_2d_with_texture(quad, hello_tex, 1.0, 1.0)

-- Désactivation du color blending
--
hello.graphics.blending.enable(0)
------------------------------------------------


------------------------------------------------
-- Rendu des informations
--
hello.print_lib_info()
hello.print_fps()
hello.print("")
hello.print_rgb("Simple Lua API for 3D programming", 0.5, 0.5, 0.5)


------------------------------------------------
-- Fin de la frame
--
hello.end_frame()




L'air de rien, nous avons fait le tour de quelques fonctionalités de base de la programmation d'une scene 3D: positionnement et rotation d'objets 2D et 3D, affichage d'objets 2D et 3D texturés et utilisation du color blending.

Si vous êtes curieux, allez regarder le contenu du fichier hello.lua. Certaines choses seront encore obscures comme les shaders mais d'autres seront parfaitement claires.

Les shaders (ou mieux les programmes GPU) sont la base du rendu 3D moderne. Le framework Hello les utilise pour dessiner les objets. Mais pour une prise en main de GeeXLab, les programmes GPU peuvent être cachés. Le programme GPU utilisé par Hello est amplement suffisant pour beaucoup de situations. Les programmes GPU feront l'objet d'une section à part entière.

Live-Coding

Dernière chose: cette démo peut etre live-codée. Cela veut tout simplement dire qu'il est possible de modifier le script FRAME, de le sauvegarder et de voir immédiatement le résultat sans quitter GeeXLab.

Le live-coding du script FRAME est activé en mettant livecoding="1" au niveau du script dans le fichier XML principal.

Quand je live-code, je mets GeeXLab sur la gauche du bureau Windows et je mets Notepad++ sur la droite. De cette manière je peux rester tout le temps dans Notepad++ et faire toutes les modifications que je souhaite.









Guide du Rootard GeeXLab | Téléchargements | Contact | Newsletter