GeeXLab
Current version: 0.20.x
>GeeXLab homepage

FurMark
Current version: 1.20.0
>FurMark homepage

GPU Caps Viewer
Current version: 1.38.0.0
>GPU Caps Viewer homepage

GPU Shark
Current version: 0.10.0.5
>GPU Shark homepage


Blogs
>JeGX's HackLab

Geeks3D's Articles
>GPU Memory Speed Demystified

>Multi-Threading Programming Resources

>GeForce and Radeon OpenCL Overview

>How to Get your Multi-core CPU Busy at 100%

>How To Make a VGA Dummy Plug

>Night Vision Post Processing Filter

PhysX FluidMark
Current version: 1.5.2
>FluidMark homepage

TessMark
Current version: 0.3.0
>TessMark homepage

ShaderToyMark
Current version: 0.3.0
>ShaderToyMark homepage
>ShaderToyMark Scores

Demoniak3D
Current Version: 1.23.0
>Demoniak3D
>Download
>Libraries and Plugins
>Demos
>Online Help - Reference Guide
>Codes Samples

3D Graphics Search Engine:

The Geeks Of 3D

 
Programmation LUA
Structures de base

Par Jérôme 'JeGX' GUINOT - The oZone3D Team
jegx [at] ozone3d [dot] net

Vertsion initiale: 6 Octobre 2005


1 - Introduction

2 - Variables

3 - Fonctions

4 - Séquencement des données

5 - Conventions de codage

6 - Downloads




1 - Introduction


LUA est un langage de scripting principalement conçu pour être intégré dans des applications hôtes. Il est parfait pour apprendre les bases de la programmation puisqu’il contient tous les éléments que l’on retrouve dans tous les autres langages. L’application hôte qui permettra d’exécuter du code LUA en situation réelle est Demoniak3D. Mais nous utiliserons aussi l’interpréteur lua.exe pour une utilisation immédiate et simple en ligne de commande.

Liens:
  • LUA home page: le site officiel de LUA.
  • LUA.exe: l'interpréteur LUA en ligne de commande.
  • XpConfigurator: l'utilitaire qui permet d'ajouter l'ouverture d'un shell DOS au clic droit de la souris dans l'Explorer. Pratique pour tester ses codes LUA avec l'interpréteur en ligne de commande.
Mais avant de commencer, nous allons faire le classique "Hello World", histoire de verifier que l'interpréteur en ligne de commande est opérationnel. Pour cela, copiez le code suivant dans un fichier que vous appelerez hello.lua:
print("Hello World From LUA!");
Code 1 - Hello World!

Mettez ce fichier dans le même répertoire que lua.exe et ouvrez un shell dans le répertoire de l'interpréteur. Maintenant vous pouvez exécuter ce premier script avec la commande suivante:
lua hello.lua

Si tout s'est bien passé, le résultat doit être comparable à la figure 1:


Fig. 1 - Votre premier script LUA!

