cinema.tex 5.4 KB

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