Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Reflexion réaliste de surface fluide

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par stefbuet, le 09 juillet 2010
Prérequis
  • Manipulation de bitmaps
  • Opérateurs binaires
  • Chargement images
  • Document class
  • Produit scalaire/vectoriel

Rendu final

L"extension Adobe Flash Plugin est nécessaire pour afficher ce contenu.

Table des matières

I/ Introduction
II/ Stockage des données et première animation
III/ Reflexion spéculaire
IV/ Reflexion de l'environnement
V/ Conclusion

I/ Introduction

Une surface d’eau peut être simulée dans une animation Flash pour par exemple renforcer le réalisme des décors d’un jeu vidéo. Nous allons au cours de cet article apprendre à créer des surfaces fluide en prenant en compte plusieurs paramètres : mouvement de l’eau (vagues), point de vue de l’utilisateur et du soleil et bien d’autres, pour au final avoir un rendu de surface fluide avec un effet d’éclairage réaliste, avec réflexion et réfraction de l’environnement. Il faut savoir que tous les calculs effectués pour une telle simulation doivent être effectués dans un espace de 3 dimensions, mais nous verrons que le fait de se trouver en 2D pourra grandement simplifier nos calculs.

II/ Stockage des données et première animation.

a/ Stockage des données.

Nous allons avoir besoin de plusieurs données pour réaliser cette simulation graphique de fluide. Par exemple la texture du fluide est une donnée, la hauteur de vague en chaque point est une autre donnée. Nous verrons plus tard d’autres types de données importantes qui interviendront dans le rendu final. Pour stocker ces données, nous allons utiliser des bitmaps, grâce à l’objet BitmapData. Couramment dans le monde du rendu graphique, ces bitmaps sont appelés Render Targets et sont utilisés plutôt en 3D pour créer des effets complexes avec des shaders. Un bitmap correspondra à notre surface, et chaque pixel du bitmap, à un point de la surface. Chaque pixel du bitmap contient une couleur, elle-même composé de 3 nombres représentant le rouge, vert et bleu allant de 0 à 255. Vous pouvez aussi rajouter un canal correspondant à l’alpha (transparence). Cette précision est faible et les API tels qu’Open GL proposent des bitmaps gérant les nombres flottants, mais Flash ne supporte pas cela actuellement. Ces données RGB représentent à la base une couleur, mais on peut les interpréter comme n’importe quelle variable ! Par exemple on pourrait stocker la position de chaque point de la surface (X,Y,Z) dans chaque pixel du bitmap en mettant X en tant que Rouge, Y pour le Vert et Z pour le Bleu. Nous allons voir cela grâce à un premier exemple simple. Notez que l’utilisation de bitmap pour stocker les données n’est pas nécessaire. En effet se trouvant non pas dans un shader, les contraintes ne sont pas les même et on peut calculer toutes les données en 1 seule fois. Nous utiliserons des bitmaps ici pour expliquer plus facilement chaque donnée à calculer.

b/ Première animation

Nous allons commencer par décrire le mouvement de notre surface fluide qui est très simple. Les vagues sont des ondes de type sinusoïdales. Nous pouvons exprimer la hauteur de la surface en chaque point grâce à une simple fonction mathématique faisant intervenir un sinus et un cosinus. Je vous rappelle que pour le moment nous calculons dans un espace à 3 dimensions. On commence par donner une hauteur comme si on était en 2D avec un simple sinus qui évolue en fonction du temps, avec un déphasage pour chaque point de plus en plus grand plus on s’éloigne du point d’origine :
Soit t le temps, x le déphasage du point avec l’origine, l’expression de la hauteur H d’amplitude A est :
H(t,x) = A*sin(t+x)

Pour avoir notre expression finale il faut passer en 3 dimensions. Imaginons que l’expression précédente s’exprime pour l’axe X, maintenant rajoutons avec un cosinus une variation de hauteur sur l’axe Z et nous aurons notre expression de hauteur de surface d’un plan XZ exprimé selon le vecteur y.
H(t,x,z) =0.5*A*(sin(t+x)+cos(t+z))