Pour écrire du code LUA, il vous faut un éditeur de texte (j'ai pas dit un logiciel de traitement de texte!). Voici une petite sélection de quelques éditeurs gratuits et commerciaux:
  • Notepad++: simple, performant, régulièrement mis à jour et gratuit. Coloration syntaxique pour la plupart des languages (LUA, XML, C/C++ HTML, VBS, Java, ...). C'est ce que j'utilise en ce moment.
  • Crimson Editor: simple, performant et gratuit. Les mises à jour sont peu fréquentes. Coloration syntaxique des languages.
  • Ultra-Edit: un éditeur de texte ultra performant. Payant (environ 40.00$).




2 - Variables


La variable est un des concepts les plus fondamentaux de la programmation. Une définition assez générale de la variable peut être: emplacement mémoire permettant de stocker une donnée.

Comme dans beaucoup de langages de scripting (PHP, VBScript, ...), les variables en LUA ne sont pas typées (typeless). Cela signifie qu'une variable peut contenir aussi bien une valeur numérique, qu'une chaîne de caractères, ou même une variable (simple ou complexe).

De plus, aucune déclaration n'est à faire avant d'utiliser une variable. On peut l'utiliser directement. C'est simple mais cela peut parfois poser des problèmes lors du codage de scripts volumineux. J'aurais bien apprécié la possibilité d'activer une option du langage pour forcer la déclaration des variables avant leur utilisation.

Voilà un exemple tout simple:
a = 10;
b = 3;
sum = a + b;
print("Sum = " .. sum );
Code 2 - Additionneur

Exécutez-le, le résultat doit être:


Fig. 2 - L'additionneur en action.


Un autre point à aborder est celui de la portée ou visibilité des variables. Par défaut les variables en LUA sont globales. Cela signifie qu'une variable peut être utilisée dans différents scripts, lorsque ces différents scripts font partie du même contexte d'exécution, comme c'est le cas dans les démos Demoniak3D (script d'initialisation, script d'update, ...). Les variables globales sont donc très pratiques dans tous les cas où l'on a besoin de communiquer des données entre différents scripts, ou lorsque l'on a besoin de données persistantes. Nous aurons l'accasion de voir un exemple de compteur de frames dans un des tutoriels consacrés à l'utilisation de LUA dans Demoniak3D.

Si vous avez besoin de limiter la porté d'une variable, il suffit d'ajouter le mot clé (mot réservé du langage) local devant la variable:
a = 10;
local b = 3;
sum = a + b;
print("Sum = " .. sum );
Code 3 - Création d'une variable locale

Les variables que nous venons de voir sont des variables simples, dans le sens où elles contiennent des valeurs simples (nombre et/ou chaîne de caractères). Dans de nombreux cas, dépendants bien sûr des algorithmes employés ou du niveau d'abstration en cours, nous avons besoin de définir des variables un peu plus sophistiquées. Un exemple simple est la manipulation de vecteurs 3D. Il est dans ce cas plus simple de manipuler une seule variable contenant les 3 composantes du vecteur, que de manipuler 3 variables représentant chacune une composante. La création d'une variable qui nous permettrait d'agir sur des vecteurs 3D peut se faire de la manière suivante:
vecA = {x=2.0, y=-1.0, z=0.0};
vecB = {x=-4.0, y=0.0, z=-2.0};	
vecSum = vecB;
vecSum.x = vecA.x + vecB.x;
vecSum.y = vecA.y + vecB.y;
vecSum.z = vecA.z + vecB.z;
Code 4 - Création de variables complexes

Une autre catégorie de variables couramment utilisée est le tableau. Un tableau peut se définir comme un ensemble de variables qui peuvent être accédées par un indice. Un tableau se note avec le symbole []. Chaque valeur contenue dans une case du tableau se nomme élément. Le code suivant nous donne un exemple:
-- Array creation.
myArray = {}; 

-- Array initialization.
myArray[0] = 10.0;
myArray[1] = 20.0;
myArray[3] = "Hello";
vecA = {x=-4.0, y=0.0, z=-2.0};	
myArray[4] = vecA;
Code 5 - Création et initialisation d'un tableau





3 - Fonctions


LUA, comme tout bon langage qui se respecte, nous donne la possibilité de créer des fonctions. Une fonction peut être vue comme une séquence d’instructions masquée derrière un nom unique et permettant de réaliser une action ou d’agir sur des données. Une fonction permet simplement de factoriser le code afin d'éviter de coder plusieurs fois une même suite d'instructions.

Il existe dans LUA 2 catégories de fonctions:
  • Fonctions existantes: librairies standards de LUA - cas de la fonction print().
  • Fonctions utilisateurs: créées par le développeur
Le cas intéressant est celui des fonctions utilisateurs.

Reprenons notre addition de tout à l'heure mais, maintenant, créons une fonction pour additionner 2 nombres. Cette fonction additionnera les 2 nombres et affichera la somme:
function add( a, b)
	local sum = a + b;
	print( "Sum = " .. sum );
end

add(12, 45);
add(548.02, 14587.2298);
add(-1, 1);
Code 6 - Création d'une fonction

Une fonction peut aussi retourner une valeur. Modifions légèrement notre additionneur pour qu'il nous retourne la somme des 2 nombres passés en paramètre:
function add( a, b)
	local sum = a + b;
	return(sum);
end

local sum = add(12, 45);
sum = add(548.02, 14587.2298);
sum = add(-1, 1);
Code 7 - Création d'une fonction addition

La variable retournée peut être simple ou complexe comme le montre l'exemple suivant:
function createVec(_x, _y, _z)
	local out = {x = _x, y = _y, z = _z};
	return(out);
end

function addVec( u, v )
	local out = {x=0.0, y=0.0, z=0.0};
	out.x = u.x + v.x;
	out.y = u.y + v.y;
	out.z = u.z + v.z;
	return(out);
end

function printVec( v )
	print( "vector: <" .. v.x .. ", " .. v.y .. ", 
	       " .. v.z .. ">" );
end

local vecA = createVec(0.22, 0.45, -0.66);
local vecB = createVec(-012, 0.55, 0.59);
local vecSum = addVec(vecA, vecB);
printVec( vecSum );
Code 8 - Création d'une fonction complexe

L'exécution du code 8 donne le résultat suivant:


Fig. 3 - L'addition de vecteurs.





4 - Séquencement des données - Structures de contrôle


Le séquencement des données est le terme technique qui engloble toute la logique permettant de contrôler le flux de données. Cela comprend principalement:
  • les structures de tests conditionnels
  • les structures itératives


4.1 - Tests conditionnels

Les tests conditionnels se font avec la construction if - then - else. Un exemple simple qui se prête à merveille aux tests conditionnels est la résolution de l'équation du second degré ax^2 + bx + c = 0.

Le code suivant applique l'algorithme classique (calcul du déterminant et test de celui-ci) pour la résolution de l'équation:
--
-- Resolution de l'equation du second degre: ax^2 + bx + c =0
--

text = "";
a = 2.0;
b = 1.0;
c = -2.0;

print( "\nEquation " .. a .. "x^2 + " .. b .. "x +
       " .. c .. " = 0\n" );

-- 1)  Calcul du determinant
--
delta = b*b - (4 * a * c );

-- 2)  Solutions en fonction de la valeur du determinant.
--
if( delta<0 ) then
	text = "Il n'y a aucune solution reelle."
elseif( delta == 0 ) then
	x = -b / (2 * a);
	text = "Il y a une solution reelle: x=" .. x;
else
	x1 = (-b + math.sqrt(delta)) / (2*a);
	x2 = (-b - math.sqrt(delta)) / (2*a);
	text = "Il y a deux solutions reelles: x1=" .. x1 .. " 
	       et x2=" .. x2;
end

print( text );
Code 9 - Résolution de l'équation du second degré

Je vous laisse le soin de tester vous-même ce code...


4.2 - Structures itératives

Les structures itératives nous permettent de répéter une séquence d’opérations un certain nombre de fois. Les itérations se font avec while / do-while / repeat-until / for.

L'exemple suivant montre comment calculer la somme des 100 premiers entiers à l'aide des itérateurs:
sum = 0;
loop_counter = 0;
limit_sup = 100;
while( loop_counter<limit_sup ) do
	sum = sum + loop_counter;
	loop_counter = loop_counter + 1;
	print( "Somme intermédiaire = " .. sum );
end

print( "
La somme des " .. limit_sup .. " premiers entiers est: " .. sum );
Code 10 - Somme des 100 premiers entiers



L'exemple suivant montre comment utiliser une boucle for:
for j=0,10 do
	for i=0,10 do
		print( "j=" .. j .. " i=" .. i );
	end
end	
Code 11 - Boucle for

Il est possible de sortir prématurément d’une itération avec le mot clé break.

Pour cloturer cette initiation à LUA, je vous propose un exemple complet qui utilise tout ce que l'on a vu jusqu'à présent: l'implémentation LUA du célèbre algorithme du tri à bulles (le bubbleSort). Le script est divisé en 3 grandes parties:
  • Partie 1: création d'un tableau contenant MAX_SIZE éléments. La valeur de chacun des éléments est générée aléatoirement grâce à la fonction random() de la librairie standard math.
  • Partie 2: tri en ordre décroissant des éléments du tableau à l'aide de l'algortihme bubble sort.
  • Partie 3: affichage des éléments triés.

-- 1) creation et initilisation d'un tableau
--
myArray = {};
MAX_SIZE = 10;
i = 0;
while( i<MAX_SIZE ) do
	myArray[i] = math.random(200);
	print( "myArray[" .. i .. "]=" .. myArray[i] );
	i = i + 1;
end

-- 2) tri du tableau avec l'algorithme du tri a 
-- bulles (bubblesSort)
--
num_swaps = 0;
i = 0;
while( i<MAX_SIZE ) do 
	j = i+1;
	while( j<MAX_SIZE ) do 
		if( myArray[j] > myArray[i] ) then
			tmp = myArray[j];
			myArray[j] = myArray[i];
			myArray[i] = tmp;
			num_swaps = num_swaps + 1;
		end
		j = j + 1;
	end
	i = i + 1;
