Création d'applications 3D

en utilisant les technologies du Web

Fivos Doganis | fivos.doganis@gmail.com

Intérêt du Web pour la 3D

  • expériences mobiles
  • technologies libres
    • cross-platform, pas d'installation, pas de store
    • ouvertes
    • non-propiétaires (contrairement à Unity ou Unreal)
    • gratuites
  • interfaçage avec de nombreuses APIs web existantes

Etapes

  • Run
  • Debug
  • Build
  • Modify
  • Create

Run: Demo

  • Vérifier que votre smartphone supporte le scan de QR code
  • iOS : app Camera par défault
  • Android
    • utiliser Google Chrome + bouton scan
    • ou installer une application fiable de scan de QR code comme Trend Micro
  • Solutions 100% web
    • webqr.com
    • qrcodescan.in

Debug

Build: outils

  • Développement web

    • navigateur (Firefox, Chrome, Safari Mobile)
    • Git (facultatif mais très utile)
    • éditeur (VSCode), ou Glitch (aucune installation!)
  • Technologies: HTML, JS, CSS, WebGL, THREE.js

  • Optionnel:

Installation du navigateur

  • Firefox installé par défaut
    • devrait suffir!
  • Chrome
    • pour tester la compatibilité et certaines fonctionnalités
    • installer la dernière version (97+) sur mobile
    • installer Chromium sur desktop
      • version open-source sans services propriétaires
sudo apt-get install chromium-browser

Installation de Git

sudo apt-get install git

git config --global user.name "myusername"
git config --global user.email myname@mymailprovider.com

Installation de l'éditeur VSCode

sudo apt update
sudo apt install software-properties-common apt-transport-https wget

wget -q https://packages.microsoft.com/keys/microsoft.asc -O- | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main"

sudo apt install code

Supprimer les warnings GPG

sudo gpgconf --kill dirmngr
sudo chown -R $USER:$USER ~/.gnupg

Customisation

  • Eviter clignottement de la UI en modifiant les préférences:
set window.titleBarStyle to custom
  • Format on Save

Développement en local

  • Impossible de charger un fichier depuis le disque sans action utilisateur
  • CORS: Cross Origin Resource Sharing
    • une des nombreuses mesures de sécurité des navigateurs web

➡️ nécessité de lancer un serveur, comme Five Server, ou encore:

$ cd /home/somedir
$ python -m SimpleHTTPServer

Puis aller sur http://localhost:8000

Glitch: éditeur interactif en ligne

Mais aussi Codepen.io, Repl.it etc.

Exercices WebGL

  • webglacademy
  • webglfundamentals
  • webgl2fundamentals

Vérifier que WebGL est supporté

Programme minimaliste: permet de tester que WebGL fonctionne
⚠️ aucun test d'erreur, pour plus de clarté (don't do this at home! 😉)

<!DOCTYPE html>
<html> 
<head>
    <meta charset='utf-8'>
</head>
<body>
    <canvas width='640' height='480'>
    <script>
        const canvas = document.querySelector('canvas');
        /** @type {WebGLRenderingContext} */
        const gl = canvas.getContext('webgl'); // instead of '2d'
        gl.clearColor(1., 0., 0., 1.); // RGBA: opaque red
        gl.clear(gl.COLOR_BUFFER_BIT); // uses current color (state machine)
    </script>
</body>
</html>

Etats

Programme minimaliste "utile": des shaders, sans vertex buffer

Ajouter ce code à la suite de gl.clear:

    // vertex shader
    const vs_source = `
    void main() {
        gl_Position = vec4(0., 0., 0., 1.);  // center
        gl_PointSize = 120.0;
    }`;

    // fragment shader
    const fs_source = `
    precision mediump float;

    void main() {
        gl_FragColor = vec4(0., 1., 0., 1.);  // green
    }`;

