Canalblog
Editer l'article Suivre ce blog Administration + Créer mon blog
Publicité
Fanfois et ses papillons
21 mai 2012

HTML5 05 (tutorial WebGL, 4ème partie)

Dans ce billet nous allons nous occuper de l'observateur que l'on appelle généralement la caméra.

Pour l'instant nous n'avons rien fait de particulier. En conséquence nous voyons un espace 3D qui se réduit à un "cube" centré sur l'origine, le point de coordonnées (0,0,0) si vous avez un doute. Le volume de ce "cube" couvre la zone -1, 1 sur les trois axes.
Dernier élément permettant de comprendre ce que nous voyons, c'est la manière dont cet espace 3D est projeté sur notre écran. Considérez que la vitre à travers laquelle vous regardez se trouve être la face avant du dit cube. Tous les points se trouvant à l'arrière de cette face y sont projetés parallèlement à l'axe des Z (projection orthogonale).

Pour vous aider à visualiser la chose nous allons créer une nouvelle page HTML, WebGL_07.htm, dans laquelle nous ajoutons au référentiel formé par nos 3 axes un trait supplémentaire.
Ce trait ne va pas être quelconque. Nous voulons qu'il soit projeté sous la forme d'un point lorsque l'axe Z, du référentiel dessiné, se trouve juste devant nous. Comme l'animation fait tourner notre figure, nous ne verrons un point que deux fois par tour. Pour faciliter la lisibilité, nous tracerons ce trait en noir. Si vous le souhaitez, vous pouvez aussi passer le fond en blanc, ce qui n'est pas fait dans le code de la page que je vous propose :

<!DOCTYPE HTML>
<html>
  <head>
    <!-- required to use WebGLHelper object -->
    <!-- <script type="text/javascript" src="http://iewebgl.com/scripts/webglhelper.js"></script> -->
    <script type="text/javascript" src="script/webglhelper.js"></script>
    <!-- La librairie de manipulation de matrices de Brandon Jones -->
    <script type="text/javascript" src="script/gl-matrix-min.js"></script>
    <!-- Notre librairie d'animation et d'aide WebGL -->
    <script type="text/javascript" src="script/WebGL_2.js"></script>
    <script type="text/javascript">
      function initBuffers(gl) {
        var lineBuffers = {}
 
        // Les points de nos axes.
        lineBuffers.positionBuffer = gl.createArrayBuffer([
          // X
          -0.5,  0.0,  0.0,
           0.5,  0.0,  0.0,
          // Y
           0.0, -0.5,  0.0,
           0.0,  0.5,  0.0,
          // Z
           0.0,  0.0, -0.5,
           0.0,  0.0,  0.5,
          // Trait.
          -0.9,  0.3, -1.0,
          -0.9,  0.3,  1.0
 
        ]);
 
        // Les couleurs de nos axes.
        var colors = [
          [1.0, 0.0, 1.0, 1.0], // X - Rose
          [1.0, 0.0, 0.0, 1.0], // X - Red
          [1.0, 1.0, 0.0, 1.0], // Y - Jaune
          [0.0, 1.0, 0.0, 1.0], // Y - Green
          [0.0, 1.0, 1.0, 1.0], // Z - Turquoise
          [0.0, 0.0, 1.0, 1.0], // Z - Blue
          [0.0, 0.0, 0.0, 1.0], // trait - Noir
          [0.0, 0.0, 0.0, 1.0]  // trait - Noir
        ];
        var colorVertices = [];
        for (var n in colors) {
          var color = colors[n];
          colorVertices = colorVertices.concat(color);
        }
        lineBuffers.colorBuffer = gl.createArrayBuffer(colorVertices);
 
        return lineBuffers;
      }
 
      function stage(gl, lineBuffers, angle) {
        // Effacement de l'image précédente.
        gl.clear();
 
        // Déplacement de l'objet.
        gl.identity();
        gl.rotate(angle, 0, 1, 0);
 
        // Dessin de l'image courante.
        gl.pushVaryingColorBuffer(lineBuffers);
        gl.pushPositionBuffer(lineBuffers);
        gl.drawLineArrays(lineBuffers);
      }
 
      function initWebGL(canvasElement) {
        // Création de notre classe d'aide.
        var gl = new WebGL(canvasElement);
 
        // Initialisation des shaders.
        gl.setShaderProgram("VARYING_COLOR");
 
        // Définition de la couleur de fond.
        gl.setClearColor(1, 0.98, 0.8, 1);
 
        // Définition des points et des couleurs.
        var lineBuffers = initBuffers(gl);
 
        // L'angle de rotation au tour de Y.
        var angle = 0;
 
        // Mise en place de la scène.
        gl.setStage(function () {
          // Calcule du nouvel angle.
          var angularVelocity = Math.PI / 4; // radians / second
          var angleEachFrame = angularVelocity * gl.getTimeInterval() / 1000;
          angle += angleEachFrame;
 
          stage(gl, lineBuffers, angle);
        });
 
        // Lancement de l'animation.
        gl.startAnimation();
      }
 
      function OnGLCanvasCreated(canvasElement, elementId) {
        window.setTimeout(function () { initWebGL(canvasElement); }, 500);
      }
 
      function OnGLCanvasFailed(canvasElement, elementId) {
        alert("Votre butineur ne supporte pas WebGL !");
      }
    </script>
  </head>
  <body>
    <script id="WebGLCanvasCreationScript" type="text/javascript" width="800" height="500" style="border:1px solid black;">
      WebGLHelper.CreateGLCanvasInline('glCanvas', OnGLCanvasCreated, OnGLCanvasFailed)
    </script>
  </body>