end

-- 3) affichage du tableau trie
--
print( "
Et voila le tableau trie:
" );
i = 0;
while( i<MAX_SIZE ) do
	print( "myArray[" .. i .. "]=" .. myArray[i] );
	i = i + 1;
end

print( "\nIl y a eu " .. num_swaps .. " swaps pour 
       trier ce tableau" );
Code 12 - Tri à bulles

L'exécution du code 12 donne le résultat suivant:


Fig. 4 - Tri à bulles.





5 - Conventions de codage


Les conventions de codage sont très importantes pour ne pas dire primordiales pour obtenir du code clair, fonctionnel, facile à lire et surtout FACILE A DEBOGUER! Chacun est libre d'adopter les conventions de codage qui lui plaisent, le principal étant d'arriver aux objectifs cités précédemment. Pour ma part voici celles que j'utilise:
  • Préfixer le nom des variables globales par g_. Exemple: g_frame_counter = 0;
  • Toujours ajouter d'un point-virgule à la fin de chaque instruction. En effet, LUA est très souple et n'oblige pas à préciser de manière explicite la fin d'une instruction. Mais il est conseillé de le faire afin de simplifier la vie du parser / compiler LUA.
  • Bien identer son code et respecter les hiérarchies d'identation. Voir l'algo du tri à bulles pour une exemple.
  • Nommer de manière explicite les noms variables et de fonctions. Ceci est fondamental pour la lecture et la compréhension d'un code LUA complexe. Il est plus facile de comprendre à quoi sert la variable g_position_sphere_red_x que x1...
  • Préfixer une variable de type tableau par arr. Exemple arr_sphere_clones ou g_arr_sphere_clones si la variable est globale.
  • Préfixer une variable avec i, f ou str pour indiquer si elle contient une valeur numérique entière (i), réelle (f) ou une chaîne de caractères (str). Exemple: g_i_status, str_name, g_f_position_x.
  • Mettre des commentaires (--). Les commentaires ont un triple rôle: ils vous permettent de faire le point sur ce que vous être en train de coder, ils vous permettent de savoir ce que vous avez codé et enfin ils permettent aux autres de savoir ce que vous avez tenté de coder...


6 - Downloads



Téléchargez les codes sources d'accompagnement + Interpréteur LUA
Mise à jour: 5 Octobre 2005




GeeXLab demos


GLSL - Mesh exploder


PhysX 3 cloth demo


Normal visualizer with GS


Compute Shaders test on Radeon


Raymarching in GLSL



Misc
>Texture DataPack #1
>Asus Silent Knight CPU Cooler
Page generated in 0.0033810138702393 seconds.