Suite du code: fonctions utiles (toujours sans test d'erreur!)

    function buildShader(gl, shaderSource, shaderType) {
        const shader = gl.createShader(shaderType); // Create the shader object
        gl.shaderSource(shader, shaderSource); // Load the shader source
        gl.compileShader(shader); // Compile the shader
        return shader;
    }

    function createProgram(gl, shaders) {
        const program = gl.createProgram();
        shaders.forEach(function(shader) {
            gl.attachShader(program, shader);
        });
        gl.linkProgram(program);
        return program;
    }

Suite et fin du code: chargement des shaders, du programme et rendu


    // load and compile the shaders
    const vs = buildShader(gl, vs_source, gl.VERTEX_SHADER);
    const fs = buildShader(gl, fs_source, gl.FRAGMENT_SHADER);
    
    // Create program on the GPU!
    const program = createProgram(gl, [vs, fs]);
    
    // Set current program (WebGL is a state machine!)
    gl.useProgram(program);

    // Draw 1 big point, see shaders 
    const offset = 0;
    const count = 1;
    gl.drawArrays(gl.POINTS, offset, count);
    

Etats

Mon premier triangle

voir webglfundamentals

Initialisation

1 . Data

const vertices = new Float32Array([
    0.5,  0.5,
    -0.5,  0.5,
    -0.5, -0.5]); // 2D points: 3 * (x, y) coordinates 
  • Création du Vertex Buffer Object (VBO) + UPLOAD vers le GPU
const vbo = gl.createBuffer(); // create Vertex Buffer Object (VBO) id
// Set current VBO: bind 'vbo' to the ARRAY_BUFFER bind point,
//  a global variable internal to WebGL (state machine!)
gl.bindBuffer(gl.ARRAY_BUFFER, vbo); 
// UPLOAD current VBO to GPU, where it will be processed by the shaders
// NOTE: STATIC_DRAW: hint for WebGL: our data won't change
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 

2 . Shaders

qui utilisent les données du buffer

  • Vertex Shader: gl_Position sera utilisée par le fragment shader
    attribute vec2 a_position; // IN, from buffer: 2D point
    void main() {
        gl_Position = vec4(a_position, 0.0, 1.0); // a_position.x, a_position.y, 0, 1, used by the fragment shader
    }
  • Fragment Shader
    precision mediump float; // float accuracy: lowp, mediump, highp
    uniform vec4 u_color; // UNIFORM == CONSTANT for entire shader program
                
    void main() {
        gl_FragColor = u_color; // final framebuffer color: RGBA
    } 

Détection des erreurs de compilation

À appeler avant glUseProgram, optionnel mais bien utile 😉

function checkShaders(gl, vs, fs, program) {
    if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) 
        console.error(gl.getShaderInfoLog(vs));
    
    if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) 
        console.error(gl.getShaderInfoLog(fs));
    
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) 
        console.error(gl.getProgramInfoLog(program));
}

3 . Connexion entre Buffer et Shaders

  • Récupération des variables utilisées dans les shaders

    • appel potentiellement coûteux
    • à effectuer à l'initialisation
    • sera utilisé lors du rendu
  • ⚠️ respecter le même nom que celui défini dans le shader

    • nom arbitraire, choisi par le développeur!
// Retrieve 'u_color' shader UNIFORM variable as an id
const u_colorLoc = gl.getUniformLocation(program, 'u_color');	

// Retrieve 'a_position' shader ATTRIBUTE variable as an id
const a_positionLoc = gl.getAttribLocation(program, 'a_position');

4 . Rendu!