</html>

Actuellement notre trait est un segment de droite qui coure du point (-0.9, 0.3, -1.0) au point (-0.9, 0.3, 1.0). Si vous affichez la page, vous constaterez que notre référentiel n'est plus basculé. En conséquence X et Z ne sont distinguables que par leur couleur.
Amusez-vous à reculer le segment au-delà de Z=-0.9 et observez ce qui se passe.
Autre paramètre avec lequel vous pouvez jouer, la longueur du segment suivant l'axe Z. Allez au-delà de la zone -1, 1.
Cela devrait vous permettre de bien "matérialiser" la zone visible et le mode de projection.

Un dernier point. Notre "cube" n'est pas vraiment carré. Ces coordonnées sont bien celle d'un cube, cela dit, à l'écran la chose n'est vraie que si le canvas contenant notre "cube" est lui-même carré. Pour vous en convaincre, modifiez le code de la page pour que la rotation se fasse au tour de l'axe Z. Il ne fait aucun doute que l'angle des axes X et Y ne reste pas droit au cours de la rotation (sauf si le canvas et un carré). La copie d'écran suivante montre ce que vous pouvez constater.
html5 05 01

Maintenant comment faire tenir une scène quelconque dans notre cube ?
Comment introduire de la perspective ?

En introduisant une seconde matrice, la matrice de projection, généralement appelée pMatrix. L'introduction d'une seconde matrice permet de traiter indépendamment l'animation de la scène et la manière dont nous la regardons. Cette matrice doit être introduite dans le code des shaders et bien sûr dans le code alimentant les shaders.
Dans ce billet nous n'utiliserons que du code shader inclus à notre classe WebGL. Le code impacté par l'introduction de cette matrice, pMatrix, dans la classe WebGLest le suivant :

