Moteur 3D : Visualisation d'élément liquides
Introduction
Pour ce projet nous avons du réaliser la visualisation d'éléments liquides sur le moteur Goblim. Pour cela nous devons utiliser les différents éléments vus pendant les cours et les tp.
Pour mon projet j'ai décidé de réalisé une mer pour pouvoir utiliser des shaders sur un objet de grande taille. Il est aussi plus simple de visualiser les effets de lumière qui seront appliqués sur l'eau.
J'ai aussi voulu mettre en place un ciel à l'aide d'un skybox pour rendre l'environnement plus réel de la mer.
Et dans une troisième partie j'ai décidé d'intégrer une méthode de post-processing afin de faire un effet de caméra quand elle passe sous le niveau de l'eau.
L'eau
Pour représenter la mer, j'ai décidé d'utiliser un plan avec de nombreux vertex, pour que le mouvement de l'eau soit le plus fluide possible.
Le vertex
Pour chaque effet que j'ai utilisé, je les applique sur le point courant ainsi que sur d'autres points du même plan afin de pouvoir calculer la nouvelle normale de mon point.
Pour réaliser le vertex shader pour les vagues j'ai séparé en différents effets pour pouvoir les contrôler plus facilement.
Il y a comme effets :
- Les vagues
- Le bruit de l'eau
- La taille des piques (hauteur)
- Le nombre de vagues
- La vitesse des vagues
Pour réaliser la forme des vagues, j'ai utilisé une fonction sinus : p.y = p.y + sin(p.x*5.0 + p.z*1.0) Cette fonction permet de faire varier la hauteur du point en fonction de ces coordonnées x et y, les coefficients 5.0 et 1.0 permettent de déterminer la direction des vagues. J'ai aussi ajouté une variable de temps afin de faire avancer les vagues.
Ensuite j'ai rajouté un bruit d'eau en utilisant une fonction de bruit trouvé sur internet par Ashima Arts et Stefan Gustavson, j'ai donc ajouté le bruit à la hauteur de mon point afin de rajouter un peu de désordre.
Pour la taille des piques de vagues j'ai juste multiplié la hauteur des vagues.
J'ai aussi rajouté une variable qui permet de contrôler le nombre de vagues en multipliant le contenu du sinus des vagues.
Tous les effets du vertex
L'éclairage
Pour réaliser l'éclairage de mes vagues j'ai implémenté l'algorithme de Phong sur mon océan.
vec3 lightVec = normalize(posLumGPU * rep);
vec3 camVec = normalize(posCamGPU * rep);
float v = (sin(time/4000.0)+1.2)/2.2;
float diffu=max(dot(normalize(lightVec),NormaleSurface),0.3);
float spec=pow(max(dot(2*diffu*normMap-normalize(lightVec), normalize(posCamGPU)),0.0),30.0);
Color = vec4(CPU_color.xyz*v*(0.6*diffu+0.5+0.3*spec), transparence);
J'ai utilisé une normale map d'eau trouvée sur internet sur le site FilterForge. Pour cela j'ai utilisé ce que l'on avait vu dans le tp pour charger la texture dans le fragment shader afin de modifier la normal.
Enfin j'ai rajouté une variable permettant de contrôler la transparence des vagues. Pour cela j'ai dû rajouter une fonction openGL pour que cela fonctionne dans mon engine.
AntTweakBar
J'ai utilisé l'AntTweakBar sur mon matérial afin de pourvoir contrôler tous les effets dont j'ai parlé plus haut, cette volonté de tout pouvoir contrôler à partir de l'interface ma conduit à faire tout les effets moi même afin de mieux les comprendre et de pouvoir les modifier ainsi que les contrôler par la suite.
vec4 colTexJour = texture(samplerJour, texCoord.xy);
vec4 colTexNuit = texture(samplerNuit, texCoord.xy);
float v = (sin(t/4000.0)+1.0)/2.0;
Color = vec4(v*colTexJour.xyz + (1-v)*colTexNuit.xyz,colTexJour.a);
Implémentation d'AntTweakBar
Pour cela j'ai crée des variables qui pouvaient être modifié par une AntTweakBar et ainsi pouvoir set une variable uniform avec pour contrôler les shaders.
La skybox
Objet et texture
Pour réaliser la skybox j'ai choisi un objet cube et les textures de ciel récupérées sur le pack de texture Minecraft : Matox. J'ai du transformer les textures pour qu'elles puissent se poser parfaitement sur le cube.
Cycle jour/nuit
J'ai aussi profité de réaliser une skybox afin de rajouter un cycle jour nuit avec 2 textures, une pour le jour une pour la nuit. on peut voir la texture de jour juste au dessus, et la texture de nuit juste après.
J'ai utilisé une fonction mathématique afin de pouvoir calculer les pourcentages d'intensité de chaque image. La fonction sin(t/4000.0)+1.0)/2.0 permet de calculer l'intensité de la première texture en fonction du temps. Pour calculer l'intensité de la deuxième texture il faut juste faire 1 - l'intensité de la première texture.
vec4 colTexJour = texture(samplerJour, texCoord.xy);
vec4 colTexNuit = texture(samplerNuit, texCoord.xy);
float v = (sin(t/4000.0)+1.0)/2.0;
Color = vec4(v*colTexJour.xyz + (1-v)*colTexNuit.xyz,colTexJour.a);
J'ai aussi profité d'avoir créé ce cycle jour nuit, pour rajouter une fonction qui permettra de changer la luminosité de l'eau en fonction de l'heure du jour et de la nuit grâce a cette fonction sin(time/4000.0)+1.2)/2.2. Elle est presque identique à la fonction de l'heure excepté le 0.2 rajouté pour éviter de ne plus du tout avoir de lumière la nuit.
Le post-processing
Je voulais rajouter un dernier effet a mon projet, il me permet de rendre la caméra bleu quand on arrive sous l'eau.
Pour cela j'ai d'abord créer un effet que j'ai appelé FBOtest avec son shader et les fonctions nécessaire pour faire fonctionner mon effet.
D'abord dans le constructeur de mon effet :
vp = new GLProgram(this->m_ClassName + "-Main", GL_VERTEX_SHADER);
fp = new GLProgram(this->m_ClassName + "-Main", GL_FRAGMENT_SHADER);
m_ProgramPipeline->useProgramStage(GL_VERTEX_SHADER_BIT, vp);
m_ProgramPipeline->useProgramStage(GL_FRAGMENT_SHADER_BIT, fp);
m_ProgramPipeline->link();
m_ProgramPipeline->bind();
Dans le constructeur de mon effet
Ainsi que dans une fonction apply :
in->getColorTexture(0)->bind();
m_ProgramPipeline->bind();
quad->drawGeometry(GL_TRIANGLES);
m_ProgramPipeline->release();
in->getColorTexture(0)->release();
Dans la fonction apply de mon effet
Pour le reste j'ai rajouter les fonctions FBO vu en cours dans mon Engine. En ce qui concerne le shader j'initialise la couleur a bleu, et je lui applique la fonction de jour nuit.
Rendu de la caméra sous l'eau le jour
Rendu de la caméra sous l'eau la nuit
float v = (sin(time/4000.0)+1.2)/2.2;
Color = vec4(v*vec3(0.0,0.65,0.9),0.7);
Fragment shader de l'effet
Conclusion
J'ai réussi à implémenter la plupart des fonctionnalités que je souhaitais à la base implémenter sur mon application. Avec un peu plus de temps j'aurai bien aimé programmer une fonction de lumière autre que Phong, j'aurai aussi voulu réaliser une distorsion de l'image quand la caméra passe sous l'eau à l'aide d'un noise.