Les buffers, les shaders sont en place, il faut encore:

  • définir des états
  • décrire le layout du buffer (souvent complexe!)
  • donner des instructions pour dessiner la scène
    gl.clearColor(0., 0., 0., 1.); // Set current clear color (black)
    gl.clear(gl.COLOR_BUFFER_BIT); // Clear the canvas to current color

    gl.useProgram(program); // Set current program (pair of shaders)
    
    // Set the color (constant triangle color, see shader)
    gl.uniform4fv(u_colorLoc, [1.0, 0.0, 0.0, 1.0]);
    // Tell WebGL how to take data from the VBO
    // and supply it to the attribute in the shader.
    gl.enableVertexAttribArray(a_positionLoc); // Turn the attribute on
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // set current vbo
    // Tell the attribute how to get data out of positionBuffer
    // (ARRAY_BUFFER)
    const size = 2; // 2 components per iteration
    const type = gl.FLOAT; // the data is 32bit floats
    const normalize = false; // don't normalize the data
    // stride: 0 = move forward size * sizeof(type) each iteration
    // to get the next position
    const stride = 0;        
    const offset = 0; // start at the beginning of the buffer
    gl.vertexAttribPointer(a_positionLoc, size, type, normalize, stride, offset);

    // primitiveType == gl.TRIANGLES:
    // each time our vertex shader is run 3 times,
    // WebGL will draw a triangle
    // based on the 3 values we set gl_Position to (see shader)
    const primitiveType = gl.TRIANGLES;
    
    // Start index of the first vertex
    // Must be a valid multiple of the size of the given type.
    const startIndex = 0;

    // Execute our vertex shader 3 times,
    // using 2 elements from the array (see size 2 above)
    // setting a_position.x and a_position.y
    const count = 3;
    gl.drawArrays(primitiveType, startIndex, count);

Etats

Presque!

Il faut ajouter une couleur par vertex
au lieu de la couleur de type uniform
cf. webglfundamentals

1 . Data

on ajoute un attribut de couleur par vertex
avec un nouveau tableau, et un nouveau buffer!

 // 2D points: 3 * (x, y) coordinates 
  // (sqrt(3) / 2 - 0.5) * 640 / 480 == 0.4880338..
const vertices = new Float32Array([-0.75,  -0.5,
                                    0.75,  -0.5,
                                    0., 0.49]);
// create Vertex Buffer Object (VBO) id
const vertexBuffer = gl.createBuffer(); 

 // set current VBO (WebGL is a state machine!)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);

// UPLOAD vertexBuffer VBO to GPU,
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); 


 // ** NEW **

 // RGB colors: 3 * (r, g, b, a) values
const colors = new Float32Array([   1., 0., 0., 1.,
                                    0., 1., 0., 1.,
                                    0., 0., 1., 1.]);  
                                    // create Vertex Buffer Object (VBO) id
const colorBuffer = gl.createBuffer(); 

 // set current VBO (WebGL is a state machine!)
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);

// UPLOAD colorBuffer VBO to GPU,
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);           

2 . Shaders

on utilise le nouvel attribut de couleur, que l'on nomme a_color
(ou mapetitecouleurparvertex mais ça fait moins pro)

  • Vertex Shader: nouvelle variable de type varying
    attribute vec2 a_position; // IN, from buffer: 2D point
    attribute vec3 a_color; // IN, from buffer: RGB color

    varying vec3 v_color; // OUT, to fragment shader

    void main() {
        v_color = a_color; // color passthrough, sent to fragment shader
        
        gl_Position = vec4(a_position, 0.0, 1.0); // used by the fragment shader
    }
  • Fragment Shader
    precision mediump float; // float accuracy: lowp, mediump, highp

    varying vec3 v_color; // IN, INTERPOLATED color from vertex shader
                         
    void main() {
        gl_FragColor = vec4(v_color, 1.); // final framebuffer color: RGBA
    } 

3 . Connexion entre Buffers et Shaders

  • Récupération des variables utilisées dans les shaders

    • appel potentiellement coûteux
    • à effectuer à l'initialisation
    • seront utilisées lors du rendu
  • ⚠️ respecter les noms définis dans le shader


// Retrieve 'a_color' shader ATTRIBUTE variable as an id
const a_colorLoc = gl.getAttribLocation(program, 'a_color');	