var WebGL = function (canvasElement) {
  // Le canvas et le contexte WebGL (ou son équivalent IEWebGL).
  this.canvas = canvasElement;
  this.context = WebGLHelper.GetGLContext(canvasElement);
  this.stage = undefined;
 
  // Le nécessaire pour gérer l'animation.
  this.t = 0;
  this.timeInterval = 0;
  this.startTime = 0;
  this.lastTime = 0;
  this.frame = 0;
  this.animating = false;
 
  // Provided by Paul Irish.
  // Permet de faire fonctionner l'animation sur tous les butineurs.
  window.requestAnimFrame = (function (callback) {
    return window.requestAnimationFrame ||
           window.webkitRequestAnimationFrame ||
           window.mozRequestAnimationFrame ||
           window.oRequestAnimationFrame ||
           window.msRequestAnimationFrame ||
           function (callback) {
             window.setTimeout(callback, 1000 / 60);
           };
  })();
 
  // Encapsulation des matrices globales de glMatrix.
  this.mat3 = mat3;
  this.mat4 = mat4;
  this.vec3 = vec3;
 
  // Les constantes définissant les types de shader possible.
  this.FIXED_COLOR = "FIXED_COLOR";
  this.VARYING_COLOR = "VARYING_COLOR";
 
  // Le nécessaire pour WebGL.
  this.shaderProgram = null;
  this.mvMatrix = this.mat4.create();
  this.pMatrix = this.mat4.create();
 
  // La fonction qui permet à setCustomShaderProgram initialiser les shaders.
  this.initCustomShaders = undefined;
 
  // Doit-on afficher des alertes ?
  this.showAlerts = true;
 
  // Initialisation générale du contexte WebGL.
  this.context.viewportWidth = this.canvas.width;
  this.context.viewportHeight = this.canvas.height;
};
 
WebGL.prototype.identity = function () {
  this.mat4.identity(this.mvMatrix);
  this.mat4.identity(this.pMatrix);
};
 
WebGL.prototype.perspective = function (viewAngle, minDist, maxDist) {
  this.mat4.perspective(viewAngle, this.context.viewportWidth / this.context.viewportHeight, minDist, maxDist, this.pMatrix);
};
 
WebGL.prototype.getVertexShaderGLSL = function (shaderType) {
  switch (shaderType) {
    case this.FIXED_COLOR:
      return "attribute vec3 aVertexPosition;\n" +
            "uniform mat4 uMVMatrix;\n" +
            "uniform mat4 uPMatrix;\n" +
            "void main(void) {\n" +
            "gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n" +
            "}";
    case this.VARYING_COLOR:
      return "attribute vec3 aVertexPosition;\n" +
            "attribute vec4 aVertexColor;\n" +
            "uniform mat4 uMVMatrix;\n" +
            "uniform mat4 uPMatrix;\n" +
            "varying vec4 vColor;\n" +
            "void main(void) {\n" +
            "gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);\n" +
            "vColor = aVertexColor;\n" +
            "}";
  }
};
 
WebGL.prototype.initPositionShader = function () {
  this.shaderProgram.vertexPositionAttribute = this.context.getAttribLocation(this.shaderProgram, "aVertexPosition");
  this.context.enableVertexAttribArray(this.shaderProgram.vertexPositionAttribute);
  this.shaderProgram.pMatrixUniform = this.context.getUniformLocation(this.shaderProgram, "uPMatrix");
  this.shaderProgram.mvMatrixUniform = this.context.getUniformLocation(this.shaderProgram, "uMVMatrix");
};
 
WebGL.prototype.setMatrixUniforms = function () {
  this.context.uniformMatrix4fv(this.shaderProgram.pMatrixUniform, false, this.pMatrix);
  this.context.uniformMatrix4fv(this.shaderProgram.mvMatrixUniform, false, this.mvMatrix);
};

Vous aurez constaté l'apparition d'une, et d'une seule, nouvelle méthode, perspective.
Première chose importante, toutes nos pages fonctionnent à l'identique. Il n'y a donc pas obligation d'utiliser pMatrix. Cela est dû au fait de la méthode identity initialise les deux matrices. En conséquence, toutes les autres méthodes manipulant les matrices pMatrix et mvMatrix doivent être appelées après identity qui, elle, doit toujours être appelées.
Pour positionner pMatrix nous utiliserons perspective. Cette dernière possède trois paramètres.
Le premier permet de définir l'angle du "cône" (pyramide tronquée pour être plus précis car la section est rectangulaire) de notre "objectif" (l'objectif de notre caméra).
Le second défini la position du plan à partir duquel nous voyons quelque chose. Ce plan est perpendiculaire à l'axe Z et ce que nous définissons c'est la distance de ce plan par rapport à l'origine.
Le troisième défini la position du plan au-delà duquel nous ne voyons plus rien. Ce plan est naturellement perpendiculaire à l'axe Z et donc parallèle au plan précédent.

