2015-07-09 16:48:04 +02:00
|
|
|
\documentclass{scrartcl}
|
|
|
|
\usepackage[utf8x]{inputenc}
|
|
|
|
\usepackage[frenchb]{babel}
|
|
|
|
\usepackage{amssymb}
|
|
|
|
\usepackage{lmodern}
|
|
|
|
\usepackage[T1]{fontenc}
|
|
|
|
\usepackage{verbatim}
|
|
|
|
\usepackage{ucs}
|
|
|
|
\usepackage{graphicx}
|
|
|
|
\usepackage{amsmath}
|
|
|
|
\usepackage{multicol}
|
|
|
|
\usepackage[hidelinks]{hyperref}
|
|
|
|
\usepackage{cpp}
|
|
|
|
\usepackage{tikz}
|
|
|
|
\usepackage{pgfplots}
|
|
|
|
\usepackage{subcaption}
|
|
|
|
\lstset{language=matlab}
|
|
|
|
|
|
|
|
\setlength{\hoffset}{-18pt}
|
|
|
|
\setlength{\oddsidemargin}{0pt} % Marge gauche sur pages impaires
|
|
|
|
\setlength{\evensidemargin}{9pt} % Marge gauche sur pages paires
|
|
|
|
\setlength{\marginparwidth}{54pt} % Largeur de note dans la marge
|
|
|
|
\setlength{\textwidth}{481pt} % Largeur de la zone de texte (17cm)
|
|
|
|
\setlength{\voffset}{-18pt} % Bon pour DOS
|
|
|
|
\setlength{\marginparsep}{7pt} % Séparation de la marge
|
|
|
|
\setlength{\topmargin}{0pt} % Pas de marge en haut
|
|
|
|
\setlength{\headheight}{13pt} % Haut de page
|
|
|
|
\setlength{\headsep}{10pt} % Entre le haut de page et le texte
|
|
|
|
\setlength{\footskip}{27pt} % Bas de page + séparation
|
|
|
|
\setlength{\textheight}{708pt} % Hauteur de la zone de texte (25cm)
|
|
|
|
|
|
|
|
\usepackage{titling}
|
|
|
|
\usepackage{fancyhdr}
|
|
|
|
\pagestyle{fancy}
|
|
|
|
\renewcommand{\headrulewidth}{1pt}
|
|
|
|
\renewcommand{\footrulewidth}{\headrulewidth}
|
|
|
|
\usepackage{float}
|
|
|
|
\newcommand{\hsp}{\hspace{20pt}}
|
|
|
|
\newcommand{\HRule}{\rule{\linewidth}{0.5mm}}
|
|
|
|
\fancyfoot[RO]{\thepage/\pageref*{end}}
|
2015-08-31 16:56:01 +02:00
|
|
|
\fancyfoot[C]{Mars 2015 --- Septembre 2015}
|
2015-07-09 16:48:04 +02:00
|
|
|
\fancyfoot[L]{\emph{Rapport de stage}}
|
|
|
|
|
|
|
|
\lstset{columns=fixed,basewidth=.5em,}
|
2015-08-31 16:56:01 +02:00
|
|
|
\newcommand{\hsc}[1]{{\Large\MakeUppercase{#1}}}
|
|
|
|
\newcommand{\hscs}[1]{{\footnotesize\MakeUppercase{#1}}}
|
2015-07-09 16:48:04 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\newcommand{\threejs}{\href{http://threejs.org/}{Three.js}}
|
|
|
|
\newcommand{\socketio}{\href{http://socket.io/}{Socket.IO}}
|
|
|
|
|
2015-09-05 23:43:05 +02:00
|
|
|
\newcommand{\namedparagraph}[1]{\paragraph{#1}\mbox{}\\}
|
|
|
|
|
|
|
|
\newcommand{\tikzvline}[2]{
|
|
|
|
\draw (#1,-6) -- (#1,-5.5);
|
|
|
|
\draw (#1,-6) node[below]{#2};
|
|
|
|
}
|
|
|
|
|
2015-07-09 16:48:04 +02:00
|
|
|
\begin{document}
|
|
|
|
|
|
|
|
\begin{titlepage}
|
|
|
|
\begin{sffamily}
|
|
|
|
\begin{center}
|
|
|
|
~\\[2cm]
|
2015-08-31 16:56:01 +02:00
|
|
|
\LARGE R\hsc{apport de Stage de 3A}\\[3cm]
|
2015-07-09 16:48:04 +02:00
|
|
|
|
|
|
|
% Title
|
|
|
|
\HRule \\[0.4cm]
|
|
|
|
{ \huge \bfseries Prédiction du comportement des utilisateurs d'une
|
|
|
|
application interactive en 3D \\[0.4cm] }
|
|
|
|
|
|
|
|
\HRule \\[3cm]
|
|
|
|
|
|
|
|
\includegraphics[scale=0.5]{img/icons/n7.png}~\\[4cm]
|
|
|
|
|
|
|
|
|
|
|
|
% Author and supervisor
|
|
|
|
\begin{minipage}{0.4\textwidth}
|
|
|
|
\begin{flushleft} \large
|
2015-08-31 16:56:01 +02:00
|
|
|
Thomas F\hscs{orgione}\\
|
2015-07-09 16:48:04 +02:00
|
|
|
3IN\\
|
|
|
|
\end{flushleft}
|
|
|
|
\end{minipage}
|
|
|
|
\begin{minipage}{0.4\textwidth}
|
|
|
|
\begin{flushright} \large
|
2015-08-31 16:56:01 +02:00
|
|
|
\emph{Tuteur :} M. Vincent C\hscs{harvillat}
|
2015-07-09 16:48:04 +02:00
|
|
|
\end{flushright}
|
|
|
|
\end{minipage}
|
|
|
|
|
|
|
|
\vfill
|
|
|
|
|
|
|
|
% Bottom of the page
|
|
|
|
{\large 16 Mars 2015 --- 25 Septembre 2015}
|
|
|
|
|
|
|
|
|
|
|
|
\end{center}
|
|
|
|
\end{sffamily}
|
|
|
|
\end{titlepage}
|
|
|
|
|
2015-08-31 16:56:01 +02:00
|
|
|
\normalsize
|
2015-09-06 16:36:14 +02:00
|
|
|
|
2015-07-09 16:48:04 +02:00
|
|
|
\tableofcontents
|
|
|
|
\newpage
|
|
|
|
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\part*{Introduction}
|
|
|
|
\newpage
|
|
|
|
|
|
|
|
\part{Choix des technologies et prise en main}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
|
|
|
La première phase de stage était de choisir les technologies qui allaient être
|
|
|
|
utilisées par la suite. Nous cherchions des technologies permettant la
|
|
|
|
visualisation 3D sur un navigateur web afin de pouvoir faire une étude
|
|
|
|
utilisateur simplement.
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\section{Côté client}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
|
|
|
Pour le côté client, il y avait plusieurs possibilités :
|
|
|
|
\begin{itemize}
|
2015-09-01 10:11:06 +02:00
|
|
|
\item WebGL, la spécification des fonctions permettant la 3D dans le
|
|
|
|
navigateur
|
2015-08-31 16:56:01 +02:00
|
|
|
\item Une librairie facilitant l'utilisation de WebGL
|
|
|
|
\item Du code C++ compilé en JavaScript grâce à Emscripten
|
|
|
|
\item N'importe quel moteur graphique qui puisse exporter vers JavaScript
|
|
|
|
\end{itemize}
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
La plupart des moteurs graphiques exportant vers JavaScript sont putôt lourd à
|
|
|
|
prendre en main, et nous voulions garder des solutions simples, c'est pourquoi
|
2015-09-06 16:36:14 +02:00
|
|
|
nous avons utilisé une librairie libre nommée \threejs permettant une
|
2015-08-31 16:56:01 +02:00
|
|
|
utilisation facile de WebGL.
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\paragraph{}
|
|
|
|
Pour des raisons de simplicité, nous avons décidé de développer le code client
|
|
|
|
pour Google Chrome et Firefox, les autres navigateurs ne sont donc pas
|
|
|
|
(officiellement) supportés.
|
|
|
|
|
|
|
|
\section{Côté serveur}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
|
|
|
Dans un premier temps, seul le côté client était pris en compte. Les programmes
|
|
|
|
étaient écrits en JavaScript et ne nécessitaient pas de serveur. Quand les
|
|
|
|
problématiques de dynamicité sont arrivées, il a fallu choisir une technologie
|
|
|
|
pour le côté serveur, et là, tous les langages étaient possibles.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Plusieurs langages et framework ont été téstés. Quand les problématiques
|
|
|
|
étaient encore simples (passage d'un paramètre dans une requête), on a commencé
|
|
|
|
par utiliser le php, puis on s'est tourné vers des scripts CGI en python. Quand
|
|
|
|
de plus nombreuses pages ont été nécessaires, on a commencé à chercher un vrai
|
|
|
|
framework, et on s'est penché sur Django (framework web pour Python) qui est
|
|
|
|
très pratique mais assez coûteux en mémoire vive (le serveur était alors
|
|
|
|
herbergé sur une petite machine de 512Mo de RAM).
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Quand les problématiques de streaming ont commencé à apparaître, nous avons
|
|
|
|
choisi la simplicité en utilisant Node.js pour le côté serveur (un serveur
|
2015-09-06 16:36:14 +02:00
|
|
|
écrit en JavaScript) à cause de la présence d'une librairie nommée \socketio
|
2015-08-31 16:56:01 +02:00
|
|
|
qui s'avère très pratique pour la communication entre le client et le serveur.
|
|
|
|
Pour des raisons pratiques, le serveur a été herbergé sur un cloud gratuit
|
|
|
|
(OpenShift).
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\section{Base de données}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
|
|
|
Pour le système de gestion de base de données, nous avons choisi Postgres (qui
|
|
|
|
est libre et qui a largement fait ses preuves). OpenShift propose d'héberger
|
|
|
|
lui-même la base de données, mais la version gratuite ne proposant qu'1 Go
|
|
|
|
d'espace de stockage, nous avons préféré l'héberger nous-même.
|
2015-07-09 16:48:04 +02:00
|
|
|
|
2015-09-05 23:43:05 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\section{Développement, debug et déploiement}
|
2015-09-05 23:43:05 +02:00
|
|
|
\paragraph{}
|
|
|
|
Pour éviter d'avoir des fichiers trop longs, nous avons choisi de séparer les
|
|
|
|
sources dans de nombreux fichiers de taille plus petite, et de les fusionner
|
|
|
|
automatiquement. Pour le développement, ils seront simplement concaténés grâce
|
|
|
|
à un script développé spécialement pour cela, qui mime les paramètres de
|
|
|
|
Closure Compiler qui sera utilisé pour la fusion au moment du le déploiement
|
|
|
|
(ce dernier permet non seulement la fusion des fichiers mais aussi la
|
|
|
|
minification\footnote{la minification sert notamment à réduire la taille du
|
|
|
|
script : n'oublions pas que nous parlons de serveur web, et il est donc
|
|
|
|
intéressant de réduire la taille des programmes de sorte à les charger plus
|
|
|
|
rapidement} (effacement des commentaires et des retours à la ligne,
|
|
|
|
simplifications des noms de variables et plus \footnote{en JavaScript, il est
|
2015-09-06 16:36:14 +02:00
|
|
|
plus court d'écrire \texttt{!0} pour \texttt{true} par exemple.}). Pour le
|
2015-09-05 23:43:05 +02:00
|
|
|
développement, on a utilisé \href{https://github.com/remy/nodemon}{nodemon} et
|
|
|
|
inotify, qui permettent de relancer le serveur local lorsqu'une modification
|
|
|
|
est détectée (la fusion des fichiers est donc réeffectuée).
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
En ce qui concerne le versionnage des fichiers, nous avons utilisé Git avec
|
|
|
|
deux \emph{repositories} :
|
|
|
|
\begin{itemize}
|
|
|
|
\item le premier, hébergé sur
|
|
|
|
\href{https://github.com/tforgione/3dinterface}{Github}, sert au
|
|
|
|
développement, et contient les fichiers fractionnés ainsi que les
|
|
|
|
outils permettant la génération des fichiers fusionnés.
|
|
|
|
\item le deuxième, hebergé chez OpenShift, qui contient la version finale
|
|
|
|
du programme, permet de déployer le code du serveur quand les
|
|
|
|
\emph{commits} sont \emph{pushés}.
|
|
|
|
\end{itemize}
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Pour nous aider au debug, nous avons utilisé \href{http://jshint.com/}{JSHint}
|
|
|
|
qui nous aide à détecter les erreurs potentielles liées aux subtilités du
|
|
|
|
langage.
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\newpage
|
|
|
|
\part{L'interface}
|
|
|
|
\section{Interactions élémentaires}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
|
|
|
La première interface a été pensée pour être la plus simple possible.
|
|
|
|
L'utilisateur contrôle une caméra qui se déplace librement dans un modèle 3D.
|
|
|
|
|
|
|
|
\paragraph{}
|
2015-09-01 10:11:06 +02:00
|
|
|
La translation de la caméra est contrôlée par le clavier : les touches Z, Q, S,
|
2015-08-31 16:56:01 +02:00
|
|
|
et D servent respectivement à avancer, aller à gauche, reculer et aller à
|
|
|
|
droite (de même que les touches fléchées).
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
On peut pivoter la caméra de plusieurs manières :
|
|
|
|
\begin{itemize}
|
2015-09-01 10:11:06 +02:00
|
|
|
\item via le pavé numérique (2, 4, 6, et 8 pour tourner respectivement vers
|
|
|
|
le bas, vers la gauche, vers la droite et vers le haut)
|
|
|
|
\item via la souris, comme \emph{drag-n-drop}, en cliquant un point de la
|
|
|
|
scène et en le déplaçant
|
|
|
|
\item via la souris, en mode \emph{pointer-lock}, comme dans un jeu video
|
|
|
|
de tir
|
2015-08-31 16:56:01 +02:00
|
|
|
\end{itemize}
|
|
|
|
|
2015-09-05 23:43:05 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\section{Les recommandations}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
|
|
|
Les recommandations sont là pour suggérer des points de vue à l'utilisateur.
|
|
|
|
Elles permettent d'aider la navigation. Elles sont affichées sous forme
|
|
|
|
d'objets 3D ajoutés à la scène. Deux affichages ont été testés.
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
|
|
|
|
\subsection{Les \emph{viewports}}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
|
|
|
Les \emph{viewports} sont les affichages les plus simples : ils représentent
|
|
|
|
une caméra, avec son centre optique et son plan image.
|
|
|
|
\begin{figure}[H]
|
|
|
|
\centering
|
2015-09-03 17:43:35 +02:00
|
|
|
\includegraphics[scale=0.275]{img/new/01.png}
|
|
|
|
\caption{Une recommandation \emph{viewport}}
|
2015-08-31 16:56:01 +02:00
|
|
|
\end{figure}
|
2015-09-03 17:43:35 +02:00
|
|
|
Cette façon d'afficher une recommandation a l'avantage d'être simple, de ne pas
|
|
|
|
beaucoup masquer le reste des modèles et suggère assez bien l'idée d'un
|
|
|
|
\emph{point de vue recommandé}, mais elle a l'inconvéniant d'être ambigüe à
|
|
|
|
cause de la perspective (dans cette image, il peut être difficile de savoir si
|
|
|
|
le point de vue et vers le modèle ou vers nous).
|
2015-08-31 16:56:01 +02:00
|
|
|
|
2015-09-05 23:43:05 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\subsection{Les flèches}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
|
|
|
Les flèches sont supposées être plus intuitives pour un utilisateur qui n'a pas
|
|
|
|
l'habitude des \emph{viewports} précédemment utilisés. Plutôt que de suggérer
|
|
|
|
un point de vue, elles suggèrent le mouvemement qui va mener à ce point de vue.
|
|
|
|
\begin{figure}[H]
|
|
|
|
\centering
|
2015-09-03 17:43:35 +02:00
|
|
|
\includegraphics[scale=0.275]{img/new/02.png}
|
|
|
|
\caption{Des recommandations flèches}
|
2015-08-31 16:56:01 +02:00
|
|
|
\end{figure}
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\subsection{Les interactions}
|
|
|
|
\subsubsection{Au survol}
|
2015-09-05 23:43:05 +02:00
|
|
|
\indent Cette fonctionnalité est inspirée des récents lecteurs video sur le
|
|
|
|
web. Lorsque l'on regarde une video, on a la barre de \emph{seeking} en bas et
|
2015-08-31 16:56:01 +02:00
|
|
|
passer le curseur sur cette barre affiche l'image de la vidéo à l'instant visé.
|
2015-09-03 17:43:35 +02:00
|
|
|
Nous avons simplement adapté cette techniques à nos recommandations : lorsque
|
|
|
|
le curseur survole une recommandation, une prévisualisation est affichée dans
|
2015-08-31 16:56:01 +02:00
|
|
|
une petite boite au voisinage du curseur.
|
2015-09-05 23:43:05 +02:00
|
|
|
|
|
|
|
\begin{figure}[H] \centering
|
2015-09-03 17:43:35 +02:00
|
|
|
\includegraphics[scale=0.275]{img/new/03.png}
|
2015-08-31 16:56:01 +02:00
|
|
|
\caption{Une prévisualisation}
|
|
|
|
\end{figure}
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\subsubsection{Au clic}
|
2015-09-05 23:43:05 +02:00
|
|
|
\indent Lors d'un clic sur une recommandation, la caméra suit un mouvement
|
|
|
|
fluide jusqu'au point de vue recommandé. La trajectoire est définié par un
|
|
|
|
polynôme interpolant tel que :
|
2015-09-03 17:43:35 +02:00
|
|
|
\begin{itemize}
|
|
|
|
\item la position initiale est la position de la caméra
|
|
|
|
\item la position finale est la position de la recommandation
|
|
|
|
\item la dérivée de la trajectoire à l'instant final est la direction de la
|
|
|
|
recommandation
|
|
|
|
\end{itemize}
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Ce mouvement fluide est là pour ne pas perturber l'utilisateur qui pourrait
|
|
|
|
\emph{se perde} si jamais il était téléporté
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
De plus, les recommandations se comportent comme des liens hypertextes : elles
|
2015-09-05 23:43:05 +02:00
|
|
|
sont bleues si elles n'ont jamais été clicées, et deviennent violettes si
|
2015-09-03 17:43:35 +02:00
|
|
|
l'utilisateur les a déjà consommées. Ceci est fait pour qu'un utilisateur
|
|
|
|
puisse savoir par où il est passé, et ce qui lui reste encore à visiter.
|
2015-09-01 10:11:06 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
|
|
|
|
\section{Autres éléments de navigation}
|
2015-09-05 23:43:05 +02:00
|
|
|
\paragraph{}
|
|
|
|
Pour faciliter la navigation, quelques autres éléments de navigation sont
|
|
|
|
présents.
|
|
|
|
|
|
|
|
\begin{figure}[H]
|
|
|
|
\centering
|
|
|
|
\begin{tikzpicture}[]
|
|
|
|
\node (myfirstpic) at (0,0) {\includegraphics[scale=0.35]{img/new/buttons.png}};
|
|
|
|
\tikzvline{-7}{1}
|
|
|
|
\tikzvline{-5.8}{2}
|
|
|
|
\tikzvline{-5.2}{3}
|
|
|
|
\tikzvline{-4}{4}
|
|
|
|
\tikzvline{-1.5}{5}
|
|
|
|
|
|
|
|
\draw (8,5) -- (7,5);
|
|
|
|
\draw (8,5) node[right]{6};
|
|
|
|
|
|
|
|
\draw (-8,5) -- (-7,5);
|
|
|
|
\draw (-8,5) node[left]{7};
|
|
|
|
\end{tikzpicture}
|
|
|
|
\caption{Les différents éléments de l'interface}
|
|
|
|
\end{figure}
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\newpage
|
2015-09-05 23:43:05 +02:00
|
|
|
\paragraph{}
|
|
|
|
\begin{enumerate}
|
|
|
|
|
|
|
|
\item \emph{Reset camera} : pour chaque scène, une position initiale est
|
|
|
|
définie. Cliquer sur ce bouton ramène la caméra à sa position initiale.
|
|
|
|
|
|
|
|
\item \emph{Previous} : à chaque clic sur une recommandation, les positions
|
|
|
|
intiales et finales sont sauvegardées. Cliquer sur ce bouton ramène à
|
|
|
|
la position précédente.
|
|
|
|
|
|
|
|
\item \emph{Next} : cliquer sur ce bouton ramène à la position suivante.
|
|
|
|
|
|
|
|
\item \emph{Pointer lock} : permet de passer du mode \emph{pointer-lock} au
|
|
|
|
mode \emph{drag-n-drop} et vice-versa.
|
|
|
|
|
|
|
|
\item \emph{Music} : un lecteur qui contrôle une petite musique qui permet
|
|
|
|
de se mettre dans l'ambiance de la scène.
|
|
|
|
|
|
|
|
\item \emph{Coin gauge} : une jauge qui représente l'avancement de la
|
|
|
|
récupération des pièces.
|
|
|
|
|
|
|
|
\item \emph{FPS counter} : indique la période de rafraîchissement du rendu.
|
|
|
|
|
|
|
|
\end{enumerate}
|
|
|
|
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\newpage
|
|
|
|
\part{L'étude utilisateur}
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
2015-09-03 17:43:35 +02:00
|
|
|
Pour tester le comportement des utilisateurs face aux recommandations, nous
|
2015-08-31 16:56:01 +02:00
|
|
|
avons dissimulé des pièces rouges à travers ces modèles, et nous avons demandé
|
|
|
|
à des utilisateurs de les trouver.
|
|
|
|
\begin{figure}[H]
|
|
|
|
\centering
|
2015-09-03 17:43:35 +02:00
|
|
|
\includegraphics[scale=0.275]{img/new/04.png}
|
|
|
|
\caption{Une pièce rouge}
|
2015-08-31 16:56:01 +02:00
|
|
|
\end{figure}
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Pour éviter la dépendance entre les recomendations et les pièces rouges (si les
|
2015-09-03 17:43:35 +02:00
|
|
|
recommandations visent les pièces rouges, il est évident qu'il sera très facile
|
|
|
|
de les trouver avec les recommandations), un système de tirage aléatoire de
|
2015-08-31 16:56:01 +02:00
|
|
|
pièces rouges a été fait.
|
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\section{Déroulement de l'expérience}
|
|
|
|
\subsection{Première page}
|
|
|
|
La première page présente rapidement l'expérience. Elle vérifie aussi le
|
|
|
|
navigateur : si le client est sur Google Chrome ou Firefox, un lien apparaîtra
|
|
|
|
pour passer à la suite, sinon, un message d'erreur s'affichera.
|
|
|
|
|
|
|
|
\subsection{Identification}
|
|
|
|
Cette page nous permet d'en savoir un peu plus sur l'utilisateur : nous allons
|
|
|
|
demander l'age, le sexe et les habitudes en terme de jeux video de
|
|
|
|
l'utilisateur (nous avons considéré que la capacité des utilisateurs à manier
|
|
|
|
notre interface allait dépendre fortement de leur habitude aux jeux video).
|
|
|
|
Nous leur demandons notamment de noter leurs capacités en terme de jeux videos
|
|
|
|
(entre 1 et 5 étoiles).
|
|
|
|
|
|
|
|
\subsection{Tutoriel}
|
|
|
|
Ensuite, nous demandons à l'utilisateur de suivre un tutoriel : c'est une sorte
|
|
|
|
de réelle expérience mais guidée. Des messages indiquant les interactions à
|
|
|
|
faire aideront l'utilisateur à s'habituer à cette interface. On y présente les
|
|
|
|
moyens de déplacer la caméra, puis les recommandations et les différents
|
|
|
|
boutons de l'interface.
|
|
|
|
|
|
|
|
|
|
|
|
\subsection{Les trois vraies expériences}
|
|
|
|
C'est ensuite que la \emph{vraie} partie de l'étude utilisateur commence : à 3
|
|
|
|
reprises, l'utilisateur va se retrouver dans une scène avec certaines pièces
|
|
|
|
rouges à trouver, et un certain style de recommandations\footnote{aucune
|
|
|
|
recommandation sera considéré comme un style de recommandation, de sorte à
|
|
|
|
comparer la présence à l'absence de recommandation pour la navigation} pour
|
|
|
|
l'aider. Nous expliquerons dans la sous-section \ref{selection} comment le
|
|
|
|
choix de la scène et du style de recommandation sera fait.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Dans chacune des expériences, l'utilisateur devra chercher des pièces rouges,
|
|
|
|
en s'aidant (ou pas) des recommandations. L'expérience se terminera soit quand
|
|
|
|
l'utilisateur aura trouvé les huit pièces rouges, soit une minute après avoir
|
|
|
|
trouvé la 6\up{ème} pièce rouge. Pour éviter que l'utilisateur soit
|
|
|
|
\emph{frustré} de ne pas avoir trouvé toutes les pièces, nous n'indiquons pas
|
|
|
|
clairement le nombre de pièces qu'il a, ou qu'il lui reste à trouver.
|
|
|
|
Simplement, une \emph{vague} idée de sa progression.
|
|
|
|
|
|
|
|
\subsection{Le \emph{feedback}}
|
|
|
|
Après avoir fini ces expériences, l'utilisateur se retrouvera sur un formulaire
|
|
|
|
lui demandant son avis quand à la difficulté de l'interface, et l'utilité des
|
|
|
|
recommandations pour se déplacer dans la scène.
|
|
|
|
|
|
|
|
\section{Choix de la scène et du style de recommandations\label{selection}}
|
|
|
|
\paragraph{}
|
|
|
|
Il y a trois scènes disponibles, et sur chaque scène, nous avons placé des
|
|
|
|
pièces dans de nombreuses positions. Lorsqu'un utilisateur commence une
|
|
|
|
expérience, l'algorithme de selection fonctionne de façon à faire des
|
|
|
|
expériences sur les mêmes scènes avec les mêmes dispositions de pièces mais des
|
|
|
|
styles de recommandations différents pour des utilisateurs de même niveau de
|
|
|
|
sorte à pouvoir comparer l'efficacité des recommandations.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Nous cherchons en fait des expériences qui permettraient de compléter les trio
|
|
|
|
d'expériences avec des pièces identiques sur des scènes identiques mais avec
|
|
|
|
des styles de recomendations différents. Si une telle expérience n'existe pas
|
|
|
|
(les trios sont déjà complets, ou bien il n'existe pas d'expérience pour un
|
|
|
|
niveau d'utilisateur donné), la scène, ainsi que les pièces à trouver et le
|
|
|
|
style de recommandations seront choisi aléatoirement : on choisira une scène et
|
|
|
|
un style de recommandations que l'utilisateur n'a pas encore effectué, et on
|
|
|
|
choisira 8 pièces aléatoirement parmi les positions possibles que nous avons
|
|
|
|
fixées au préalable.
|
|
|
|
|
|
|
|
\section{Positions possibles des pièces}
|
|
|
|
Pour choisir les positions possibles des pièces dans chaque scène, un petit
|
|
|
|
outil à été développé permettant de se déplacer dans une scène et des créer des
|
|
|
|
pièces en cliquant. Cliquer sur une paroie de la scène crée une pièce devant
|
|
|
|
cette paroie, et cliquer sur une pièce la supprime. Un bouton permet d'envoyer
|
|
|
|
la liste des pièces par mail, lorsque l'on a fini de créer des pièces.
|
|
|
|
|
|
|
|
|
|
|
|
\newpage
|
|
|
|
\part{Streaming de modèle 3D}
|
|
|
|
Le but ultime de ce projet est de biaiser l'utilisateur avec les
|
|
|
|
recommandations de sorte à être capable de prévoir ses déplacements futurs, et
|
|
|
|
ainsi précharger les parties du modèle qui vont être vues. Cette section
|
|
|
|
présente le travail qui a été réalisé dans le domaine du chargement de modèle.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Évidemment, cette partie est celle qui comment après la fin de la première
|
|
|
|
partie : il faut nous seulement connaître l'influence des recommandations sur
|
|
|
|
l'utilisateur, ensuite être capable de prévoir le comportement de
|
|
|
|
l'utilisateur, et enfin s'en servir pour précharger les bonnes parties du
|
|
|
|
modèles. Tout ceci n'étant pas encore possible, le travail qui a été fait est
|
|
|
|
nettement plus simpliste : il n'y aura aucune prévision du comportement ici.
|
|
|
|
|
|
|
|
\section*{Introduction}
|
|
|
|
Notre problématique ici est de transférer des modèles 3D sur le réseau. Les
|
|
|
|
modèles sont stockés sur le serveur au format \texttt{.obj} et sont constitués
|
|
|
|
:
|
|
|
|
\begin{itemize}
|
|
|
|
\item des materiaux (\texttt{usemtl}) : cela définit le matériau utilisé
|
|
|
|
pour les faces qui vont suivre
|
|
|
|
\item de sommets (\emph{vertices}) : des points 3D
|
|
|
|
\item de coordonnées de textures : des points en 2D qui référence un point
|
|
|
|
d'une image
|
|
|
|
\item de normales : des vecteurs en 3D
|
|
|
|
\item de faces : une liste de 3 ou 4 sommets (représentés par leurs
|
|
|
|
indices), avec éventuellement leurs coordonnées de textures et/ou
|
|
|
|
normales
|
|
|
|
\end{itemize}
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
La seule contrainte d'ordre des éléments est qu'une face qui contient des
|
|
|
|
sommets, coordonnées de textures ou normales n'arrive pas avant ses sommets,
|
|
|
|
coordonnées de textures ou normales dans le fichier : si l'on parcourt les
|
|
|
|
lignes du fichier, on doit déjà avoir toutes les informations sur la face.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Généralement, le fichier \texttt{.obj} vient souvent avec un fichier
|
|
|
|
\texttt{.mtl} qui contient la définition des matériaux (leurs noms, leurs
|
|
|
|
textures, leurs constantes...).
|
|
|
|
|
|
|
|
\newpage
|
|
|
|
\section{Streaming linéaire}
|
|
|
|
La première étape de cette fonctionnalité a été de faire un système
|
|
|
|
client-serveur permettant le streaming de modèle 3D. En effet, les
|
|
|
|
\emph{loaders} de modèles présents dans la libraire \threejs ne permettent pas
|
|
|
|
le chargement progressif : ils se content d'envoyer une requête vers le fichier
|
|
|
|
contenant le modèle et à créer un modèle une fois que le fichier est chargé
|
|
|
|
complètement.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Pour commencer, nous avons donc utilisé \socketio, une librairie permettant de
|
|
|
|
gérer les sockets facilement avec JavaScript et Nodejs, pour faire une première
|
|
|
|
version simpliste du streaming : on travaillait sur un modèle ne contenant que
|
|
|
|
des sommets et des faces (donc pas de textures ni de normales) et le protocole
|
|
|
|
fonctionnait ainsi :
|
|
|
|
|
|
|
|
\begin{figure}[H]
|
|
|
|
\centering
|
|
|
|
\begin{tikzpicture}[]
|
|
|
|
\draw (0,0) node[above]{Client};
|
|
|
|
\draw (0,0) -- (0,-6);
|
|
|
|
\draw (5,0) node[above]{Serveur};
|
|
|
|
\draw (5,0) -- (5,-6);
|
|
|
|
|
|
|
|
\draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle};
|
|
|
|
\draw[->] (0,-1) -- (5, -2);
|
|
|
|
|
|
|
|
\draw (2.5,-2.3) node[rotate=12] {Des éléments};
|
|
|
|
\draw[<-] (0,-3) -- (5, -2);
|
|
|
|
|
|
|
|
\draw (2.5,-3.3) node[rotate=-12] {ACK};
|
|
|
|
\draw[->] (0,-3) -- (5, -4);
|
|
|
|
|
|
|
|
\draw (2.5,-4.3) node[rotate=12] {D'autres éléments};
|
|
|
|
\draw[<-] (0,-5) -- (5, -4);
|
|
|
|
|
|
|
|
\draw (2.5,-6) node{$\vdots$};
|
|
|
|
\end{tikzpicture}
|
|
|
|
\caption{Transmission élémentaire\label{transmission1}}
|
|
|
|
\end{figure}
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Le serveur envoyait alors les éléments dans l'ordre dans lequel ils étaient
|
|
|
|
présents dans le fichier du modèle 3D. Le gros inconvéniant de cette méthode
|
|
|
|
est que souvent, les sommets sont présents au début du fichier, et les faces
|
|
|
|
vers la fin : nous recevons donc des informations de sommets au début qui ne
|
|
|
|
nous permettent rien d'afficher, puis toutes les faces d'un coup, ce qui fait
|
|
|
|
que le streaming n'est pas aussi progressif que l'on souhaiterais.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Dans le cas d'un modèle sans coordonnées de textures et sans normales, la seule
|
|
|
|
condition pour pouvoir afficher une face est d'avoir envoyé les sommets qui la
|
|
|
|
composent. On peut donc améliorer la fluidité en réarrangeant le fichier
|
|
|
|
\texttt{.obj} : il suffit de faire apparaître les faces dès que les sommets
|
|
|
|
sont disponibles.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Dans le cas où l'on souhaite gérer les textures et les normales, utiliser cette
|
|
|
|
technique est beaucoup plus compliqué puisqu'il faut aussi décider de l'ordre
|
|
|
|
relatif entre les sommets, coordonnées de textures et normales : en effet, pour
|
|
|
|
envoyer une face le plus tôt possible, il faut que toutes ces composantes
|
|
|
|
soient envoyés, et il est donc nécessaire de mélanger les sommets, coordonnées
|
|
|
|
de texture et normales.
|
|
|
|
|
|
|
|
\newpage
|
|
|
|
\section{Streaming linéaire amélioré}
|
|
|
|
La remarque précédente conduit directement à cette méthode. Le principe reste
|
|
|
|
le même que celui précédent (figure \ref{transmission1}), mais les éléments
|
|
|
|
seront envoyés différemment : on va en fait parcourir les faces directement.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
Le serveur va garder en mémoire ce qui a déjà été envoyé et ce qui ne l'a pas
|
|
|
|
été, et envoyer les faces dans l'ordre : si certains éléments des faces n'ont
|
|
|
|
pas encore été envoyé, on les enverra juste avant d'envoyer la face en
|
|
|
|
question.
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
On peut de cette façon à la fois gérer les coordonnées de texture et les
|
|
|
|
normales, tout en envoyant les faces le plus tôt possible (on peut aussi noter
|
|
|
|
que si certains sommets / coordonnées de textures / normales sont inutilisés
|
|
|
|
dans les faces, ils ne seront pas envoyés et on s'évite donc de transférer des
|
|
|
|
données inutiles).
|
|
|
|
|
|
|
|
\paragraph{}
|
|
|
|
C'est dans cette version que nous avons commencé à nous intéresser à la façon
|
|
|
|
de gérer les matériaux. Dans \threejs, un objet 3D est lié à un materiau, et
|
|
|
|
nous sommes donc obligés de créer autant d'objets que de materiaux. Pour cela,
|
|
|
|
nous avons légèrement modifié notre protocole :
|
|
|
|
|
|
|
|
\begin{figure}[H]
|
|
|
|
\centering
|
|
|
|
\begin{tikzpicture}[]
|
|
|
|
\draw (0,0) node[above]{Client};
|
|
|
|
\draw (0,0) -- (0,-8);
|
|
|
|
\draw (5,0) node[above]{Serveur};
|
|
|
|
\draw (5,0) -- (5,-8);
|
|
|
|
|
|
|
|
\draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle};
|
|
|
|
\draw[->] (0,-1) -- (5, -2);
|
|
|
|
|
|
|
|
\draw (2.5,-2.3) node[rotate=12] {Liste des matériaux};
|
|
|
|
\draw[<-] (0,-3) -- (5, -2);
|
|
|
|
|
|
|
|
\draw[dashed, ->] (-0.1,-3) -- (-0.1,-4);
|
|
|
|
|
|
|
|
\draw (2.5,-3.3) node[rotate=-12] {ACK};
|
|
|
|
\draw[->] (0,-3) -- (5, -4);
|
|
|
|
|
|
|
|
\draw (2.5,-4.3) node[rotate=12] {Des éléments};
|
|
|
|
\draw[<-] (0,-5) -- (5, -4);
|
|
|
|
|
|
|
|
\draw (2.5,-5.3) node[rotate=-12] {ACK};
|
|
|
|
\draw[->] (0,-5) -- (5, -6);
|
|
|
|
|
|
|
|
\draw (2.5,-6.3) node[rotate=12] {D'autres éléments};
|
|
|
|
\draw[<-] (0,-7) -- (5, -6);
|
|
|
|
|
|
|
|
\draw (2.5,-8) node{$\vdots$};
|
|
|
|
\end{tikzpicture}
|
|
|
|
\caption{Transmission avec gestion des matériaux}
|
|
|
|
\end{figure}
|
|
|
|
|
2015-08-31 16:56:01 +02:00
|
|
|
\paragraph{}
|
2015-09-06 16:36:14 +02:00
|
|
|
Les directives de matériau à utiliser (\texttt{usemtl}) seront ignorées, et les
|
|
|
|
faces seront envoyées avec l'indice de l'objet auquel elles appartiennent, ce
|
|
|
|
qui permettra au client de savoir dans quel objet (et donc avec quel matériau)
|
|
|
|
elles doivent être ajoutées.
|
2015-08-31 16:56:01 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\newpage
|
|
|
|
\section{Streaming intelligent}
|
|
|
|
C'est la dernière version du streaming qui a été faite sur ce projet. À chaque
|
|
|
|
transfert, le client envoie sa position au serveur (ainsi que les plans
|
|
|
|
définissant son \emph{frustum}\footnote{les bords du champ de vision de la
|
|
|
|
caméra}) et le serveur va parcourir les faces du modèle en cherchant celles qui
|
|
|
|
apparaissent dans le \emph{frustum}\footnote{dans cette version, on considère
|
|
|
|
qu'une face apparaît dans le \emph{frsutum} si un de ces sommets y appartient.
|
|
|
|
Évidemment, une face très grande pourrait appraître dans le \emph{frustum}
|
|
|
|
sans qu'aucun de ses sommets n'y soit, mais nous n'avons pas traité ce cas
|
|
|
|
particulier ici.}. On évite ainsi d'envoyer les faces du modèle qui sont
|
|
|
|
derrière la caméra et que l'utilisateur ne voit pas.
|
2015-09-05 23:43:05 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\paragraph{}
|
|
|
|
Bien sûr, si il n'y a plus de faces dans le \emph{frustum} de la caméra, on va
|
|
|
|
envoyer les faces en suivant la méthode de la section précédente.
|
2015-08-31 16:56:01 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\begin{figure}[H]
|
|
|
|
\centering
|
|
|
|
\begin{tikzpicture}[]
|
|
|
|
\draw (0,0) node[above]{Client};
|
|
|
|
\draw (0,0) -- (0,-8);
|
|
|
|
\draw (5,0) node[above]{Serveur};
|
|
|
|
\draw (5,0) -- (5,-8);
|
2015-09-05 23:43:05 +02:00
|
|
|
|
2015-09-06 16:36:14 +02:00
|
|
|
\draw (2.5,-1.3) node[rotate=-12] {Path jusqu'au modèle};
|
|
|
|
\draw[->] (0,-1) -- (5, -2);
|
|
|
|
|
|
|
|
\draw (2.5,-2.3) node[rotate=12] {Liste des matériaux};
|
|
|
|
\draw[<-] (0,-3) -- (5, -2);
|
|
|
|
|
|
|
|
\draw[dashed, ->] (-0.1,-3) -- (-0.1,-4);
|
|
|
|
|
|
|
|
\draw (2.5,-3.3) node[rotate=-12] {Caméra / \emph{frustum}};
|
|
|
|
\draw[->] (0,-3) -- (5, -4);
|
|
|
|
|
|
|
|
\draw (2.5,-4.3) node[rotate=12] {Des éléments};
|
|
|
|
\draw[<-] (0,-5) -- (5, -4);
|
|
|
|
|
|
|
|
\draw (2.5,-5.3) node[rotate=-12] {Caméra / \emph{frustum}};
|
|
|
|
\draw[->] (0,-5) -- (5, -6);
|
|
|
|
|
|
|
|
\draw (2.5,-6.3) node[rotate=12] {D'autres éléments};
|
|
|
|
\draw[<-] (0,-7) -- (5, -6);
|
|
|
|
|
|
|
|
\draw (2.5,-8) node{$\vdots$};
|
|
|
|
\end{tikzpicture}
|
|
|
|
\caption{Version finale}
|
|
|
|
\end{figure}
|
|
|
|
|
|
|
|
\newpage
|
|
|
|
\part*{Conclusion}
|
2015-07-09 16:48:04 +02:00
|
|
|
|
|
|
|
\label{end}\end{document}
|