// Retrieve 'a_position' shader ATTRIBUTE variable as an id
const a_positionLoc = gl.getAttribLocation(program, 'a_position');

4 . Rendu!

Les buffers, les shaders sont en place, reste à:

  • définir des états
  • décrire le layout du buffer
  • donner des instructions pour dessiner la scène
    gl.clearColor(0., 0., 0., 1.); 
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.useProgram(program); // Set current program

{
    // Turn the attribute on
    gl.enableVertexAttribArray(a_positionLoc); 
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); // set current vbo
    // Tell the attribute how to get data out of positionBuffer
    // (ARRAY_BUFFER)
    const size = 2; // 2 components per iteration
    const type = gl.FLOAT; // the data is 32bit floats
    const normalize = false; // don't normalize the data
    // stride: 0 = move forward size * sizeof(type) each iteration
    // to get the next position
    const stride = 0;        
    const offset = 0; // start at the beginning of the buffer
    gl.vertexAttribPointer(a_positionLoc, size, type, normalize, stride, offset);
}
  • Mêmes étapes pour le colorBuffer
    NOTE: Refactoring! Dans la vraie vie on évite de se répéter: DRY
{
    // Tell WebGL how to take data from the VBO
    // and supply it to the attribute in the shader.
    gl.enableVertexAttribArray(a_colorLoc); // Turn the attribute on
    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    // Tell the color attribute how to get data out of colorBuffer (ARRAY_BUFFER)
    const size = 4; // NOTE: 4 components per iteration
    const type = gl.FLOAT; // the data is 32bit floats
    const normalize = false; // don't normalize the data
    // stride: 0 = move forward size * sizeof(type) each iteration
    // to get the next position
    const stride = 0;        
    const offset = 0; // start at the beginning of the buffer
    gl.vertexAttribPointer( a_colorLoc, size, type, normalize, stride, offset);
}
  • Rendu: code inchangé!
    // primitiveType == gl.TRIANGLES:
    // each time our vertex shader is run 3 times,
    // WebGL will draw a triangle
    // based on the 3 values we set gl_Position to (see shader)
    const primitiveType = gl.TRIANGLES;
    
    // Start index of the first vertex
    // Must be a valid multiple of the size of the given type.
    const startIndex = 0;

    // Execute our vertex shader 3 times,
    // using 2 elements from the array (see size 2 above)
    // setting a_position.x and a_position.y
    const count = 3;
    gl.drawArrays(primitiveType, startIndex, count);

Etats

💪 😎 🎉

Variantes

  // POINTS : position + color
  const vertexBuffer = new Float32Array([
    -0.5, -0.5, // vertex 1 : left
    1., 0., 0., // color 1 : red
    0.5, -0.5, // vertex 2 : right
    0., 1. , 0., // color 2 : green
    0., 0.48, // vertex 3 : top
    0., 0., 1. // color 3 : blue
  ]);

  const vbo = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
  gl.bufferData(gl.ARRAY_BUFFER, vertexBuffer, gl.STATIC_DRAW); // send to GPU

  // FACES : indices
  const indices = new Uint16Array([0, 1, 2]);
  var indexBuffer = gl.createBuffer ();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); // send to GPU

    gl.enableVertexAttribArray(a_colorLoc);
    gl.enableVertexAttribArray(a_positionLoc);
    gl.useProgram(program);
    gl.clearColor(0.0, 0.0, 0.0, 1.0);
    const animate = () => {
        gl.viewport(0.0, 0.0, canvas.width, canvas.height);
        gl.clear(gl.COLOR_BUFFER_BIT);

        gl.bindBuffer(gl.ARRAY_BUFFER, vbo);

        gl.vertexAttribPointer(a_positionLoc, 2, gl.FLOAT, false,4*(2+3),0) ;
        gl.vertexAttribPointer(a_colorLoc, 3, gl.FLOAT, false,4*(2+3),2*4) ;

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
        gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_SHORT, 0);
        gl.flush();
        window.requestAnimationFrame(animate);
    };
    animate();

