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
