XII. Les effets de lumière et de brouillard▲
XII-A. La lumière▲
XII-A-1. Les types de lumières▲
OpenGL gère quatre types de lumière.
La lumière ambiante : GL_AMBIENT.
C'est la lumière de la lampe par défaut.
Elle semble venir de toutes les directions et est réfléchie dans toutes les directions.
La lumière diffuse : GL_DIFFUSE.
Elle arrive d'une direction particulière et est réfléchie dans toutes les directions.
C'est le cas d'une lampe classique, une ampoule par exemple.
La lumière spéculaire : GL_SPECULAR.
Elle arrive d'une direction particulière et est réfléchie dans une direction particulière.
C'est le cas d'un rayon de lumière réfléchi par un miroir par exemple.
La lumière émise : GL_EMISSION.
Elle est émise par un objet et non par une lampe.
La lumière émise n'éclaire pas les autres objets.
Les lumières peuvent se cumuler : par exemple, une lampe peut envoyer de la lumière ambiante et de la lumière diffuse en même temps.
XII-A-2. Les lampes▲
OpenGL gère huit lampes, en plus de la lampe par défaut.
La lampe par défaut est une lumière ambiante, on le voit bien : notre cube est éclairé sur toutes ses faces comme si la lumière venait de toutes les directions.
Si on souhaite gérer notre propre lumière, il faut d'abord activer l'éclairage :
glEnable GL_LIGHTING
(ceci désactive la lampe par défaut).
Puis activer les lampes que nous souhaitons gérer :
glEnable GL_LIGHTn avec n de 0 à 7.
Les fonctions glLight* permettent de gérer les attributs des lampes :
- le premier paramètre est le numéro de la lampe, de GL_LIGHT0 à GL_LIGHT7 ;
- le deuxième paramètre est le code de l'attribut à modifier ;
- le troisième paramètre est la valeur à affecter à l'attribut.
Les fonctions en suffixe f demandent un paramètre de type single.
Les fonctions en suffixe i demandent un paramètre de type long.
Les fonctions en suffixe fv demandent un paramètre de type tableau de single.
Les fonctions en suffixe iv demandent un paramètre de type tableau de long.
Fonction | Code de l'attribut | Valeur | Exemple |
---|---|---|---|
glLightfv ou glLightiv | GL_AMBIENT | Quatre paramètres RGBA qui déterminent l'intensité et la couleur de la lumière. Si on utilise des paramètres long avec glLightiv, les valeurs sont recalculées linéairement pour que le maxi des composantes soit égal à 1. Ces paramètres peuvent être négatifs (de -1 à 1). |
Exemple de lampe bleue Sélectionnez
|
glLightfv ou glLightiv | GL_DIFFUSE | Quatre paramètres RGBA qui déterminent l'intensité et la couleur de la lumière. Même utilisation que GL_AMBIENT. |
|
glLightfv ou glLightiv | GL_SPECULAR | Quatre paramètres RGBA qui déterminent l'intensité et la couleur de la lumière. Même utilisation que GL_AMBIENT. |
|
glLightfv ou glLightiv | GL_POSITION | Quatre paramètres x, y, z et w qui déterminent la position de la lampe. - Si w vaut 0, la lampe est directionnelle : x, y, z est un vecteur qui définit la direction de la lumière. - Si w vaut 1, la lampe est positionnelle : x, y, z définit la position de la source de lumière. Par défaut, la lampe est directionnelle avec un vecteur (0, 0, 1). |
Exemple de lampe positionnelle Sélectionnez
|
glLightf ou glLighti | GL_SPOT_CUTOFF | Un paramètre qui définit l'angle d'émission de la lumière. La valeur est de 180° par défaut. Les valeurs possibles vont de 0 à 90 ; la valeur 180 est acceptée. |
|
glLightfv ou glLightiv | GL_SPOT_DIRECTION | Trois paramètres qui déterminent la direction d'un spot. La direction par défaut est (0, 0, -1). Cet attribut n'est utile que si l'angle d'ouverture de la lampe (GL_SPOT_CUTOFF) n'est pas 180° (sa valeur par défaut). |
|
glLightf ou glLighti | GL_SPOT_EXPONENT | Un paramètre qui définit l'atténuation angulaire. Valeurs de 0 à 128. L'atténuation vaut 0 par défaut et correspond à une répartition uniforme de la lumière. Plus la valeur est élevée, plus la lumière est concentrée au centre. |
|
glLightf ou glLighti | GL_CONSTANT_ATTENUATION | Un paramètre : le coefficient d'atténuation constant. |
|
glLightf ou glLighti | GL_LINEAR_ATTENUATION | Un paramètre : le coefficient d'atténuation linéaire (fonction de la distance). |
|
glLightf ou glLighti | GL_QUADRATIC_ATTENUATION | Un paramètre : le coefficient d'atténuation quadratique (fonction de la distance au carré). |
XII-A-3. Mise en pratique▲
Nous allons ajouter une lampe diffuse qui tourne autour du cube.
Initialisons d'abord l'éclairage, dans la fonction InitScene :
' Initialisation de l'éclairage
glEnable GL_LIGHTING
glEnable GL_LIGHT0
On a activé la lampe GL_LIGHT0.
Par défaut, la lampe émet une faible lumière ambiante.
On voit très légèrement le cube.
Donnons-lui en plus une lumière diffuse, à la suite du code précédent :
' Ajout d'une lumière diffuse
Dim
lColor
(
1
To
4
) As
Single
lColor
(
1
) =
2
lColor
(
2
) =
2
lColor
(
3
) =
2
lColor
(
4
) =
1
glLightfv GL_LIGHT0, GL_DIFFUSE, lColor
(
1
)
La lampe émet maintenant une lumière blanche (2, 2, 2).
Remarquez qu'on peut utiliser des valeurs supérieures à 1.
Par défaut, elle éclaire de l'avant vers le fond.
Si vous tournez autour du cube, vous verrez que l'arrière n'est pas éclairé.
Le cube est blanc, car il est éclairé par une lumière blanche.
On a perdu les couleurs du cube.
Pour les retrouver, activer la coloration des matériaux dans l'initialisation de l'éclairage :
' Active la coloration des matériaux
glEnable GL_COLOR_MATERIAL
On retrouve les couleurs du cube.
Pour faire tourner la lampe autour du cube, on définit une variable en en-tête du module pour conserver l'angle de rotation de la lampe.
' RotationY de la lampe
Private
gLightRotate As
Double
Puis on positionne la lampe dans la fonction Render, juste après l'appel à glClear.
' Positionne la lampe
gLightRotate =
gLightRotate +
0
.03
Dim
lposition
(
1
To
4
) As
Single
lposition
(
1
) =
3
*
Cos
(
gLightRotate)
lposition
(
2
) =
0
lposition
(
3
) =
3
*
Sin
(
gLightRotate)
lposition
(
4
) =
1
glLightfv GL_LIGHT0, GL_POSITION, lposition
(
1
)
La lampe tourne, mais on ne voit pas vraiment où elle se trouve et si l'éclairage est correct.
Pour matérialiser la lampe, on va dessiner une petite sphère.
Pour dessiner une sphère, on utilise la fonction gluSphere qui nous facilite la tâche.
Ajoutez le code de dessin de la sphère juste après le positionnement de la lampe :
' Dessine une sphère à l'endroit où se trouve la lampe
#If VBA7 Then
Dim
lQuad As
LongPtr
#Else
Dim
lQuad As
Long
#End If
glPushMatrix
glColor3VB vbYellow
glTranslated 3
*
Cos
(
gLightRotate), 0
, 3
*
Sin
(
gLightRotate)
lQuad =
gluNewQuadric
gluQuadricOrientation lQuad, GLU_INSIDE
gluSphere lQuad, 0
.5
, 20
, 15
gluDeleteQuadric lQuad
glPopMatrix
Notez l’utilisation de LongPtr à partir de VBA7 (Office 2010) pour gérer correctement les versions 64 bits ; en effet certaines fonctions glu renvoient des pointeurs.
Les fonctions glPushMatrix et glPopMatrix prennent ici toute leur importance.
On peut transformer la sphère et le cube indépendamment en sauvegardant puis en restaurant la matrice de modélisation-visualisation.
Notez qu'on a défini une sphère de couleur jaune, de taille 0.5, avec 20 divisions pour les longitudes et 15 divisions pour les latitudes.
On a également modifié l'orientation de la sphère (GLU_INSIDE) de manière à ce que les faces soient éclairées (sinon une lumière à l'intérieur de la sphère ne l'éclaire pas à l'extérieur).
La sphère est créée centrée sur l'origine.
Nous la déplaçons à l'endroit où se situe la lampe avec la fonction glTranslated.
Testons notre éclairage :
On remarque un problème : la lampe est à droite et commence à éclairer la face gauche en violet.
Il se trouve qu’on n’a pas défini de normale lors du dessin du cube.
Il faut ajouter une normale pour chaque face du cube.
' Début de la primitive pour le cube
glBegin GL_QUADS
' Face du haut = vert
Call
glColor3d
(
0
, 1
, 0
)
Call
glNormal3d
(
0
, 1
, 0
)
Call
glVertex3d
(
1
, 1
, -
1
)
Call
glVertex3d
(-
1
, 1
, -
1
)
Call
glVertex3d
(-
1
, 1
, 1
)
Call
glVertex3d
(
1
, 1
, 1
)
' Face du bas = orange
Call
glColor3d
(
1
, 0
.5
, 0
)
Call
glNormal3d
(
0
, -
1
, 0
)
Call
glVertex3f
(-
1
, -
1
, -
1
)
Call
glVertex3f
(
1
, -
1
, -
1
)
Call
glVertex3f
(
1
, -
1
, 1
)
Call
glVertex3f
(-
1
, -
1
, 1
)
' Face de derrière = rouge
Call
glColor3d
(
1
, 0
, 0
)
Call
glNormal3d
(
0
, 0
, -
1
)
Call
glVertex3f
(
1
, -
1
, -
1
)
Call
glVertex3f
(-
1
, -
1
, -
1
)
Call
glVertex3f
(-
1
, 1
, -
1
)
Call
glVertex3f
(
1
, 1
, -
1
)
' Face de devant = jaune
Call
glColor3d
(
1
, 1
, 0
)
Call
glNormal3d
(
0
, 0
, 1
)
Call
glVertex3f
(-
1
, -
1
, 1
)
Call
glVertex3f
(
1
, -
1
, 1
)
Call
glVertex3f
(
1
, 1
, 1
)
Call
glVertex3f
(-
1
, 1
, 1
)
' Face de gauche = violet
Call
glColor3d
(
1
, 0
, 1
)
Call
glNormal3d
(-
1
, 0
, 0
)
Call
glVertex3f
(-
1
, 1
, 1
)
Call
glVertex3f
(-
1
, 1
, -
1
)
Call
glVertex3f
(-
1
, -
1
, -
1
)
Call
glVertex3f
(-
1
, -
1
, 1
)
' Face de droite = bleu
Call
glColor3d
(
0
, 0
, 1
)
Call
glNormal3d
(
1
, 0
, 0
)
Call
glVertex3f
(
1
, 1
, -
1
)
Call
glVertex3f
(
1
, 1
, 1
)
Call
glVertex3f
(
1
, -
1
, 1
)
Call
glVertex3f
(
1
, -
1
, -
1
)
glEnd
Comme pour les couleurs, on définit une normale par face, mais la normale s'applique à chaque vertice de la face.
Pour que l'éclairage soit correct, il faut que les normales aient une longueur de 1.
Ce qui est le cas pour nos normales lors de leur définition.
Mais lorsqu'on redimensionne la scène, où si on définit des normales plus complexes (non parallèles à un axe), elles peuvent ne plus être à la bonne taille.
Utilisez la commande glEnable GL_NORMALIZE à l'initialisation pour qu'OpenGL redimensionne les normales avant éclairage.
Testons à nouveau l'éclairage avec les normales :
Voilà qui est mieux, les faces sont éclairées correctement.
En y regardant de plus près, on voit que chaque face du cube est éclairée progressivement.
C'est le comportement standard, que l'on peut changer avec la fonction glShadeModel :
- glShadeModel GL_FLAT permet de colorier tout le polygone uniformément.
C'est plus rapide, mais moins joli ; - glShadeModel GL_SMOOTH permet de colorier le polygone progressivement.
C'est le comportement par défaut.
XII-B. Le brouillard▲
Le brouillard est très facile à mettre en œuvre.
Pour activer le brouillard : glEnable GL_FOG.
Les fonctions glFog* permettent de gérer les attributs du brouillard :
- le premier paramètre est le code de l'attribut à modifier ;
- le second paramètre est la valeur à affecter à l'attribut.
Les fonctions en suffixe f demandent un paramètre de type single.
Les fonctions en suffixe i demandent un paramètre de type long.
Les fonctions en suffixe fv demandent un paramètre de type tableau de single.
Les fonctions en suffixe iv demandent un paramètre de type tableau de long.
Fonction | Code de l'attribut | Valeur | Exemple |
---|---|---|---|
glFogf ou glFogi | GL_FOG_MODE | Un paramètre qui détermine l'équation à utiliser pour gérer l'atténuation du brouillard. Les valeurs possibles sont : - GL_LINEAR pour une atténuation linéaire, c'est le plus rapide ; - GL_EXP pour une atténuation exponentielle, c'est le mode par défaut ; - GL_EXP2 pour une atténuation exponentielle au carré. |
Exemple d'atténuation linéaire Sélectionnez
|
glFogf ou glFogi | GL_FOG_DENSITY | Un paramètre qui détermine la densité du brouillard. La valeur doit être positive. La densité par défaut est de 1. |
Exemple de densité de brouillard Sélectionnez
|
glFogf ou glFogi | GL_FOG_START | Un paramètre qui détermine la distance où commence le brouillard. C'est une distance par rapport à la caméra. Valable pour un mode GL_LINEAR seulement. |
Exemple de début du brouillard Sélectionnez
|
glFogf ou glFogi | GL_FOG_END | Un paramètre qui détermine la distance où s'arrête le brouillard. C'est une distance par rapport à la caméra. Valable pour un mode GL_LINEAR seulement. |
Exemple de fin du brouillard Sélectionnez
|
glFogf ou glFogi | GL_FOG_INDEX | Un paramètre qui détermine l'index de la couleur du brouillard. Uniquement si la fenêtre a été créée avec des couleurs indexées (GLUT_INDEX). |
Exemple de couleur indexée du brouillard Sélectionnez
|
glFogfv ou glFogiv | GL_FOG_COLOR | Quatre paramètres qui déterminent la couleur RGBA du brouillard. Uniquement si la fenêtre a été créée avec des couleurs RGB (GLUT_RGB ou GLUT_RGBA). Par défaut, le brouillard est noir (0, 0, 0, 0). |
Exemple de brouillard gris Sélectionnez
|
glHint | GL_FOG_HINT | Un paramètre qui détermine la qualité du brouillard. Peut valoir GL_FASTEST, GL_NICEST ou GL_DONT_CARE. |
Exemple de brouillard moins beau, mais plus rapide Sélectionnez
|
Ajoutons un brouillard gris à notre scène, dans la fonction InitScene :
' Initialisation du brouillard
Dim
lfogcolor
(
1
To
4
) As
Single
glEnable GL_FOG
lfogcolor
(
1
) =
0
.7
lfogcolor
(
2
) =
0
.7
lfogcolor
(
3
) =
0
.7
lfogcolor
(
4
) =
1
glFogfv GL_FOG_COLOR, lfogcolor
(
1
)
glFogi GL_FOG_MODE, GL_LINEAR
glFogf GL_FOG_START, 7
glFogf GL_FOG_END, 10
glFogf GL_FOG_DENSITY, 0
.1
glHint GL_FOG_HINT, GL_NICEST
Et voici notre cube dans le brouillard :
XIII. Rappel du code complet▲
Un rappel du contenu du module VB à ce stade du tutoriel, avec la lumière et le brouillard :
Option
Explicit
' Temps écoulé pour calcul du frame rate
Private
gElapsedTime As
Long
' Nombre d'affichages pour calcul du frame rate
Private
gDisplayCount As
Long
' RotationX
Private
gRotateX As
Double
' RotationY
Private
gRotateY As
Double
' Zoom
Private
gZoom As
Double
' RotationY de la lampe
Private
gLightRotate As
Double
Function
FonctionOpenGL
(
)
' Chargement de freeGLUT
If
Not
LoadFreeGlut
(
CurrentProject.Path
) Then
MsgBox
"Impossible de charger la bibliothèque freeGLUT"
Exit
Function
End
If
' Initialisation de la bibliothèque
glutInit 0
&
, ""
' Initialisation du mode d'affichage
glutInitDisplayMode GLUT_RGBA Or
GLUT_DOUBLE Or
GLUT_DEPTH
' Définition de l'option de sortie de boucle
glutSetOption GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS
' Création d'une fenêtre
glutCreateWindow "Tutoriel fenêtre GLUT"
' Fonction d'affichage
glutDisplayFunc AddressOf CallBackDraw
' Fonctions de rappel clavier
glutSpecialFunc AddressOf CallBackSpecial
' Fonction de rappel molette de souris
glutMouseWheelFunc AddressOf CallBackMouseWheel
' Appel de la fonction d'initialisation
InitScene
' Fonction d'attente
glutTimerFunc 1000
/
50
, AddressOf CallBackTimer, 0
' Boucle principale
glutMainLoop
End
Function
' Fonction d'attente
Public
Sub
CallBackTimer
(
ByVal
value As
Long
)
' Rappel de la fonction d'attente
glutTimerFunc 1000
/
50
, AddressOf CallBackTimer, 0
' Rafraichir l'affichage si une fenêtre openGL est active
If
glutGetWindow <>
0
Then
glutPostRedisplay
End
Sub
' Fonction d'affichage
Public
Sub
CallBackDraw
(
)
' Appel de la fonction de rendu
Call
Render
' Échange les buffers
glutSwapBuffers
' Calcul et affichage du frame rate
gDisplayCount =
gDisplayCount +
1
If
gDisplayCount =
100
Then
glutSetWindowTitle "Frame rate : "
&
format
(
gDisplayCount /
(
glutGet
(
GLUT_ELAPSED_TIME) -
gElapsedTime) *
1000
, "0,0"
)
gDisplayCount =
0
gElapsedTime =
glutGet
(
GLUT_ELAPSED_TIME)
End
If
End
Sub
' Fonction d'initialisation
Public
Sub
InitScene
(
)
' Mode d'affichage = remplissage
glPolygonMode GL_FRONT_AND_BACK, GL_FILL
' Tests de profondeur
glEnable GL_DEPTH_TEST
glDepthFunc GL_LEQUAL
' Initialisation des variables de visualisation
gRotateX =
0
gRotateY =
0
gZoom =
10
' Initialisation de l'éclairage
glEnable GL_LIGHTING
glEnable GL_LIGHT0
' Ajout d'une lumière diffuse
Dim
lColor
(
1
To
4
) As
Single
lColor
(
1
) =
2
lColor
(
2
) =
2
lColor
(
3
) =
2
lColor
(
4
) =
1
glLightfv GL_LIGHT0, GL_DIFFUSE, lColor
(
1
)
' Active la coloration des matériaux
glEnable GL_COLOR_MATERIAL
' Initialisation du brouillard
Dim
lfogcolor
(
1
To
4
) As
Single
glEnable GL_FOG
lfogcolor
(
1
) =
0
.7
lfogcolor
(
2
) =
0
.7
lfogcolor
(
3
) =
0
.7
lfogcolor
(
4
) =
1
glFogfv GL_FOG_COLOR, lfogcolor
(
1
)
glFogi GL_FOG_MODE, GL_LINEAR
glFogf GL_FOG_START, 7
glFogf GL_FOG_END, 10
glFogf GL_FOG_DENSITY, 0
.1
glHint GL_FOG_HINT, GL_NICEST
End
Sub
' Fonction de rendu
Public
Sub
Render
(
)
' Passage en matrice de projection
glMatrixMode GL_PROJECTION
' Initialisation de la matrice
glLoadIdentity
' Définition de la perspective
gluPerspective 45
, 1
, 0
.1
, 100
' Passage en matrice de modélisation-visualisation
glMatrixMode GL_MODELVIEW
' Initialisation de la matrice
glLoadIdentity
' Position de la caméra
gluLookAt 0
, 0
, gZoom, 0
, 0
, 0
, 0
, 1
, 0
glRotated gRotateX, 1
, 0
, 0
glRotated gRotateY, 0
, 1
, 0
' Vide les buffers couleur et profondeur
glClear GL_COLOR_BUFFER_BIT Or
GL_DEPTH_BUFFER_BIT
' Positionne la lampe
gLightRotate =
gLightRotate +
Dim
lposition
(
1
To
4
) As
Single
lposition
(
1
) =
3
*
Cos
(
gLightRotate)
lposition
(
2
) =
0
lposition
(
3
) =
3
*
Sin
(
gLightRotate)
lposition
(
4
) =
1
glLightfv GL_LIGHT0, GL_POSITION, lposition
(
1
)
' Dessine une sphère à l'endroit où se trouve la lampe
#If VBA7 Then
Dim
lQuad As
LongPtr
#Else
Dim
lQuad As
Long
#End If glPushMatrix
glColor3VB vbYellow
glTranslated 3
*
Cos
(
gLightRotate), 0
, 3
*
Sin
(
gLightRotate)
lQuad =
gluNewQuadric
gluQuadricOrientation lQuad, GLU_INSIDE
gluSphere lQuad, 0
.5
, 20
, 15
gluDeleteQuadric lQuad
glPopMatrix
' Sauvegarde la matrice
glPushMatrix
' Rotation du cube
glRotated 30
, 1
, 1
, 1
' Début de la primitive pour le cube
glBegin GL_QUADS
' Face du haut = vert
Call
glColor3d
(
0
, 1
, 0
)
Call
glNormal3d
(
0
, 1
, 0
)
Call
glVertex3d
(
1
, 1
, -
1
)
Call
glVertex3d
(-
1
, 1
, -
1
)
Call
glVertex3d
(-
1
, 1
, 1
)
Call
glVertex3d
(
1
, 1
, 1
)
' Face du bas = orange
Call
glColor3d
(
1
, 0
.5
, 0
)
Call
glNormal3d
(
0
, -
1
, 0
)
Call
glVertex3f
(-
1
, -
1
, -
1
)
Call
glVertex3f
(
1
, -
1
, -
1
)
Call
glVertex3f
(
1
, -
1
, 1
)
Call
glVertex3f
(-
1
, -
1
, 1
)
' Face de derrière = rouge
Call
glColor3d
(
1
, 0
, 0
)
Call
glNormal3d
(
0
, 0
, -
1
)
Call
glVertex3f
(
1
, -
1
, -
1
)
Call
glVertex3f
(-
1
, -
1
, -
1
)
Call
glVertex3f
(-
1
, 1
, -
1
)
Call
glVertex3f
(
1
, 1
, -
1
)
' Face de devant = jaune
Call
glColor3d
(
1
, 1
, 0
)
Call
glNormal3d
(
0
, 0
, 1
)
Call
glVertex3f
(-
1
, -
1
, 1
)
Call
glVertex3f
(
1
, -
1
, 1
)
Call
glVertex3f
(
1
, 1
, 1
)
Call
glVertex3f
(-
1
, 1
, 1
)
' Face de gauche = violet
Call
glColor3d
(
1
, 0
, 1
)
Call
glNormal3d
(-
1
, 0
, 0
)
Call
glVertex3f
(-
1
, 1
, 1
)
Call
glVertex3f
(-
1
, 1
, -
1
)
Call
glVertex3f
(-
1
, -
1
, -
1
)
Call
glVertex3f
(-
1
, -
1
, 1
)
' Face de droite = bleu
Call
glColor3d
(
0
, 0
, 1
)
Call
glNormal3d
(
1
, 0
, 0
)
Call
glVertex3f
(
1
, 1
, -
1
)
Call
glVertex3f
(
1
, 1
, 1
)
Call
glVertex3f
(
1
, -
1
, 1
)
Call
glVertex3f
(
1
, -
1
, -
1
)
glEnd
' Restaure la matrice
glPopMatrix
End
Sub
' Gestion du clavier
Public
Sub
CallBackSpecial
(
ByVal
key As
Long
, ByVal
x As
Long
, ByVal
y As
Long
)
Select
Case
key
Case
GLUT_KEY_LEFT
gRotateY =
gRotateY -
10
Case
GLUT_KEY_RIGHT
gRotateY =
gRotateY +
10
Case
GLUT_KEY_UP
gRotateX =
gRotateX -
10
Case
GLUT_KEY_DOWN
gRotateX =
gRotateX +
10
End
Select
End
Sub
' Gestion de la molette de la souris
Public
Sub
CallBackMouseWheel
(
ByVal
wheel As
Long
, ByVal
direction As
Long
, ByVal
x As
Long
, ByVal
y As
Long
)
gZoom =
gZoom +
direction
End
Sub