Autres exemples

Optimisations

Vertex Array Object

  • optimise le temps de rendu: l'utiliser toujours!
  • histoire résumée des buffers, vocabulaire, structures, schémas
  • WebGL 1 (disponible partout)
 const ext = gl.getExtension("OES_vertex_array_object");
if (!ext) { // tell user they don't have the required extension or work around it
} else {
  const someVAO = ext.createVertexArrayOES();
}
  • WebGL 2
var someVAO = gl.createVertexArray();

// at init time
for each model / geometry / ...
    var vao = gl.createVertexArray();
    gl.bindVertexArray(vao);
    for each attribute
    gl.enableVertexAttribArray(...);
    gl.bindBuffer(gl.ARRAY_BUFFER, bufferForAttribute);
    gl.vertexAttribPointer(...);
    if indexed geometry
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bindVertexArray(null);
// draw
    gl.bindVertexArray(vaoForGeometry); // only 1 DrawCall
// clean
    gl.bindVertexArray(null); //Always unbind the VAO when you're done

Optimisations

Quelle que soit l'API!

  • Profiling
  • "Batching":
    • Texture Atlas
    • Degenerate Triangle Strips

Plus de shaders

The Book of Shaders

PixelSpiritDeck

ShaderToy

Vertex Shader Art


Editeurs en ligne

APIs plus avancées

  • WebGL 2.0
    • nouveaux shaders: in, out, flexibilité, draw_buffers, UBO
  • WebGPU
    • généralise les concepts des VAO à tous les attributs et les états
  • THREE.js ⭐ ⭐ ⭐ ⭐ ⭐
    • API de plus haut niveau: abstraction de WebGL! 🎉
      • SceneGraph (comme OpenInventor, Unity ou Blender) granularité: buffers triangles objets 3D!
    • migration transparente vers WebGL2, WebGPU etc.

WebAssembly (Wasm)

  • Compiler vers le web

{ C++, OpenGL } ➡️ emscripten ➡️ { HTML, JS (Wasm), WebGL }

  • C++
#include <functional>

#include <emscripten.h>
#include <SDL.h>

#define GL_GLEXT_PROTOTYPES 1
#include <SDL_opengles2.h>

//...

// an example of something we will control from the javascript side
bool background_is_black = true;

// the function called by the javascript code
extern "C" void EMSCRIPTEN_KEEPALIVE toggle_background_color() { background_is_black = !background_is_black; }

std::function<void()> loop;
void main_loop() { loop(); }

int main()
{
    SDL_Window *window;
    SDL_CreateWindowAndRenderer(640, 480, 0, &window, nullptr);


//...
 
    loop = [&]
    {
        // move a vertex
        const uint32_t milliseconds_since_start = SDL_GetTicks();
        const uint32_t milliseconds_per_loop = 3000;
        vertices[0] = ( milliseconds_since_start % milliseconds_per_loop ) / float(milliseconds_per_loop) - 0.5f;
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

        // Clear the screen
        if( background_is_black )
            glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        else
            glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw a triangle from the 3 vertices
        glDrawArrays(GL_TRIANGLES, 0, 3);

        SDL_GL_SwapWindow(window);
    };

    emscripten_set_main_loop(main_loop, 0, true);

    return EXIT_SUCCESS;
}
  • HTML / JavaScript
<body>

    <!-- Create the canvas that the C++ code will draw into -->
    <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>

    <!-- Allow the C++ to access the canvas element --> 
    <script type='text/javascript'>
        var canv = document.getElementById('canvas');
        var Module = {
            canvas: canv
        };
    </script>
    
    <!-- Call the javascript glue code (index.js) as generated by Emscripten -->
    <script src="index.js"></script>
    
    <!-- Allow the javascript to call C++ functions -->
    <script type='text/javascript'>
        canv.addEventListener('click',    _toggle_background_color, false);
        canv.addEventListener('touchend', _toggle_background_color, false);
    </script>

    <!-- Describe what the user is seeing -->
    <p>Click the canvas to change the background color.</p>
    <hr>
    <p>Minimal example of animating the HTML5 canvas from C++ using OpenGL through WebAssembly.</p>
    <p>Source code: <a href="https://github.com/timhutton/opengl-canvas-wasm">https://github.com/timhutton/opengl-canvas-wasm</a></p>