Cela dit cette fonction définit implicitement d'autres choses sur lesquels nous n'avons pas la main.
Pour commencer, elle positionne la caméra sur l'origine et la braque vers l'arrière en plein sur l'axe Z. Si l'on revient aux paramètres de la fonction, les deuxième et troisième correspondent donc en fait à des points situés sur la partie négative de l'axe Z
Autre chose, elle introduit un ratio entre la largeur et la hauteur. Cela a pour effet de rétablir les angles droits. Si vous reprenez le code de la page où vous faites tourner le référentiel autour de l'axe des Z, que vous le modifiez légèrement pour utiliser la méthode perspective, vous constaterez que vous obtenez cela :
html5 05 02
Si vous avez du mal à obtenir ce résultat, voici le code complet de la page WebGL_08.htmcorrespondante à la capture :

<!DOCTYPE HTML>
<html>
  <head>
    <!-- required to use WebGLHelper object -->
    <!-- <script type="text/javascript" src="http://iewebgl.com/scripts/webglhelper.js"></script> -->
    <script type="text/javascript" src="script/webglhelper.js"></script>
    <!-- La librairie de manipulation de matrices de Brandon Jones -->
    <script type="text/javascript" src="script/gl-matrix-min.js"></script>
    <!-- Notre librairie d'animation et d'aide WebGL -->
    <script type="text/javascript" src="script/WebGL_2.js"></script>
    <script type="text/javascript">
      function initBuffers(gl) {
        var lineBuffers = {}
 
        // Les points de nos axes.
        lineBuffers.positionBuffer = gl.createArrayBuffer([
          // X
          -0.5,  0.0,  0.0,
           0.5,  0.0,  0.0,
          // Y
           0.0, -0.5,  0.0,
           0.0,  0.5,  0.0,
          // Z
           0.0,  0.0, -0.5,
           0.0,  0.0,  0.5
        ]);
 
        // Les couleurs de nos axes.
        var colors = [
          [1.0, 0.0, 1.0, 1.0], // X - Rose
          [1.0, 0.0, 0.0, 1.0], // X - Red
          [1.0, 1.0, 0.0, 1.0], // Y - Jaune
          [0.0, 1.0, 0.0, 1.0], // Y - Green
          [0.0, 1.0, 1.0, 1.0], // Z - Turquoise
          [0.0, 0.0, 1.0, 1.0]  // Z - Blue
        ];
        var colorVertices = [];
        for (var n in colors) {
          var color = colors[n];
          colorVertices = colorVertices.concat(color);
        }
        lineBuffers.colorBuffer = gl.createArrayBuffer(colorVertices);
 
        return lineBuffers;
      }
 
      function stage(gl, lineBuffers, angle) {
        // Effacement de l'image précédente.
        gl.clear();
 
        // Initialisation des matrices.
        gl.identity();
 
        // Définition de la perspective.
        gl.perspective(45, 0.1, 10.0);
 
        // Déplacement de l'objet.
        gl.translate(0.0, 0.0, -1.0);
        gl.rotate(angle, 0, 0, 1);
 
        // Dessin de l'image courante.
        gl.pushVaryingColorBuffer(lineBuffers);
        gl.pushPositionBuffer(lineBuffers);
        gl.drawLineArrays(lineBuffers);
      }
 
      function initWebGL(canvasElement) {
        // Création de notre classe d'aide.
        var gl = new WebGL(canvasElement);
 
        // Initialisation des shaders.
        gl.setShaderProgram("VARYING_COLOR");
 
        // Définition de la couleur de fond.
        gl.setClearColor(1, 0.98, 0.8, 1);
 
        // Définition des points et des couleurs.
        var lineBuffers = initBuffers(gl);
 
        // L'angle de rotation au tour de Z.
        var angle = 0;
 
        // Mise en place de la scène.
        gl.setStage(function () {
          // Calcule du nouvel angle.
          var angularVelocity = Math.PI / 4; // radians / second
          var angleEachFrame = angularVelocity * gl.getTimeInterval() / 1000;
          angle += angleEachFrame;
 
          stage(gl, lineBuffers, angle);
        });
 
        // Lancement de l'animation.
        gl.startAnimation();
      }
 
      function OnGLCanvasCreated(canvasElement, elementId) {
        window.setTimeout(function () { initWebGL(canvasElement); }, 500);
      }
 
      function OnGLCanvasFailed(canvasElement, elementId) {
        alert("Votre butineur ne supporte pas WebGL !");
      }
    </script>
  </head>
  <body>
    <script id="WebGLCanvasCreationScript" type="text/javascript" width="800" height="500" style="border:1px solid black;">
      WebGLHelper.CreateGLCanvasInline('glCanvas', OnGLCanvasCreated, OnGLCanvasFailed)
    </script>
  </body>