Je vais garder cette forme d'ondulation pour tout le reste de l'article. Mais vous pourriez par exemple simuler l'oscillation sinusoïdale entretenue d'un fluide (la forme quand une goutte tombe dans l'eau, mais sans atténuation) en utilisant la formule h=sin(x²+y²), ou n'importe quelle fonction de votre choix.

Maintenant il va nous falloir stocker ces données pour les utiliser plus tard. Comme expliqué ci-dessus, nous allons utiliser un bitmap pour stocker l’information de la hauteur du fluide. Une seule composante du bitmap est nécessaire car la hauteur est un simple nombre, nous utiliserons donc le canal Rouge. On notera que dans ce tutoriel je vais utiliser un bitmap par type de donnée du jeu, mais dans des applications tels que les jeux 3D exploitant le GPU des cartes graphique, il faut économiser au maximum la mémoire vidéo utilisée, et on stocke plusieurs types de données dans le même Render Target.
Passons à l’implémentation :

package {
 
	import flash.display.*;
	import flash.events.*;
	import flash.utils.*;
	import flash.net.*;
	import flash.geom.*;
	import flash.text.*;
 
	public class Main extends Sprite {
 
		public function Main() {
 
			stage.align=StageAlign.TOP_LEFT;
			stage.scaleMode=StageScaleMode.NO_SCALE;
 
			var renderTargetSize:uint=64;
			surfaceHeight=new BitmapData(renderTargetSize,renderTargetSize, false);
 
                        //Le bitmap pour afficher le resultat à l'ecran
                        var b:Bitmap=new Bitmap(surfaceHeight);
			b.width=256;
			b.height=256;
			b.smoothing=true;
			addChild(b);
 
                        //on update la surface à chaque image...
                        addEventListener(Event.ENTER_FRAME, updateSurface);			
 
		}
 
 
		private function getSurfaceHeight(...):Number {
			return(...);
		}																												
 
		private function updateSurface(e:Event):void {
 
			for(var i:Number=0; i<surfaceHeight.width; i++) {
				for(var j:Number=0; j<surfaceHeight.height; j++) {
				    //modification du bitmap pixel par pixel
				    //grace à la fonction getSurfaceHeight
                            surfaceHeight.setPixel(i,j,getSurfaceHeight(...));
 
				}
			}
		}
 
		private var surfaceHeight:BitmapData;
 
	}
}

Voila notre classe de base avec deux méthodes en plus. Nous avons créé un bitmap pour contenir l'information de hauteur de la surface. Ce bitmap est affiché grâce à un objet d'affichage Bitmap, pour voir le résultat des calculs. Ce bitmap sera modifié par la fonction updateSurface qui parcourt chacun de ces pixels un à un et les modifie grâce à un appel à la fonction getSurfaceHeight pas encore implémentée. J'ai fixé la taille des bitmaps de données à 64×64 pour le reste de l'article car c'est un rapport qualité sur temps de calcul acceptable. La hauteur est stockée sur le canal rouge du bitmap sans prendre en compte l'amplitude des ondes, de 0 à 255. Pour cela on utilise l'opérateur binaire « que vous devez connaitre. Vous devez aussi savoir comment fonctionne » et | pour comprendre l'utilisation des couleurs dans ce article.

Passons à l'implémentation de la fonction getSurfaceHeight:

private function getSurfaceHeight(t:Number, x:Number, y:Number, nb:uint, width:Number, height:Number):Number {
			var c:Number=2*Math.PI*nb;
			return(0.25*(2+Math.sin(t+c*x/width)+Math.cos(t+c*y/height)));
		}

Comme vous pouvez le voir, cette fonction est une très simple implémentation de la formule donnée ci-dessus ( H(t,x,z) =0.5*A*(sin(t+x)+cos(t+z)) avec une gestion du nombre de vagues demandées par l'utilisateur au niveau de l'offset). Il ne faut pas oublier de mettre à jour l'appel de la fonction getSurfaceHeight dans la fonction updateSurface car nous avons ajouté des paramètres : t pour le temps, nb pour le nombre d'onde affichées, width & height pour les dimensions du bitmap et c'est tout!.

var nb:uint=3;
var t:Number=getTimer()/700;
surfaceHeight.setPixel(i,j,255*getSurfaceHeight(t, i, j, nb, surfaceHeight.width, surfaceHeight.height)<<16);




Ces données de hauteurs peuvent être utilisées pus tard, mais avec des approximations, elles sont inutiles et nous avons fait cet exemple uniquement dans le but de vous montrer comment gérer des données à travers un bitmap. Nous allons maintenant attaquer le vif du sujet !

III/ Reflexion spéculaire.


Avant d’effectuer tout calcul de réflexion ou réfraction de l’environnement nous allons calculer les reflets spéculaires de la surface fluide. En programmation graphique 3D, nous utilisons pour faire des éclairages réalistes le modèle de Phong, il consiste à donner un éclairage diffus et spéculaire aux objets en fonction de la position de l’observateur, des lumières et du point de la surface à traiter. Ici, comme nous sommes avec une surface transparente, l’éclairage diffus n’est pas utile, mais nous allons calculer la map spéculaire de la surface. Elle correspond aux zones de la surface où le rayon venant de la source de lumière, pour nous le soleil, est directement réfléchie dans l’œil de l’observateur, d’où une importante lumière blanche.

water05_3b.jpg

Nous obtenons la composante spéculaire de l’éclairage avec la formule suivante :
S = ( ob . R ) ^ is

Avec :
ob : vecteur normé de la direction œil-point surface.
R : vecteur normé correspondant à la direction de la lumière reflétée.
is : indice spéculaire : plus il est grand, moins les reflets spéculaires seront larges.
S sera compris entre 0 et 1

Calculons d’abord le vecteur ob, c’est le plus simple. Pour cela il nous faut la position de l’observateur (son œil) et la position du point de la surface actuellement observée. Pour cela nous pourrions utiliser notre bitmap contenant la hauteur de la surface actuellement. Cependant nous ferons l’approximation que la surface est plane. Cette approximation exclue la nécessité du premier bitmap calculé d’où un gain de calculs. La différence sur le résultat n’est même pas visible.

Ob = positionSurface – positionObservateur

(dessin eau + observateur)

Nous prendrons :
positionObservateur=(width/2, width/3, height*2)

Et nous avons : positionSurface=(x, H(x,y), y)

Pour calculer le vecteur R, cela est plus compliqué. Il nous faut d’abord le vecteur d’incidence de la lumière. Considérant le soleil à l’infini, nous prendrons un vecteur constant, on considère donc le soleil comme une lumière de type directionnelle et non pas omnidirectionnelle. Ce vecteur sera donc L=(1,1,1).
Ensuite, il nous faut aussi connaitre la normale de la surface. Pour cela, nous allons faire comme précédemment : calculer la normale de chaque point de la surface, et stocker ces informations dans un bitmap. L’équation de la surface du fluide est décomposable sur les deux axes X et Z avec une fonction cosinus et une autre fonction sinus. En déterminant les deux vecteurs directeurs en un point donné de la surface, on peut obtenir la normale à la surface en faisant un produit vectoriel des deux vecteurs. La normale en chaque point de la surface est un des éléments les plus importants pour créer notre effet d’eau, on l’utilisera pour le reflet spéculaire, mais aussi la réflexion, réfraction.
En projetant l’équation sur les axes X et Z on obtient :

H=0.5*A*(Hx + Hy)
Hx = cos(t+x)
Hz = sin(t+y)

Les vecteur directeurs donc donc (dérivées):
Hxd = (1, -sin(t+x), 0)
Hzd =(0, cos(t+y), 1)

Si vous utilisez une formule plus compliquée pour la forme de votre surface, par exemple sin(x²+z²) il vous faudra faire une dérivée partielle pour avoir les composantes x et z de la dérivée de la fonction.

Avec un produit vectoriel des deux dérivées, on trouve la normale N à la surface en un point x,y de la surface :
N=(-Hxd, 1, -Hxd)

Passons à l’implémentation :

La fonction pour récupérer la valeur de la réflexion spéculaire:

private function getSurfaceNormal(t:Number, x:Number, y:Number, amp:Number, nb:uint, width:Number, height:Number):Object {
	var c:Number=2*Math.PI*nb;
	var factorAxisX:Number=-0.5*amp*Math.sin(t+c*x/width);
	var factorAxisY:Number=0.5*amp*Math.cos(t+c*y/height);
	var normal:Object=new Object();
	normal.x=-factorAxisX;
	normal.y=1;
	normal.z=-factorAxisY;
	return(normalize(normal));
}

Encore une fois rien de technique dans cette fonction, nous ne faisons que calculer des valeurs grâce aux formules précédentes. Cependant, j'utilise ici deux méthodes non définies dans l'API Flash pour retourner la valeur normalisé d'un vecteur dans un espace de dimension 3, et pour limiter une valeur réelle à un intervalle.

//limite la valeur a entre b et c
private function clamp(a:Number, b:Number, c:Number):Number {
	var max:Number=(b>c)?b:c;
	var min:Number=(max==b)?c:b;
	return(Math.min(max,Math.max(a,min)));
}
 
//renvoie un vecteur de même direction de norme 1
private function normalize(vector:Object):Object {
	var l:Number=Math.sqrt(Math.pow(vector.x,2)+Math.pow(vector.y,2)+Math.pow(vector.z,2));
	var toReturn:Object={x:vector.x/l, y:vector.y/l, z:vector.z/l};
	return(toReturn);
}


Maintenant, il faut créer un bitmap pour gérer les informations des normales, comme on l'avait fait avec la hauteur de la surface. Puis, dans la boucle de rendu de la fonction updateSurface, nous devrons mettre à jour ce bitmap. Vous noterez la technique utilisée pour stocker la normale dans le bitmap : Elle est constitué de 3 composants allant de -1 à 1 que l'on veut mettre dans 3 variables allant de 0 à 255. Nous les transformerons donc en utilisant la formule 0.5*(N+1)*255. Cette formule étant linéaire, on pourra retrouver facilement la valeur de la normale avec les composantes RGB du bitmap.

Dans le constructeur de la classe (ne pas oublier de déclarer cette variable dans la classe!):

surfaceNormal=new BitmapData(renderTargetSize,renderTargetSize, false);


Dans la boucle de parcours des pixels de la surface:

//a:amplitude
var a:Number=0.2;
var normal:Object=getSurfaceNormal(t, i, j, a, nb, surfaceHeight.width, surfaceHeight.height);
//encodage de 0 à 255 de la normale (origine: -1 à 1)
surfaceNormal.setPixel(i,j,((0.5*(normal.x*255+255))<<16)|((0.5*(normal.y*255+255))<<8)|((0.5*(normal.z*255+255))));


Ayant le vecteur normal et le vecteur d’incidence de la lumière L, on peut maintenant calculer le vecteur de réflexion R de la lumière :
R=2*(N.L)N-L



Nous pouvons maintenant calculer la composante spéculaire de la surface fluide à partir de la formule précédente : S = ( ob . R ) ^ is

Implémentation :

private function getSurfaceSpecular(x:Number, y:Number, amp:Number, glossiness:uint, viewPos:Object, normalData:BitmapData, heightData:BitmapData):Number {
 
        //on recupere la normale et on la décode pour avoir ces composantes x,y,z:
	var normalColor:Number=normalData.getPixel(x,y);
	var normal:Object=new Object();
	normal.x=((normalColor>>16)/255)*2-1;
	normal.y=((0xFF&(normalColor>>8))/255)*2-1;
	normal.z=((0xFF&normalColor)/255)*2-1;
 
       //direction du soleil normalisée
	var L:Object=normalize({x:0, y:1, z:-1});
	L=normalize(L);
 
       //calcul angle de réflexion parfait
	var NdotL:Number=Math.max(0,L.x*normal.x+L.y*normal.y+L.z*normal.z);
	var R:Object=new Object();
	R.x=2*normal.x*NdotL-L.x;
	R.y=2*normal.y*NdotL-L.y;
	R.z=2*normal.z*NdotL-L.z;
	R=normalize(R);
 
       //Calcul de l'intensité de la réflexion spéculaire
	var ob:Object={x:viewPos.x-x, y:viewPos.y, z:viewPos.z-z};
	ob=normalize(ob);
	var OBdotR=Math.max(0, ob.x*R.x+ob.y*R.y+ob.z*R.z);
	return(Math.pow(OBdotR, glossiness));
 
}


Et on met cela dans un BitmapData que je vous laisse le soin de créer de la même manière que les deux autres : surfaceSpecular.
Maintenant, injectons les données dans notre bitmap dans la boucle de rendu:

var glossiness:Number=100;
var viewPos={x:finalResult.width/2, y:30, z:0.8*finalResult.height} //cette valeur est simpas...
var s:Number=255*getSurfaceSpecular(i, j, a, glossiness, viewPos, surfaceNormal, surfaceHeight);
surfaceSpecular.setPixel(i,j,(s<<16)|(s<<8)|(s)); //R,G,B = s,s,s (opérateurs binaires à connaitre cf. début article)


IV/ Réflexion de l’environnement.


Nous allons maintenant refléter l’environnement de la scène dans l’eau. Pour cela il nous faudra d’abord une map de réflexion. C’est une image correspondant à la scène autour de la surface d’eau. Il en existe différents types : sphere map, cube map, torondoique map… Nous allons utiliser la première car c’est la plus simple. De nombreuses images de ce type sont disponibles sur internet. On les obtient par un rendu 3D. Pour calculer en chaque point de la surface la couleur correspondante, il nous faut connaitre le vecteur reflété de la vision de l’observateur. Pour cela nous utiliserons la même formule que précédemment lorsque nous avons voulu calculer le vecteur reflété du soleil. Les composantes X et Z de ce vecteur correspondrons à la position du pixel sur la sphere map appelée aussi environement map ou reflexion map.

Concept du calcul de reflexion, ici avec une cube map.
cube_mapped_reflection_example.jpg

Passons à l’implémentation :

Calcul de la couleur des pixel par reflexion:

private function getSurfaceReflection(x:Number, y:Number, viewPos:Object, normalData:BitmapData, reflectionMap:BitmapData):Object {
 
        //décodage de la normale en ce point
	var normalColor:Number=normalData.getPixel(x,y);
	var normal:Object=new Object();
	normal.x=((normalColor>>16)/255)*2-1;
	normal.y=((0xFF&(normalColor>>8))/255)*2-1;
	normal.z=((0xFF&normalColor)/255)*2-1;
 
        //calcul du vecteur de la vision reflétée parfait.
	var ob:Object={x:viewPos.x-x, y:viewPos.y, z:viewPos.z-y};
	ob=normalize(ob);
	var NdotOB:Number=Math.max(0,ob.x*normal.x+ob.y*normal.y+ob.z*normal.z);
	var R:Object=new Object();
	R.x=2*normal.x*NdotOB-ob.x;
	R.y=2*normal.y*NdotOB-ob.y;
	R.z=2*normal.z*NdotOB-ob.z;
	R=normalize(R);
 
        //les composantes X et Y sont les coordonnées (-1..1) du pixel de la sphere map à afficher.
        //normalement pas besoin de clamper la valeur, mais pour des raisons d'approximation, je le fait ici.		
	var posX=clamp((1+R.x)*reflectionMap.width/2,1,reflectionMap.width);
	var posY=clamp((1+R.z)*reflectionMap.width/2,1,reflectionMap.height);
	var color=reflectionMap.getPixel(posX, posY);
 
        //on retourne la couleur!
	var returnColor=new Object();
	returnColor.r=color>>16;
	returnColor.g=0xFF&(color>>8);
	returnColor.b=0xFF&color;
	return(returnColor);
}


Ensuite on met comme d'habitude tout cela dans un bitmap que vous devez créer : surfaceReflection:
N'oubliez pas de charger une image dans un bitmapData nommé reflectionMap…

var color:Object=getSurfaceReflection(i, j, viewPos, surfaceNormal, reflectionMap);
surfaceReflection.setPixel(i,j,(color.r<<16)|(color.g<<8)|(color.b));


Si vous ne savez pas comment créer votre relfexionMap :

//dans le constructeur
l=new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE, startProcess);
l.load(new URLRequest(URLAb+"reflexionMap.png"));
//...
 