</body>

Compilation

  • Installer Emscripten

  • Générer index.js et index.wasm:

emcc main.cpp -std=c++11 -s WASM=1 -s USE_SDL=2 -O3 -o index.js
  • Ouvrir index.html (avec server)

Résultat

I implemented WebGL in Chrome and my name is on the spec so I have some clue how it works

TL;DR
If you want to get stuff done use three.js.

Gregg Tavares (@greggman)

PAUSE

🍜

reprise à 13h30

THREE.js

Aperçu interactif
by
David Lyons
https://davidlyons.dev/threejs-intro

Setup

Setup

  • THREE.js est une librairie, PAS une API comme WebGL
  • THREE.js encapsule WebGL 1, 2 et WebGPU
  • Il faut donc importer les modules avant de coder
    • soit en utilisant des CDN (Content Delivery Network)
      • ➡️ permet des tests rapides
    • soit en passant par une installation complète (via Node.js)
      • ➡️ permet d'accéder à plus de resources, moyennant une toolchain complexe (npm, webpack, parcel⭐ etc.)
      • téléchargement zip déconseillé (dépendances complexes)

Setup: CDN

 <!DOCTYPE html>
 <html lang="en">
    
    <head>
        <title>three.js</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    </head>
    
    <style>
      html, body { margin: 0; padding: 0;  overflow: hidden; }
    </style>
    
    <body>
        <!-- Import maps polyfill -->
        <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
        <script type="importmap">
          {
            "imports": {
              "three": "https://unpkg.com/three@0.148.0/build/three.module.js",
              "three/addons/": "https://unpkg.com/three@0.148.0/examples/jsm/"
            }
          }
        </script>

        <script type="module">

            // Example of hard link to official repo for data, if needed
            const MODEL_PATH = 'https://raw.githubusercontent.com/mrdoob/three.js/r148/examples/models/gltf/LeePerrySmith/LeePerrySmith.glb';

            import * as THREE from 'three'

            import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
            import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

            // INSERT CODE HERE

        </script>
    </body>
</html>

