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