This commit is contained in:
2019-10-02 11:32:47 +02:00
parent 947a32d970
commit a1676138c9
2 changed files with 99 additions and 30 deletions

View File

@@ -1,8 +1,10 @@
A 3D streaming system is a system that collects 3D data and dynamically renders it.
The previous chapter voluntarily remained vague about what \emph{3D data} actually is.
This chapter presents in detail what 3D data is and how it is reenderer, and give insights about interaction and streaming by comparing the 3D case to the video one.
\section{What is a 3D model?}
Before talking about 3D streaming, we need to define what is a 3D model and how it is rendered.
\subsection{Content of a 3D model}
\subsection{3D data}
A 3D model consists in a set of data.
\begin{itemize}
@@ -19,7 +21,7 @@ A 3D model encoded in the OBJ format typically consists in two files: the materi
\paragraph{}
The materials file declare all the materials that the object file will reference.
A material consists in name, and other photometric properties such as ambient, diffuse and specular colors, as well as texture maps.
Each face correspond to a material and a renderer can use the material's information to render the faces.
Each face correspond to a material and a renderer can use the material's information to render the faces in a specific way.
A simple material file is visible on Listing~\ref{i:mtl}.
\paragraph{}
@@ -63,26 +65,94 @@ An example of object file is visible on Listing~\ref{i:obj}.
\subsection{Rendering a 3D model\label{i:rendering}}
To be able to render a model, it is first required to send the data (vertices, textures coordinates, normals, faces and textures) to the GPU, and then only the rendering can be done.
Then, to render a 3D model, the objects from the model are traversed, the materials and textures are bound to the target on which the rendering will be done, and then, \texttt{glDrawArray} or \texttt{glDrawElements} function is called.
To understand how performance is impacted by the structure of the model, we need to realize two things:
A typical 3D renderer follows Algorithm~\ref{f:renderer}.
\newcommand\mycommfont[1]{\footnotesize\ttfamily\textcolor{blue}{#1}}
\SetCommentSty{mycommfont}
\SetNoFillComment%
\begin{algorithm}[th]
\SetKwData{Texture}{texture}
\SetKwData{Object}{object}
\SetKwData{Geometry}{geometry}
\SetKwData{Textures}{all\_textures}
\SetKwData{Object}{object}
\SetKwData{Scene}{scene}
\SetKwData{True}{true}
\SetKwFunction{LoadGeometry}{load\_geometry}
\SetKwFunction{LoadTexture}{load\_texture}
\SetKwFunction{BindTexture}{bind\_texture}
\SetKwFunction{Draw}{draw}
\tcc{Initialization}
\For{\Object\in\Scene}{%
\LoadGeometry(\Object.\Geometry)\;
\LoadTexture(\Object.\Texture)\;
}
\BlankLine%
\BlankLine%
\tcc{Render loop}
\While{\True}{%
\For{\Object\in\Scene}{%
\BindTexture{\Object.\Texture}\;
\Draw{\Object.\Geometry}\;
}
}
\caption{A rendering algorithm\label{f:renderer}}
\end{algorithm}
The first task the renderer needs to perform is sending the data to the GPU\@: this is done in the loading loop at the beginning.
This step can be slow, but it is generally acceptable since it only occurs once at the beginning of the program.
Then, the renderer starts the rendering loop: at each frame, it renders the whole scene.
During the rendering loop, there are two things to consider regarding performances:
\begin{itemize}
\item calling many times \texttt{glDrawArray} on small arrays is considerably slower than calling it once on a big array;
\item calling \texttt{glDrawArray} on a small array is faster than calling it on a big array.
\item obviously, the more faces a geometry contains, the slower the \texttt{draw} call is;
\item the more objects in the scene, the more overhead at each step of the loop.
\end{itemize}
However, due to the way the materials and textures work, we are forced to call \texttt{glDrawArray} at least as many times as there are materials in the model.
Minimizing the numbers of materials used in a 3D model is thus critical for rendering performances.
The way the loop works forces objects with different textures to be rendered separately.
An efficient renderer keeps the number of objects in a scene low to avoid introducing overhead.
Another way to improve the performance of rendering is \textbf{frustum culling}.
Frustum culling is a technique that consists in avoiding drawing objects that are not in the field of view of the user's camera.
It is particularly efficient when they are many objects in a scene since it gives potential for skips.
However, an important feature of 3D engine regarding performance is frustum culling.
The frustum is the viewing volume of the camera.
Frustum culling consists in avoiding rendering objects that are outside the viewing volume of the camera.
Algorithm~\ref{f:frustum-culling} is a variation of Algorithm~\ref{f:renderer} with frustum culling.
These two aspects are somehow contradictory, and to have greatest performance for 3D rendering, one must ensure that:
\begin{itemize}
\item the least amount of materials are used, and most objects that share materials are drawn together in a single \texttt{glDrawArray} call;
\item objects are not all drawn together and grouped together depending on their location to keep the frustum culling efficient.
\end{itemize}
\begin{algorithm}[th]
\SetKwData{Texture}{texture}
\SetKwData{Object}{object}
\SetKwData{Geometry}{geometry}
\SetKwData{Textures}{all\_textures}
\SetKwData{Object}{object}
\SetKwData{Scene}{scene}
\SetKwData{True}{true}
\SetKwData{CameraFrustum}{camera\_frustum}
\SetKwFunction{LoadGeometry}{load\_geometry}
\SetKwFunction{LoadTexture}{load\_texture}
\SetKwFunction{BindTexture}{bind\_texture}
\SetKwFunction{Draw}{draw}
\tcc{Initialization}
\For{$\Object\in\Scene$}{%
\LoadGeometry(\Object.\Geometry)\;
\LoadTexture(\Object.\Texture)\;
}
\BlankLine%
\BlankLine%
\tcc{Render loop}
\While{\True}{%
\For{$\Object\in\Scene$}{%
\If{$\Object\cap\CameraFrustum\neq\emptyset$}{%
\BindTexture{\Object.\Texture}\;
\Draw{\Object.\Geometry}\;
}
}
}
\caption{A rendering algorithm with frustum culling\label{f:frustum-culling}}
\end{algorithm}
A renderer that uses a single object avoids the overhead, but fails to benefit from frustum culling.
A better renderer ensures to have objects that do not spread across the whole scene, since that would lead to a useless frustum culling, and many objects to avoid rendering the whole scene at each frame, but not too many objects to avoid suffering from the overhead.