const scene = new THREE.Scene();
const aspect = window.innerWidth / window.innerHeight;
const camera = new THREE.PerspectiveCamera( 75, aspect, 0.1, 1000 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshNormalMaterial();
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;

const render = () => {
  requestAnimationFrame( render );
  cube.rotation.x += 0.1;
  cube.rotation.y += 0.1;
  renderer.render( scene, camera );
};

render();

Setup: NPM

sudo apt install nodejs
curl -L https://npmjs.org/install.sh | sudo sh 
  • installer parcel (voir THREE Parcel plus bas pour installation auto)
npm install --save-dev parcel
  • installer three 🎉
npm install three

THREE.js IntelliSense

  • accès facile à la documentation des classes THREE.js
  • autocompletion
  • vérification des types, même en JavaScript
npm install @types/three --save-dev

Utilisation de Parcel

  • appeler le bundler directement
npx parcel src/index.html
  • ou modifier package.json
{
  "name": "my-project",
  "source": "src/index.html",
  "scripts": {
    "start": "parcel",
    "build": "parcel build"
  },
  "devDependencies": {
    "parcel": "latest"
  }
}

npm start

THREE Parcel boilerplate +

Environnement THREE.js + Parcel préconfiguré!

https://github.com/fdoganis/three_parcel

git clone git@github.com:fdoganis/three_parcel.git

cd three_parcel

npm install

Lancer avec npm start ou utiliser VS Code F5

Ouvrir http://localhost:1234 avec le navigateur

Autres boilerplates

Bundler wars 💥

THREE.js avancé

THREE.js avancé

Good coding practices

Helpers

Debugging tips 🐛

Extra

Physique

Modèles 3D

Modification et conversion

Textures

Exercices THREE.js

Exercices THREE.js

PAUSE

30' ⌛

Projets THREE.js

Exemples

  • Officiels

    • à jour!
    • recommandés!
      • ⚠️ l'API de THREE.js change très souvent!
      • l'API JavaScript aussi...
  • Lee Stemkoski

    • datent (code à rafraîchir)

Idées de projets

Fun

  • Adapter du code existant pour exploiter THREE
    • jeux de plateau
    • jeux "Flash"
    • jeux videos connus (mini-games, party games)
    • ajout de son, de physique

Autres idées

GIS Links

http://www.itowns-project.org/itowns/examples/index.html#3dtiles_25d

http://www.itowns-project.org/itowns/examples/#source_stream_wfs_3d

https://geoservices.ign.fr/documentation/services/utilisation-web/affichage-wmts/cesiumjs-et-wmts

https://codepen.io/photonlines/pen/JzaLYJ

https://douglasduhaime.com/posts/visualizing-tsne-maps-with-three-js.html

https://openlayers.org/en/latest/examples/wmts.html

https://www.3ds.com/insights/customer-stories/rennes-metropole

https://www.usinenouvelle.com/article/simulation-de-singapour-a-rennes-comment-la-ville-tire-profit-de-son-double-virtuel.N1018329

https://docs.mapbox.com/mapbox-gl-js/example/3d-buildings/

https://osmbuildings.org

https://github.com/Microsoft/USBuildingFootprints

https://wiki.openstreetmap.org/wiki/Simple_3D_buildings

https://threejs.org/examples/#misc_controls_map

Extrude using geo json:
https://threejs.org/examples/webgl_geometry_extrude_shapes2.html

From height field to 3D grid mesh
https://blog.mastermaps.com/2013/10/terrain-building-with-threejs.html?m=1

Infinitown
https://demos.littleworkshop.fr/infinitown

OSMBuildings 3D
https://github.com/OSMBuildings/OSMBuildings

OpenLayers
https://openlayers.org

Leaflet
https://leafletjs.com

More:
https://www.sitepoint.com/3d-maps-with-eegeo-and-leaflet/

Other tech:
https://www.igismap.com/everything-about-web-3d-map-examples-tools-library-and-uses/

Terrain
https://felixpalmer.github.io/procedural-gl-js/docs/

3D Tiles (RealityCapture) ⭐

Wrappers

React Three Fiber (R3F)

A-Frame

ECS

Tests

const THREE = require('three');
const assert = require('assert');

describe('The THREE object', function() {
  it('should have a defined BasicShadowMap constant', function() {
    assert.notEqual('undefined', THREE.BasicShadowMap);
  }),

  it('should be able to construct a Vector3 with default of x=0', function() {
    const vec3 = new THREE.Vector3();
    assert.equal(0, vec3.x);
  })
})

Fin!

**************************

Voir Readme / TODO

**************************

![bg 100%](https://ipfs.io/ipfs/Qmd91vgr2dxRxFdaaRRVWNmqhfqejB2UbRRgsEnbyPxWP4)

![bg 140%](https://images.unsplash.com/photo-1589431882969-f74b941beb0e)

![bg](https://images.unsplash.com/photo-1505299344687-ee45ad431f9b)

<!-- _footer: photo de couverture: Mitchell Kavan https://unsplash.com/@mtk

<!-- _footer: photo de couverture: Steven Ramon https://unsplash.com/@ssttevven

https://www.youtube.com/watch?v=zBJXXcFy6iU

https://gitlab.inria.fr/Loki/PolyphonyECS