</html>

On constate immédiatement deux choses :

  1. le référentiel est plus gros ;
  2. les angles sont bien droits. Conséquences, les axes ne pointent plus vers les coins du canvas (sauf s'il est carré).

Détaillons le code de notre page pour comprendre ce qui se passe.
Le positionnement du point de vue est réalisé à l'aide de l'appelle suivant : gl.perspective(45, 0.1, 10.0);.
Cela signifie que nous avons un angle de vue, un "cône", de 45°. La pointe de ce "cône" est placée à l'origine du contexte WebGL. L'axe du "cône" est l'axe Z vers les z négatifs. Nous voyons ce qui se trouve dans ce "cône" entre les positions -0,1 et -10,0 de l'axe Z.
Pour rendre notre référentiel visible, nous l'avons reculé à l'aide de gl.translate(0.0, 0.0, -1.0);. Mais pourquoi est-il plus gros ?
Deux interprétations sont possibles :

  1. nous sommes plus près ;
  2. notre "focale" est trop petite.

Pour le premier point, on comprend bien que, maintenant que la perspective est introduite, plus l'objet est près, plus il est gros. Amusez-vous à reculer la scène pour vous en convaincre. Si vous ne voyez plus rien, c'est probablement que vous êtes allé trop loin. N'oubliez pas que nous avons limitez la zone visible à 0,1 - 10,0.
Après avoir remis le référentiel à -1,0, modifiez l'angle de l'objectif. Si vous le réduisez, l'objet grossi. Si vous l'augmentez, l'objet diminue.

Maintenant que se passe-t-il si nous laissons notre scène en (0, 0, 0) ?
Pour cela, créons une nouvelle page, WebGL_09.htm donc, dans laquelle nous reprenons le positionnement du référentiel que nous avions mis en place lors du précédent billet. La fonction stageest alors :

function stage(gl, lineBuffers, angle) {
  // Effacement de l'image précédente.
  gl.clear();
 
  // Initialisation des matrices.
  gl.identity();
 
  // Définition de la perspective.
  gl.perspective(45, 0.1, 10.0);
 
  // Déplacement de l'objet.
  // On met l'objet dans le champs de vision.
  //gl.translate(0.0, 0.0, -0.08);
  //gl.translate(0.0, 0.0, -0.002);
  // Rotation au tour de X.
  gl.rotate(Math.PI * 0.3, 1, 0, 0);
  // Rotation au tour de Y.
  gl.rotate(angle, 0, 1, 0);
 
  // Dessin de l'image courante.
  gl.pushVaryingColorBuffer(lineBuffers);
  gl.pushPositionBuffer(lineBuffers);
  gl.drawLineArrays(lineBuffers);
}

Avec le code ci-dessus vous ne devez plus rien voir. Est-ce normal ?
Pour tenter de répondre à cette question, enlevez les commentaires de la ligne gl.translate(0.0, 0.0, -0.08);. Vous devez voir quelque chose comme
html5 05 03
La zone non dessinée correspond donc à ce qui se trouve avant le plan situé à z=-0,1.
Mais comment expliquer que sans translation nous ne voyons plus rien ?
Une explication peut-être que la figure n'est plus dans le cône.
Augmentons donc l'angle passé à la fonction perspective.
A priori ce n'est pas la solution car nous ne voyons toujours rien.
Si nous cumulons avec la translation à -0,08, l'effet est bien celui attendu.

La rotation initiale au tour de l'axe X introduit un doute quant au fait que notre scène puisse sortir du "cône" de visibilité. Eliminons se doute en mettant en commentaire cette rotation.

Sans cette rotation, il est maintenant certain que les axes X et Z, de notre référentiel dessiné, resteront toujours dans le plan XZ du contexte WebGL. En conséquence, quel que soit l'angle du cône, à un moment ou un autre nous devrions voir quelque chose. Rappelons que nous traçons la portion -0,5 à 0,5 de nos axes, nous dépassons donc sans problème le plan initial qui se trouve en -0,1 même lorsque le centre de nos axes est au centre du contexte WebGL.

Et bien même dans ces conditions, vous ne verrez probablement rien en dessous d'une translation inférieure à -0,002, où il faut déjà être particulièrement attentif (avec un fond noir c'est plus facile).
Pour expliquer ce phénomène il y a deux possibilités. De manière imagée, on peut considérer que cela est dû à un effet de loupe qui fait qu'un tout petit déplacement de notre référentiel dessiné, provoque une grande augmentation de la zone invisible. La zone visible semble alors repoussée à l'infini. De manière plus mathématique, les projections nous conduisent à diviser par un nombre qui tend vers 0, le résultat tend donc bien vers l'infini.
Pour le matérialiser, faite des translations de plus en plus petites. La limite pour voir quelque chose se situe vers -0,002 avec un angle de 45°.

