Bonjour à tous !
Bienvenue dans cette série de tuto consacrés à OpenGL ES. Ici, nous allons apprendre à nous servir de cette technologie en la mettant en forme sur le “précieux” Cette série de tuto s’inspire fortement des tutos Nehe, mais ils auront l’avantage d’être en français ! Au final, nous arriverons à dessiner des cubes ou autres en 3D ! Pour commencer, nous allons appréhender cet univers, puis nous dessinerons un triangle. Ce sera déjà bien pour une première ! 1) Nouveau projet, nouvel univers 1. Nouveau projet Commencer par créer un projet sous XCode “OpenGL ES application”. Compiler et lancer : Apple vous montre comment dessiner un carré en 2D, tournant sur lui-même et remplit d’un dégradé de couleur. Nous allons regarder ce code ensemble et le modifier quelque peu… Tout se passe dans “Eagle View”. Par défaut, lorsque je parlerai de .h ou .m, cela se référera à EagleView.m ou EagleView.h. Dans votre .m, vous avez cette ligne#define USE_DEPTH_BUFFER 0
Ici, à 0, cela signifie que l’on ne s’en sert pas car l’exemple d’Apple est en 2D. Pour la 3D, nous devons donc le mettre à 1
#define USE_DEPTH_BUFFER 1
Dans cet exemple on raisonnait donc en coordonnées (X,Y) alors que nous souhaitons travailler dans un espace en 3D, donc avec les systèmes d’axes (X,Y,Z).
En changeant cette ligne de code, on change le comportement de la méthode créée par Apple :-(
BOOL
) createFramebuffer ;
On ne s’inquiète pas, si c’est écrit par Apple, espérons qu’ils savent ce qu’ils font !
Il nous faut maintenant créer une nouvelle méthode. Elle permettra de créer les conditions dans lesquelles OpenGL va travailler. J’y reviendrai plus tard. 2. setupView- (
void
)setupView {
const
GLfloat zNear = 0.1, zFar = 1000.0, fieldOfView = 60.0;
GLfloat size;
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0);
// Nous donne la taille de l'écran de l'iPhone
CGRect rect =
self
.bounds;
glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / (rect.size.width / rect.size.height), zNear, zFar);
glViewport(0, 0, rect.size.width, rect.size.height);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}
Regardons de plus près :
glEnable(GL_DEPTH_TEST);
Il faut garder à l’esprit que dans OpenGL, on active lorsque l’on se sert, désactive lorsque ce n’est plus le cas.
Ici, on ne va pas le désactiver, donc on le met dans cette méthode.
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Dans OpenGL ES, le système de couleur utilisé est RVBA (Rouge Vert Bleu Alpha).
Maintenant, vous apercevez que DEGREES_TO_RADIANS n’est pas défini. Eh bien, rajoutez cette ligne de code ici :
#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGLDrawable.h>
#import "EAGLView.h"
#define USE_DEPTH_BUFFER 1
#define DEGREES_TO_RADIANS(__ANGLE) ((__ANGLE) / 180.0 * M_PI)
// A class extension to declare private methods
3. Redéfinition de drawView
Réecrivons la méthode drawView comme suit :- (
void
)drawView {
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
Cela mérite quelque explications
Les trois premières lignes mettent en place notre espace de dessin. Les deux lignes suivantes échangent notre espace de dessin avec celui qui est affiché sur l’écran. On appelle cela “double buffered”. En fait, on crée deux buffers identiques. L’un est affiché à l’écran, l’autre est effacé, et l’on écrit dedans. Lorsque c’est fini, on échange les deux, et on recommence. On efface et redessine sur celui qui était affiché avant l’échange. Tout ceci dans le but de faire que l’animation soit douce. Pour l’instant, on ne va rien faire de plus dans cette méthode. Votre programme n’affichera rien d’autre qu’un bel écran noir… 4. Gestion des erreurs Juste avant la méthode dealloc, écrivez ceci :- (
void
)checkGLError:(
BOOL
)visibleCheck {
GLenum error = glGetError();
switch
(error) {
case
GL_INVALID_ENUM:
NSLog
(@
"GL Error: Enum argument is out of range"
);
break
;
case
GL_INVALID_VALUE:
NSLog
(@
"GL Error: Numeric value is out of range"
);
break
;
case
GL_INVALID_OPERATION:
NSLog
(@
"GL Error: Operation illegal in current state"
);
break
;
case
GL_STACK_OVERFLOW:
NSLog
(@
"GL Error: Command would cause a stack overflow"
);
break
;
case
GL_STACK_UNDERFLOW:
NSLog
(@
"GL Error: Command would cause a stack underflow"
);
break
;
case
GL_OUT_OF_MEMORY:
NSLog
(@
"GL Error: Not enough memory to execute command"
);
break
;
case
GL_NO_ERROR:
if
(visibleCheck) {
NSLog
(@
"No GL Error"
);
}
break
;
default
:
NSLog
(@
"Unknown GL Error"
);
break
;
}
}
5. Derniers préparatifs avant l’assaut
Il nous faut appeler la méthode setupView au lancement, donc on la place ici :- (
id
)initWithCoder:(
NSCoder
*)coder {
if
((
self
= [
super
initWithCoder:coder])) {
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)
self
.layer;
eaglLayer.opaque =
YES
;
eaglLayer.drawableProperties = [
NSDictionary
dictionaryWithObjectsAndKeys:
[
NSNumber
numberWithBool:
NO
], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
nil
];
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if
(!context || ![EAGLContext setCurrentContext:context]) {
[
self
release];
return
nil
;
}
animationInterval = 1.0 / 60.0;
[
self
setupView];
// Je suis là !
}
return
self
;
}
Dans le .h, il faut également rajouter
- (
void
)setupView;
- (
void
)checkGLError:(
BOOL
)visibleCheck;
C’est tout pour cette première partie. Gardez dans un coin ce bout de projet pour pouvoir en faire un point de départ !
Vous trouverez ce source ici 2) Dessinons ! 1. Définition du triangle On va définir le triangle comme suit :const
GLfloat triangleVertices[] = {
0.0, 2.0, -5.0,
// Haut du triangle
-2.0, -2.0, -5.0,
// bas gauche
2.0, -2.0, -5.0
// bas droit
};
Notez que l’on raisonne en sens anti-horaire !
En 3D, on a ceci :Au centre, le point a pour coordonnées (0.0, 0.0, 0.0) En fait, c’est à cet origine que l’on est, que l’on voit. Situons le premier point, correspondant au haut du triangle : X = 0.0 on est sur le plan (Y,Z) passant par l’origine du repère Y = 2.0 on est sur une droite appartenant à ce plan :
Z = -5.0 on “recule de 5 unités”
Au final : 2. Dessinons Allons dans notre méthode drawView :- (
void
)drawView {
// Def de notre triangle
const
GLfloat triangleVertices[] = {
0.0, 2.0, -5.0,
// Haut du triangle
-2.0, -2.0, -5.0,
// bas gauche
2.0, -2.0, -5.0
// bas droit
};
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
/*************** Debut du nouveau code ******************/
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glVertexPointer(3, GL_FLOAT, 0, triangleVertices);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 3);
/*************** Fin du nouveau code ********************/
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}
Alors… Comme d’habitude, je vais expliquer ce que l’on fait !
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
On efface ce qui a été préalablement dessiné, tout simplement. (Souvenez-vous : deux buffers ! un pour montrer, l’autre pour dessiner)
GL_COLOR_BUFFER_BIT
renvoi à la couleur que l’on a définie dans le setupView, c’est-à-dire le noir.
Ici, comme l’on a activé le Depth_buffer, on est obligé de l’effacer !
glVertexPointer(3, GL_FLOAT, 0, triangleVertices);
glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid * pointer)
– size correspond aux nombre de coordonées que l’on utilise
– type correspond au type de valeurs que nous passons ; ici ce sont des float (1.0 est un float)
– stride correspond aux nombre de bytes à ignorer entre chaque coordonnées. Ne vous en inquiétez pas
pour le moment !
– enfin, le pointeur vers les données à dessiner.
glEnableClientState(GL_VERTEX_ARRAY);
N’oubliez pas que OpenGL est une machine à états : on allume ou on éteint !
glDrawArrays(GL_TRIANGLES, 0, 3);
Dès que cette méthode est appelée, OpenGL a l’information que nous sommes prêts à afficher le triangle. Par défaut, les triangles sont remplis.
glDrawArrays(GLenum mode, GLint first, GLsizei count)
– mode : le mode de dessin que nous souhaitons utiliser. Ici, c’est clairement un triangle. Ce point sera plus clair et montreras toute sa puissance lorsque nous dessinerons un carré. ça paraît bête comme ça !
– first vertex : ici, notre tableau n’a que trois points. Nous voulons donc que OpenGL commence à dessiner au premier et finisse au dernier. Si nous utilisions quelque chose de plus compliqué, nous pourrions définir un offset à partir duquel lire les coordonnées ici.
– vertex count : le nombre de points que nous allons dessiner. Ici, un triangle en a 3, une ligne en aurait 2, un pentagone 5, …
Compiler, lancer, un beau triangle apparaît !
Essayer de changer les valeurs de Z en les mettant à 0.0… Plus rien ne se dessine… Pourquoi ? Réfléchissez. La réponse dans le prochain tuto !
A bientôt dans la suite !