//dans startProcess:
private function startProcess(e:Event):void {
    reflectionMap=new BitmapData(l.width, l.height, false);
    reflectionMap.draw(l);
}


V/ Image finale et scene de test.


Vous avez toutes les cartes en mains pour finaliser notre surface. Il suffit d’ajouter à l’image de réflexion de l’environnement la réflexion spéculaire. Pour simuler la transparence de l’eau nous pouvons ajouter avec une transparence plus ou moins grande une image de fond de torrent, c’est la boue en quelque sorte. Cependant pour plus de réalisme, il faudrait calculer un nouveau bitmap avec l’image du sol de la surface d’eau avec un effet de réfraction en fonction de l’angle de vue et de la normal en chaque point de la surface. Vous pouvez vous baser sur les calculs précédents pour effectuer cet effet seul, une sorte de mise en pratique.





Une fois l’image finale de la surface obtenue, il faut l’intégrer à une scène. Pour donner une forme réaliste à la surface, nous pouvons utiliser un masque avec bords progressifs. Pour cela il suffit de charger une image gérant la tranparence, par exemple un PNG, et transférer son canal alpha vers celui du bitmap de la surface grace à la fonction copyChannel de BitmapData. Nous pourrons aussi utiliser les propriétés 3D des éléments d’affichage supportés par Flash pour donner un effet de perspective à la surface, par exemple en inclinant la surface de 60° sur l’axe X donnant un effet de perspective. Vous pourrez aussi implémenter un effet de rotation en faisant tourner sur l’axe Z la surface, sans oublier de changer la position de l’observateur en même temps pour les calculs de réflexion. Vous pourrez alors voir les reflets changer de façon très réaliste ! Vous pourrez aussi facilement changer la forme des vagues pour par exemple créer des oscillations circulaires, selon vos besoins.