Maintenant que nous avons introduit la perspective, et ses déformations, comment peut-on bouger la caméra ?
En fait on ne la bouge pas !
La chose coûterait trop cher en temps de calcul et n'est pas très simple à coder. Alors comment fait-on ?
En bougeant la scène dans le sens contraire.
En effet bouger la caméra vers la gauche revient à déplacer la scène vers la droite d'une même distance.
Pour les rotations, même constat.

Mais alors, qu'est-ce qui différentie un mouvement de caméra de l'animation "normale" de la scène" ?
Une seule chose.
Un mouvement de caméra est provoqué par l'utilisateur.
Si ce n'est pas le cas, il s'agit d'un film pas de la présentation d'un "monde 3D".

Nous introduirons l'interaction de l'utilisateur dans le prochain billet. En ce qui concerne le présent billet, nous allons simplement construire une scène un poil plus intéressante.
Déjà, grâce à la perspective, nous ne sommes plus cantonnés au petit monde initial que constitue le "cube" représentant l'espace 3D WebGL. Nous allons donc utiliser une plage plus importante pour notre monde virtuel. D'autre part il s'agirait d'avoir quelque chose à visualiser. Nous allons donc tracer une courbe.

Le code de notre nouvel page, WebGL_10.htm, est le suivant :

<!DOCTYPE HTML>
<html>
  <head>
    <!-- required to use WebGLHelper object -->
    <!-- <script type="text/javascript" src="http://iewebgl.com/scripts/webglhelper.js"></script> -->
    <script type="text/javascript" src="script/webglhelper.js"></script>
    <!-- La librairie de manipulation de matrices de Brandon Jones -->
    <script type="text/javascript" src="script/gl-matrix-min.js"></script>
    <!-- Notre librairie d'animation et d'aide WebGL -->
    <script type="text/javascript" src="script/WebGL_2.js"></script>
    <script type="text/javascript">
      function initBuffers(gl) {
        var lineBuffers = {}
 
        // Les points de nos axes.
        var points = [
          // X
          -10.0,  0.0,  0.0,
           10.0,  0.0,  0.0,
          // Y
           0.0, -10.0,  0.0,
           0.0,  10.0,  0.0,
          // Z
           0.0,  0.0, -10.0,
           0.0,  0.0,  10.0
        ];
        var x = 10.0;
        var y = -10.0
        var z = 0.0;
        var teta = 0.0;
        points = points.concat(x);
        points = points.concat(y);
        points = points.concat(z);
        while (true) {
          teta += Math.PI / 16;
          x = 10 * Math.cos(teta);
          y += 0.25;
          z = 10 * Math.sin(teta);
 
          points = points.concat(x);
          points = points.concat(y);
          points = points.concat(z);
 
          if (y >= 10.0)
            break;
 
          points = points.concat(x);
          points = points.concat(y);
          points = points.concat(z);
        }
 
        lineBuffers.positionBuffer = gl.createArrayBuffer(points);
 
        // Les couleurs de nos axes.
        var colors = [
          [1.0, 0.0, 1.0, 1.0], // X - Rose
          [1.0, 0.0, 0.0, 1.0], // X - Red
          [1.0, 1.0, 0.0, 1.0], // Y - Jaune
          [0.0, 1.0, 0.0, 1.0], // Y - Green
          [0.0, 1.0, 1.0, 1.0], // Z - Turquoise
          [0.0, 0.0, 1.0, 1.0]  // Z - Blue
        ];
        var colorVertices = [];
        for (var n in colors) {
          var color = colors[n];
          colorVertices = colorVertices.concat(color);
        }
        for (var i = 0; i < lineBuffers.positionBuffer.numElements - 6; i++) {
          colorVertices = colorVertices.concat([0.0, 0.0, 0.0, 1.0]);
        }
        lineBuffers.colorBuffer = gl.createArrayBuffer(colorVertices);
 
        return lineBuffers;
      }
 
      function stage(gl, lineBuffers, angle) {
        // Effacement de l'image précédente.
        gl.clear();
 
        // Initialisation des matrices.
        gl.identity();
 
        // Définition de la perspective.
        // "Ouverture" de l'objectif, 45°.
        // Zone visible, 0,1 à 100.
        gl.perspective(45, 0.1, 100.0);
 
        // Déplacement de l'objet.
        // On met l'objet dans le champs de vision.
        gl.translate(0.0, 0.0, -50.0);
        // Rotation au tour de X pour permettre la visualisation du plan XZ.
        gl.rotate(Math.PI * 0.1, 1, 0, 0);
        // Rotation au tour de Y.
        gl.rotate(angle, 0, 1, 0);
 
        // Dessin de l'image courante.
        gl.pushVaryingColorBuffer(lineBuffers);
        gl.pushPositionBuffer(lineBuffers);
        gl.drawLineArrays(lineBuffers);
      }
 
      function initWebGL(canvasElement) {
        // Création de notre classe d'aide.
        var gl = new WebGL(canvasElement);
 
        // Initialisation des shaders.
        gl.setShaderProgram("VARYING_COLOR");
 
        // Définition de la couleur de fond.
        gl.setClearColor(1, 0.98, 0.8, 1);
 
        // Définition des points et des couleurs.
        var lineBuffers = initBuffers(gl);
 
        // L'angle de rotation au tour de Y.
        var angle = 0;
 
        // Mise en place de la scène.
        gl.setStage(function () {
          // Calcule du nouvel angle.
          var angularVelocity = Math.PI / 4; // radians / second
          var angleEachFrame = angularVelocity * gl.getTimeInterval() / 1000;
          angle += angleEachFrame;
 
          stage(gl, lineBuffers, angle);
        });
 
        // Lancement de l'animation.
        gl.startAnimation();
      }
 
      function OnGLCanvasCreated(canvasElement, elementId) {
        window.setTimeout(function () { initWebGL(canvasElement); }, 500);
      }
 
      function OnGLCanvasFailed(canvasElement, elementId) {
        alert("Votre butineur ne supporte pas WebGL !");
      }
    </script>
  </head>
  <body>
    <script id="WebGLCanvasCreationScript" type="text/javascript" width="800" height="500" style="border:1px solid black;">
      WebGLHelper.CreateGLCanvasInline('glCanvas', OnGLCanvasCreated, OnGLCanvasFailed)
    </script>
  </body>
</html>

A l'écran cela donne :
html5 05 04
Notre courbe est donc une spirale, calculée avec un pas que vous pouvez modifier (dans le code) pour faire plus ou moins rond suivant ce que vous désirez, et ce que votre machine supporte.
Nous dessinons maintenant dans un volume plus important. Les coordonnées que nous utilisons sur chaque axe vont de -10 à +10.
La zone visible va de 0,1 à 100.
Pour faire simple, le centre de notre dessin à été placé en z=-50, au milieu de la zone visible.

Rendez-vous au prochain billet pour introduire les interactions utilisateur

Si vous souhaitez récupérer le code à jour, c'est ici

Publicité
Publicité
Commentaires
Fanfois et ses papillons
Publicité
Archives
Publicité