123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- Financé par un concours d'innovation, VideoLabs avait démarré un projet de
- cinéma virtuel dans VLC, avec notamment le support multiplate-forme de plusieurs
- casques de réalité virtuelle. J'ai eu l'occasion d'y participer afin d'améliorer
- le moteur de rendu, maintenir le code et tenter d'optimiser le moteur de rendu
- pour le faire fonctionner sur des laptops sans chipset graphique dédié.
- \section{Fonctionnement}
- Le cinéma 3D est fait pour fonctionner dans un contexte de réalité virtuelle, la
- latence d'affichage a donc été un critère crucial pour pouvoir fournir une bonne
- expérience à l'utilisateur. Afin de la minimizer, l'ensemble de la partie rendu
- fonctionne dans le module manipulant la sortie vidéo OpenGL. Cela permet
- d'effectuer le rendu final de la scène sur la surface de rendu au lieu de mettre
- l'image rendue dans la file d'image à afficher puis attendre qu'elle soit
- sélectionnée.
- Ce module de rendu est un module de type \og{}video\_output\fg{} s'occupant des détails
- liés à opengl, qu'on raccourcira ici pour \og{}vout\fg{}. Le code qui nous concerne sera
- principalement situé dans le fichier
- \texttt{modules/video\_output/opengl/vout\_helper.c}.
- \section{Shaders}
- Étant donné que le rendu s'effectue via OpenGL, le pipeline de rendu n'est pas
- un pipeline fixe et il faut alors écrire au moins deux programmes spéciaux pour
- correctement le définir: un vertex shader et un fragment shader.
- % TODO; expliquer fonctionnement du pipeline
- Le vertex shader s'exécute sur chaque vertex et permet de définir de nouvelles
- valeurs qui pourront être réutilisées en entrée par les fragments shaders. En
- particulier, les transformations de l'espace de coordonnées objet à l'espace de
- coordonnées monde s'effectuent dans ce programme.
- Le fragment shader s'exécute pour chaque pixel à afficher et peut définir des
- règles d'interpolation sur les paramètres qu'il récupère depuis le vertex
- shader.
- On peut retrouver le code du fragment shader dans
- \texttt{modules/video\_output/opengl/fragment\_shader.c}, tandis que le code du
- vertex shader sera dans \texttt{modules/video\_output/opengl/vout\_helper.c}.
- Le travail d'amélioration du rendu a consisté à modifier le fragment shader, le
- vertex shader ansi que le code générant les commandes de dessin pour le GPU.
- L'algorithme d'illumination qui a été implémenté est Blinn-Phong, pris depuis le
- livre \textit{More Opengl Game Programming}\cite{Karayanis:2005:MOG:1051376}. En
- particulier, il s'agissait ici d'un algorithme de Forward rendering en une seule
- passe, c'est-à-dire que l'ensemble des lumières était rendues dans le fragment
- shader pour chaque objet affiché.
- La caractéristique de Blinn-Phong est de prendre en compte des directions
- supplémentaires pendant le rendu: ici il n'est pas complet, la contribution
- spéculaire étant absente.
- \begin{code}{c}{Extrait du fragment shader concernant Blinn-Phong}
- result = vec4(ambient * SceneAmbient, 1.f);
- for(int i=0; i<5; ++i) {
- vec3 light_pos_world= Lights.Position[i];
- vec3 light_to_object = Position_world - light_pos_world;
- vec3 spot_dir = normalize(Lights.Direction[i]);
- float distance = length(light_to_object);
- float attenuation = Lights.Kq[i] * distance * distance;
- vec3 light_dir = light_to_object / distance;
- vec3 light_diffuse = Lights.Diffuse[i];
- float light_spot_dot = dot(light_dir, spot_dir);
- vec3 light_contribution =
- 1 + max(0, dot(-light_dir, normal_world))
- * light_diffuse * diffuse / attenuation;
- result.xyz += light_spot_dot * light_contribution/2;
- }
- \end{code}
- \section{Amélioration des performances}
- La modification précédente a eu un impact très négatif sur les performances pour
- les laptops les moins équipés. Sur mon ordinateur de travail, je suis ainsi
- passé de 200 images par secondes à seulement une quinzaine.
- J'ai alors utilisé \inltype{apitrace} pour mieux mesurer ce qui posait problème,
- révélant sans surprise qu'il s'agissait du fragment shader. Un simple
- redimensionnement de l'écran permet ainsi de gagner en performance.
- J'ai donc essayé plusieurs techniques, chacune n'apportant que peu de bénéfice
- \begin{itemize}
- \item L'ajout d'algorithmes de frustrum culling,
- \cite{Lengyel:2011:MGP:2031513} qui n'a pas eu que très peu
- d'impact. En effet, les fragments en dehors de l'écran ne sont déjà pas pris
- en compte par le fragment shader, mais cela confirme que l'ensemble des
- ressources est monopolisé par ce fragment shader.
- \item L'utilisation d'étape de early depth, où la géométrie est rendue sans
- texture pour remplir le tampon de profondeur, puis la scène réelle est
- rendue en éliminant directement les fragments ne validant pas le test de
- profondeur.
- \item Un début de tentative de rendu déféré, ayant les mêmes avantages que
- le early depth, mais annulé devant le manque de gain de performance avec la
- méthode précédente.
- \end{itemize}
- Malheureusement aucune n'a permis de résoudre le problème de performance, bien
- qu'il soit déjà intuitivement difficile de faire fonctionner un affichage de
- réalité virtuelle sur une station mal équipée.
- % TODO: expliquer chargement de texture comme paramètre pour le fragment shader
- % TODO: expliquer Blinn-Phong
- % TODO: expliquer chargement des textures et modèles
- % TODO: expliquer débogage (normal, position, apitrace, etc)
- % TODO: expliquer travail effectué au début
- % TODO: expliquer fonctionnement HMD
|