Implémentation :

Pour initialiser un peu tout ça:

//Dans le constructeur
//Bitmapdata pour le resultat final & le mask alpha
//Attention, il faut bien activer le canal Alpha ici.
finalResult=new BitmapData(renderTargetSize,renderTargetSize, true);
maskFilter=new BitmapData(renderTargetSize,renderTargetSize, true, 0);
 
//le bitmap pour afficher le resultat
surface=new Bitmap(finalResult);
surface.smoothing=true;
surface.x=-100;
surface.y=stage.stageHeight/2;
surface.width=surface.height=768;
surface.rotationX=50;
addChild(surface);
 
l2=new Loader();
l2.contentLoaderInfo.addEventListener(Event.COMPLETE, onAlphaLoaded);
l2.load(new URLRequest(URLAb+"alphaChannel.png"));
 
//dans la fonction onAlphaLoaded:
//permet de dessiner le mask alpha dans un bitmap de la taille de la surface.
//La matrice permet de mettre le mask à la bonne échelle quelle que soit la taille de l'image.
var m:Matrix=new Matrix();
m.scale(finalResult.width/512.0, finalResult.height/512.0);
maskFilter.draw(l2, m, null, BlendMode.ADD);


Et après la boucle de rendu (Attention, après, pas à l'intérieur sinon vous aller avoir une énorme perte de performances!)

//on dessine tout sur le bitmap:
finalResult.draw(surfaceReflection);
finalResult.draw(surfaceSpecular, null, null, BlendMode.ADD);
//on applique le filtre alpha:
finalResult.copyChannel(maskFilter, new Rectangle(0,0,finalResult.width, finalResult.height), new Point(0,0), BitmapDataChannel.ALPHA, BitmapDataChannel.ALPHA);


Vous pourrez constater que la vitesse n’est pas fameuse. Simplement car l’AS3 est un langage interprété donc lent, et que cette technique n’est pas censée tourner sous Flash. La méthode présentée en fait ici est celle utilisée dans les jeux vidéos 3D pour faire tous les effets très réalistes de réflexions. Cependant ils sont exécutés sur le processeur graphique de votre machine, et compilés, leur donnant une grande vitesse.

Pour aller plus vite, vous pouvez baisser la taille des bitmaps, mais vous perdrez en précision. Une très bonne optimisation est possible lorsque vous utilisez un masque bitmap, ne calculer que les pixels visibles et en testant avant tout calcul si la valeur alpha du pixel courant est supérieure à 0, les gains de performances ne sont vraiment pas négligeables. Mais surtout, vous pouvez utiliser Pixel Blender pour profiter d’une accélération hardware. Ici j’ai préféré rester dans Flash intégralement pour mieux présenter la méthode de calcul. Pour finir, vous voyez que nous calculons la couleur de tous les pixels. Cette technique vous permettra si vous le souhaitez d’utiliser du normal mapping, des images pour avoir une surface encore plus réalise en modifiant un peu les normales !

Code utilisé pour les optimisations :

//A mettre en première ligne des fonctions :
 
//getSurfaceReflexion():
if(maskFilter.getPixel32(x,y)>>24==0) return({r:0, g:0, b:0}); 
 
//getSurfaceSpecular:
if(maskFilter.getPixel32(x,y)>>24==0) return(0);
 
//getSurfaceNormal:
if(maskFilter.getPixel32(x,y)>>24==0) return({x:0,y:0,z:0});


J’espère que cet article aura plu à la plupart d’entre vous. Je suis conscient qu’il sera plus apprécié par les développeurs que les graphistes car il présente une méthode réaliste et complexe à mettre en œuvre. Vous pouvez aussi simplement déformer une image avec un simple filtre de displacement mapping, mais aucune prise en compte de la position du soleil ! Aucune prise en compte de la position de l’observateur !

Telechargements.


Sources + SWF final : main.rar

Stefbuet.