Nothing-is-3D

portfolio de Vincent Lamy

Blender|BabylonJS|lang-fr

De Blender vers BabylonJS

11 commentaires

(logiciels utilisés : Blender 2.79b, BabylonJS 3.3.0)

( english version available)

Cet article va prendre le prétexte d'une toute petite scène pour présenter un workflow simple de Blender vers BabylonJS, comprenant la gestion des lightmaps, afin de tendre vers un éclairage réaliste.

(cliquez pour lancer)
lien direct vers la démo

Les non-utilisateurs de Blender resteront peut-être intéressés par ce tutorial, puisque les grandes lignes de modélisation, unwrapping et gestion de la scène et ses objets restent les mêmes peu importe le logiciel, et aussi bien entendu la partie BabylonJS (BJS de son petit nom) qui est totalement indépendante du modeler utilisé.

Nous allons toucher des mains le webGL, donc pour les infographistes habitués des moteurs en vogue comme Unreal Engine ou Unity3D, autant vous prévenir maintenant, voici les gros points qui vont vous faire mal :

  • les outils d'éditions et de réglages côté moteur 3D : beaucoup moins accessibles et ergonomiques
  • la gestion de l'éclairage par les lightmaps : à l'ancienne, toute la chaine est à gérer par le graphiste
  • le code ! Et oui, il va falloir pondre du javascript, en plus de quelques bouts de html/css (faire une appli webGL c'est faire un site web après tout)

Le workflow en quelques mots

Téléchargement des sources

Les sources du projet sont disponibles ici. Vous y trouverez :

  • le dossier 3D, contenant les fichiers blend et les textures originales
  • le dossier BJS, contenant l'espace de travail BabylonJS et les exemples du tutoriel

Les bases de la 3D temps réel

Je ne m'attarderais pas ici sur les méthodes de modélisation et de texturing, qui conservent les mêmes logiques peu importe le moteur qu'on utilise. Je pars du postulat que vous avez déjà ces notions de base (liste non-exhaustive) :

  • unité de base au mètre
  • modélisation orientée... temps réel évidemment
  • textures en puissance de 2
  • nommage correct et rigoureux de tous les assets

Il est bien sur conseillé d'avoir regardé et testé les Babylon 101 et les How to... de BabylonJS.

Standard vs PBR

Pour cet exemple je ne suis pas en workflow PBR mais Standard. Ceci vient du fait que l'exporteur officiel Blender-vers-BJS ne gère pas encore la conversion en PBR des matériaux. Il est aussi encore utile de savoir se débrouiller avec le Standard Workflow si l'on vise des machines utilisateurs peu puissantes (tout le monde n'a pas le dernier smartphone à la mode).

Il est cependant possible de travailler en PBR en exportant au format glTF, ce workflow est encore en phase expérimentale et soumis à quelques bugs, mais il fera l'objet d'une suite à ce tuto.

L'éclairage

Puisqu'il n'y a pas de moteur de rendu raytracing dans BabylonJS, je vais devoir faire mon éclairage sous un moteur précalc', comme Cycles. Cela m'oblige à créer deux versions de ma scène :

  • la scène 3D temps réel, qui est en fait le fichier de travail principal et d'où la scène BJS est exportée, avec les matériaux standards Blender Render
  • la scène 3D précalculée, qui va être dédiée à la création de lightmaps, avec les matériaux et les lights Cycles

L'édition de la scène BabylonJS

Les développeurs de BabylonJS nous proposent un éditeur, mais il possède des limitations qui ne me conviennent pas au quotidien, en particuler le fait que si je fais un nouvel export de ma scène Blender, il me faut recommencer tous mes réglages côté éditeur. Pourquoi pas lorsqu'il s'agit de jouer sur une micro-scène en one-shot, mais en production classique avec des exports & réexports permanents, ça n'est pas utilisable en l'état. Éditeur à surveiller tout de même, puisque souvent mis à jour.

J'effectuerais donc le plus de réglages possibles dans la scène Blender directement, puis une fois dans BJS j'utiliserais l'outil Inspector pour peaufiner tout ça, et reporterais les données... en javascript.

L'optimisation

Pour ce tutoriel je n'irais pas très loin dans les différents techniques d'optimisation, puisque ce n'est pas le sujet, je ferais donc le minimum nécessaire. Sur un scène complexe, la moindre optimisation peut avoir son importance (liste non-exhaustive) :

  • maillage (et donc poids) des meshes
  • taille des textures (peuvent se réduire en fin de projet)
  • nombre de lampes dynamiques
  • dépliage serré - avec parcimonie - des UV lightmaps
  • jonction des objets & partage des matériaux

Côté Blender

Scène temps réel (Blender Render)

L'organisation de la scène

On est en temps réel, donc évidemment les objets doivent avoir un nom explicite, tachez de faire attention à ça, c'est important pour la suite lorsqu'on s'amusera avec du code.

Les objets sont séparés en calques pour rendre plus fluide nos opérations.

blender-layers

La modélisation

Rien d'exceptionnel ici. Pensez juste à préparer vos seams et sharp edges quand un mesh est terminé, pas besoin de s'y replonger après coup.

modeling-stats

On devra évidemment enchainer non seulement sur le dépliage des UV classiques, mais aussi ceux dédiés aux lightmaps :

uv-channels

UVMap pour le texture tiling, UV2 pour les futures lightmaps

Ne pas perdre du temps à soigner les UV2 pour le moment, il s'agit juste de vérifier que le dépliage automatique se comporte bien (Unwrapping ou SmartUnwrap). Les UV1 peuvent être définitifs.

La préparation pour les lightmaps

Les objets vont être attachés ensemble selon la distribution désirée des lightmaps (là ça dépend vraiment de chaque projet), mais il va falloir réfléchir à plusieurs choses :

  • si des objets partagent les mêmes matériaux, on tentera évidemment de les réunir
  • certains objets n'auront pas besoin de lightmaps, et seront même gênants lors du rendu Cycles, on pourra donc les attacher ensemble, et veiller à utiliser un mot-clef permettant de les repérer par code une fois dans le moteur 3D
  • notez que si l'on souhaite être absolument rigoureux sur la répartition de surface des lightmaps, on peut s'embêter à calculer les texels afin de conserver une harmonie entre tous les différents objets et la résolution de leur lightmap, mais pour cette scène je me le fais au feeling.

Il serait aussi possible de laisser détachés les objets et donc d'avoir des UV2 partagés, mais le workflow se compliquerait aussi bien du côté Blender que du côté Babylon.

fnt-UV2

  • dans la planche UV, notez la face selectionnée : il s'agit de la surface arrière du tableau, scalée à 0.05 puisqu'il est inutile qu'elle utilise de la place sur la future ligthmap
  • on peut remarquer qu'il reste un peu de vide : des plugins Blender aideraient à mieux répartir tout ça, mais dans une contrainte de rapidité de prod' en milieu pro', il n'est pas toujours évident d'atteindre la perfection. Ici le déplié reste satisfaisant.

noLM-objects

mes deux objets exclus des lightmaps, utilisant le mot-clef noLM (pour no LightMap) : holdout.noLM.000 et furnitures.noLM.000

L'exportation de la scène

À ce stade, il est déjà possible de faire un premier export vers BabylonJS, même si nos lightmaps n'existent toujours pas. Vous pouvez déjà lire la doc' officielle à propos de l'exporteur (j'en ai écrit une partie, n'hésitez pas à me faire des retours et suggestions).

Si vous n'avez encore de setups BabylonJS sous la main, n'oubliez pas l'existence de l'éditeur, ou même de la sandbox, qui vous permettront de regarder votre 3D sous tous les angles en deux temps, trois mouvements.

L'export en lui-même n'est vraiment pas compliqué. Comme vous l'avez vu dans la doc' officielle, les réglages se situent dans les propriétés des meshes directement (ou du world, ou du material, etc).

export

Nous aurons alors un rendu moche, mais qui servira :

  • à repérer d'éventuels bugs et à les corriger avant de commencer à jongler entre les scènes.
  • à commencer du réglage, mais forcément très basique et succin puisque l'ambiance sera modifiée lors de l'ajout des lightmaps

export-without-lightmaps

export brut de Blender vers la Babylon Sandbox

Scène précalculée (Cycles)

Lorsque notre scène temps réel est prête, il est temps de s'occuper de notre ambiance lumineuse. Il va être question ici de se concentrer sur l'éclairage et non sur le peaufinage des matériaux. En fait, nous allons même écraser la plupart des matériaux en les remplaçant par un seul tout simple et tout basique.

Les objets

La première action à affectuer dans notre nouvelle scène Cycles vide est évidemment d'importer nos objets dedans.

Plusieurs type d'actions existent, dépendant de vos préférences :

  • Ctrlc puis Ctrlv, rapide et efficace
  • le File > Append, qui peut être intéressant
  • le File > Link qui serait le workflow parfait, mais je n'ai pas encore trouvé l'astuce idéale pour ça
    • pour les curieux, la piste la plus solide serait de lier les matériaux Cycles au niveau de l'objet plutôt que de ses data (ou vice-versa), mais mon plugin BakeTool n'a pas l'air d'apprécier.

Dans le cas où vous mettez à jour la scène Cycles, vous aurez déjà présentes les anciennes version d'objets. Avant d'importer les nouvelles versions, supprimez les anciennes et n'oubliez pas de supprimer les Orphan Data via l'Outliner.

Là encore, j'utilise soigneusement les calques, avec certains roles assignés :

  • les objets lightmappés, qui seront potentiellement à mettre à jour de temps en temps
  • les objets dédiés au rendu mais ne recevant pas de lightmaps (abat-jour par exemple)
  • les lights et les caméras

Les matériaux

Puisqu'au baking seul l'impact lumineux nous intéresse, il n'est pas vraiment utile de se coltiner une conversion de tous nos matériaux Blender Render vers Cycles. Sur une petite scène comme ça, cela reste envisageable mais sur une scène avec plus d'une centaine de matériaux y'a de quoi s'arracher les cheveux - surtout s'il nous faut faire une mise à jour de toute la géométrie deux jours plus tard.

C'est pourquoi nous allons créer un matériau Cycles par défault, tout simple, nommé par exemple _cycles_default_ :

cycles-default-material

Afin d'éviter de le perdre lors d'un éventuel Orphan Data clean, on le met en Fake User.

Ensuite, il est possible - grâce au plugin Material Specials - d'écraser et d'assigner ce matériaux à tous nos objets : ShiftQ > Assign Material > _cycles_default_

Oui mais, et le color bleeding ? En effet on le perd totalement avec cette technique. On peut alors sélectionner les éléments d'importance et créer des matériaux particuliers. Dans ma scène, j'ai choisi de conserver mon parquet ; mais là encore le matériau est tres simple puisqu'il s'agit tout simplement de la diffuse connectée au shader Diffuse BSDF.

cycles-default

Une petite capture vidéo du process sera peut-être bienvenue :

lien direct vers la vidéo

L'éclairage

Le world output est on-ne-peut-plu' simple, mais suffisant pour cette scène :

cycles-world

La light est toute simple également :

cycles-light-nodes

En revanche, en ayant placé la light à son emplacement logique (à savoir au milieu de l'ampoule), nous nous confrontons à un problème évident : la lumière ne passe pas à travers l'ampoule et l'abat-jour.

cycles-light-position

Pour l'ampoule le problème est très simple à régler, il suffit de lui assigner un shader entièrement transparent. Pour l'abat-jour, le matériaux n'est finalement pas beaucoup plus complexe, on apporte juste une couleur unie proche du tissu de l'abat-jour :

cycles-light-materials

notez l'activation du mode Fake User sur ces matériaux, afin de ne pas les perdre en cas de mise à jour des meshes

Pour réduire le bruit au rendu , se référer à la doc' Blender officielle.

On a maintenant un éclairage qui ressemble à quelque chose, on va pouvoir passer au baking des lightmaps.

cycles-lighting-preview

Le baking

Ici j'utilise Baketool, qui n'est pas très compliqué à prendre en main. Si vous souhaitez utiliser le workflow par défaut, voir la doc' Blender officielle.

Préparez votre liste d'objets et bakez sur leur second canal UV.

Le point important ici est de sélectionner la passe de Diffuse sans sa Color. En effet, nous ne désiront que l'impact lumineux direct et indirect.

baketool-setup

configuration du baking avec baketool

cycles-lighting-bake-settings

pour info, l'option de baking par défaut de Blender

Lancez le calcul.

lightmaps-baked-and-uv

admirez ce magnifique espace perdu pour la lightmap des murs, huez moi

Il est possible de tolérer un certain niveau de bruit au rendu (puisque le baking prend beaucoup plus de temps de calcul qu'un rendu classique), puis de faire une passe de denoise ensuite :

ligthmap-denoise

filtre Denoise sous Gimp

Enregistrez vos lightmaps finales dans votre dossier BJS/assets/lightmaps/.

Côté BabylonJS

Comme vu plus haut, il vous est possible de ne pas vous prendre la tête avec du code en utilisant l'éditeur officiel. Soyez juste certain de ne pas avoir à mettre à jour votre fichier exporté.

Notez aussi qu'il vous faut avoir accès à un serveur web. Vous trouverez un peu d'aide sur cette page.

Mais pour ce tuto je vais utiliser la méthode en partant de zéro. Néanmoins même si vous n'êtes pas très à l'aise avec le code, le copier-coller est toujours possible. Notez que je ne suis pas développeur pro', et qu'il est possible que mon code soit à peaufiner (me faire des retours si c'est le cas ;) )

Préférez afficher votre scène en mode navigation privée, pour éviter les problèmes de cache, et sachez qu'avoir la console ouverte est quand même très utile (en allant dans les settings de celle-ci, vous pouvez forcer la non-mise en cache).

L'export/import de notre scène

Pour éviter de se retrouver avec un tas de fichiers en vrac à la racine de notre future application 3D web, on va organiser une petite arborescence :

Arborescence Fonction
filetree
  • BJS : le dossier racine de notre projet webGL
    • assets : c'est ici qu'on va exporter notre fichier .babylon
      • lightmaps : contient nos lightmaps
    • js : les fichiers javascripts
    • index.html : notre fichier principal où va se trouver notre code

Vous pouvez déjà exporter le fichier babylon dans le dossier assets, les textures nécessaires y seront copiées automatiquement. Placez dans le dossier dédié les lightmaps.

Plutôt que de lier le moteur BabylonJS sur une version en ligne, je pense qu'il est toujours préférable de rapatrier une version en local (placée dans le dossier js). Le reste de notre javascript sera ici écrit directement dans notre fichier html principal, mais il serait plus propre de l'écrire dans un fichier dédié placé dans ce dossier js lui aussi.

L'index.html est l'endroit où tout va se passer. Commencez par créer un fichier texte vide nommé ainsi, puis éditez-le.

Toujours en s'aidant de la doc' BJS officielle, on arrive vite à notre premier chargement de scène 3D :

<!doctype html>
<html>
<head>
    <title>From Blender to Babylon - standard workflow</title>
    <meta charset="UTF-8">
    <script src="https://www.nothing-is-3d.com/js/babylon.js"></script>
    <style>
        html, body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            font-family: tahoma, arial, sans-serif;
            color:white;
        }

        #canvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>
<body>
    <canvas id="canvas"></canvas>
    <script type="text/javascript">
        var canvas = document.getElementById("canvas");
        var engine = new BABYLON.Engine(canvas, true);
        var scene = new BABYLON.Scene(engine);
        // ArcRotateCamera doc, you can use FreeCamera if you prefer:
        // http://doc.babylonjs.com/api/classes/babylon.arcrotatecamera#constructor
        var arcRotCam = new BABYLON.ArcRotateCamera(
            "arcRotateCamera", 1, 1, 4,
            new BABYLON.Vector3(0, 1, 0),
            scene
        );
        arcRotCam.attachControl(canvas, true);

        // SceneLoader doc :
        // http://doc.babylonjs.com/api/classes/babylon.sceneloader#append
        BABYLON.SceneLoader.Append(
            "assets/",
            "scene-BJS.babylon",
            scene
        );

        engine.runRenderLoop(function () {
            scene.render();
        });

        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>
</html>

Dans les balises <head> rien de particulier : on indique le chemin du moteur, et on informe le navigateur que l'on souhaite que la partie 3D (canvas) remplisse toute notre page.

Dans les balises <body> ce qui nous intéresse particulièrement ici est la fonction BABYLON.SceneLoader.Append qui nous permet d'intégrer notre 3D. Mais on aura auparavant :

  • instancié le moteur 3D var engine = new BABYLON.Engine(canvas, true);)
  • créé une scène en utilisant ce moteur var scene = new BABYLON.Scene(engine);
  • créé une caméra

Les valeurs de positions caméra que j'ai renseigné ci-dessus méritent d'être peaufinées. Après avoir regardé du côté de la doc', je me débrouille pour que :

  • ma vue initiale soit correcte : var arcRotCam = new BABYLON.ArcRotateCamera("arcRotateCamera", 5.5, 1.2, 3.75, new BABYLON.Vector3(-0.8, 0.75, 0.8), scene);
  • mes coups de molettes dézomment moins violemment : arcRotCam.wheelPrecision = 200;
  • mon clipping min soit adapté à la scène : arcRotCam.minZ = 0.005; ( = 5 mm)

Nous voici donc avec notre premier chargement de scène !

BJS-first-load

voir tuto01.html

Avant de s'attaquer aux réglages matériaux, il nous reste à gérer les lightmaps et à les assigner de façon automatique à nos objets.

Les lightmaps

Ici on va monter d'un cran la complexité du code javascript, parce qu'il va nous falloir :

  1. boucler parmis nos objets et exclure ceux qui n'ont pas de lightmaps
  2. charger les lightmaps existantes
  3. assigner les bonnes lightmaps à chaque matériau
  4. effectuer du réglage de base des matériaux pour un affichage correct des lightmaps

Pour être certain que nos objets existent lorsqu'on appelera notre fonction décrite ci-dessus, il va falloir nous placer dans le onSuccess de notre SceneLoader. Exemple avec le code ci-dessous, où le file loaded! ne s'affichera dans la console que lorsque tout le contenu de notre fichier .babylon sera chargé :

BABYLON.SceneLoader.Append(
    "assets/",
    "scene-BJS.babylon",
    scene,
    function(){
        console.log("file loaded!")
    }
);

voir tuto02.html (regardez la console)

On va donc faire nos bouts de code dans cette fonction. Mais avant le SceneLoader, j'ai besoin d'une fonction d'assignation de la lightmap à un material, qui sera appelée ici assignLightmapOnMaterial.

Voici donc la chose :

function assignLightmapOnMaterial(material, lightmap) {
    material.lightmapTexture = lightmap;
    // we want using UV2
    material.lightmapTexture.coordinatesIndex = 1;
    // our lightmap workflow is a darken one
    material.useLightmapAsShadowmap = true;
}

BABYLON.SceneLoader.Append(
    "assets/",
    "scene-BJS.babylon",
    scene,
    function () {
        /** LIGHTMAP ASSIGNATION PROCESS **/
        // lightmapped meshes list
        var lightmappedMeshes = ["wallz.000", "furnitures.000"];
        // we start cycling through them
        for (var i = 0; i < lightmappedMeshes.length; i++) {
            var currentMesh = scene.getMeshByName(lightmappedMeshes[i]);
            // lightmap loading
            var currentMeshLightmap = new BABYLON.Texture(
                "assets/lightmaps/" + currentMesh.name + "_LM.jpg",
                scene
            );
            currentMeshLightmap.name = currentMesh.name + "_LM";
            // we start cycling through each mesh material(s)
            if (!currentMesh.material) {
                // no material so skipping
                continue;
            } else if (!currentMesh.material.subMaterials) {
                // no subMaterials
                assignLightmapOnMaterial(
                    currentMesh.material,
                    currentMeshLightmap
                );
            } else if (currentMesh.material.subMaterials) {
                // we cycle through subMaterials
                for (var j = 0; j < currentMesh.material.subMaterials.length; j++) {
                    assignLightmapOnMaterial(
                        currentMesh.material.subMaterials[j],
                        currentMeshLightmap
                    );
                }
            }
        }
        /** END OF LIGHTMAP ASSIGNATION PROCESS **/
    }
);

Si vous souhaitez faire usage du copier-coller sans analyse, ne vous concentrez que sur cette partie : var lightmappedMeshes = ["wallz.000", "furnitures.000"]; où il vous faut renseigner le nom de vos meshes à lightmapper.

Evidemment sur une scène avec des dizaines ou centaines d'objets il vous sera impossible de maintenir une telle liste manuellement, mais automatiser tout ça rendrait le code moins compréhensible pour un néophyte du javascript, j'ai donc fait ce choix pour ce tutorial.

firfst-lightmap-assignation

voir tuto03.html

Bon alors on se rend vite compte ici qu'il manque un réglage sur notre rendu. Quand on a activé le paramètre useLightmapAsShadowmap dans nos matériaux lightmappés, on a l'ambientColor de la scene qui agit en multiply. Ainsi, si cette couleur est grise, la plus haute valeur d'ambient de nos matériaux, même si celle-ci est blanche, sera grise aussi (clampée par la scene.ambientColor).

On passe donc cette ambient en blanc, dans notre sceneLoader :

scene.ambientColor = BABYLON.Color3.White();

firfst-lightmap-assignation2

voir tuto04.html

L'éclairage dynamique

Vous aviez peut-être déjà placer une pointLight dans Blender ? (Creez-en une si non) Celle-ci est bien exportée et agit déjà sur vos matériaux, sur les diffuse & specular color :

Blender BabylonJS
blender-bjs-light dynlight-exported

Tout comme les matériaux, le plus vous préréglez dans Blender, le plus simple c'est. Pour des réglages plus avancés, vous devriez comprendre comment faire une fois la partie sur les réglages matériaux effectuée.

Les matériaux

Pour connaitre tous les paramètres qui nous sont accessibles, comme d'hab'... il faut regarder la doc'. N'oubliez pas que le plus simple est d'utiliser les paramètres accessibles directement via Blender.

Ainsi donc, comment accèder à un matériau en javascript ? Tout d'abord, nous allons lancer l'outil Inspector, qui va nous permettre d'accèder à des réglages de scène avec une UI.

On va donc placer l'appel de cet outil à la fin de notre fonction de sceneLoader :


[...]

        }
        /** END OF LIGHTMAP ASSIGNATION PROCESS **/

        /* tools */
        scene.debugLayer.show();
    }
);

[...]

Mettons qu'il nous tienne à coeur de passer notre mur blanc en rose. Il nous suffit d'aller dans l'onglet Material, de trouver notre matériau (utiliser le Filter by name...), de trouver notre ambientColor et de la régler.

pink-wall

Vous rechargez alors votre page pour vérifier que ça soit bien pris en compte... et là c'est le drame : évidemment, notre réglage s'est perdu en route.

On va donc créer une variable javascript correspondant à notre matériau, et lui assigner en dur notre paramètre :


[...]

        }
        /** END OF LIGHTMAP ASSIGNATION PROCESS **/

        var wall01Mtl = scene.getMaterialByName("scene_BJS.wall01.000");
        wall01Mtl.ambientColor = new BABYLON.Color3(0.87, 0.2, 0.57);

        /* tools */
        scene.debugLayer.show();
    }
);

[...]

Et hop ! on est bon.

voir tuto05.html

Alors vous me direz "Oui mais l'ambientColor on peut la régler sans s'emmerder directement dans Blender non ?", et vous aurez raison, c'était juste pour avoir un paramètre visuel pour le tuto.

La démarche est juste identique pour n'importe quel paramètre, ici en consultant l'API sur l'ambientColor j'ai pu voir que BJS me demandait de lui fournir une Color3, dont acte.

Imaginons que je veuille supprimer une lightmap sur un matériau d'un de mes objets lightmappé ?

scene.getMaterialByName("scene_BJS.wall01.000").lightmapTexture = null;

ici je n'ai carrement pas créé de variable contenant le matériau, c'est probablement une mauvaise pratique

Mes normalMaps ont été générées en norme DirectX et non OpenGL ?

for (var k = 0; k < scene.materials.length; k++) {
    scene.materials[k].invertNormalMapY = true;
}

là on boucle carrement parmis tous les matériaux de la scène

Avant de finir ce tuto, une micro-astuce pour vous que vous puissiez facilement simuler un semblant de reflection sans pour autant pêter les perf' : l'utilisation d'une spheremap. C'est une technique qui date de Mathusalem en 3D temps réel et qui donnera vite un aspect cheap, mais sur des surfaces courbes (comme la céramique de notre lampe) aura un effet convenable. Prenez juste un screenshot de votre scène BJS et appliquez un effect de sphérisation :

spheremap01

filtre de sphérisation sous Gimp

Puis dans Blender, assignez votre texture sur votre matériau et passez le Mapping en Spherical.

Finitions

Support du tactile

Pour s'assurer une bonne compatibilité des dispositifs tactiles, ils nous faut ajouter une librairie javascript nommée PEP.
Téléchargez la version minifiée ici, placez-là dans le dossier BJS/js/ puis appelez-là dans la balise <head>.
Il vous faudra aussi ajouter touch-action="none" dans la balise <canvas>


<!doctype html>

<html>

<head>

    [...]

    <script src="https://www.nothing-is-3d.com/js/babylon.js"></script>
    <script src="https://www.nothing-is-3d.com/js/pep.min.js"></script>

    [...]

</head>
<body>
    <canvas id="canvas" touch-action="none"></canvas>

Et voilà, vous avez le support du tactile.

Les post-processes

Si vous le souhaitez, vous pouvez ajouter des post-processes, je vous laisse lire la doc' officielle. Sur ma démo présentée en début de page, j'ai juste utilisé un glowLayer, en plaçant à la fin de ma fonction SceneLoader ce bout de code :

var glowLayer = new BABYLON.GlowLayer("glowLayer", scene)

Résultat

Je ne vous ais pas mâché tout le travail dans ce tuto, ni détaillé chaques opérations effectuées, mais vous devriez pouvoir arriver à ce résultat sans trop de difficultés:

voir tuto-final.html
Bien entendu, jettez un oeil sur les sources fournies en début de tutoriel.

Ce tuto m'a demandé plusieurs heures de travail, si vous souhaitez me payer une bière 🍻, c'est par ici ;)

Aller plus loin

Voici quelques pistes & astuces en vrac pour tendre vers la classe ultime :

  • ajouter du contraste sur les lightmaps avec une passe d'ambient occlusion
  • ajouter des post-processes (mais attention : une scène 3D temps réel doit toujours être belle même avec ceux-ci désactivés)
  • apprendre javascript : évidemment quand on parle de webGL, le plus on connait javascript, le mieux on va s'en sortir
  • apprendre python afin de pouvoir automatiser des taches chiantes sous Blender, exemples :
    • assignation auto du _cycles_default_ on link obj, voir même autoconvert du material courant (pour avoir le naming conversé)
    • relink automatique des matériaux cycles configurés, utilisation de Data ou Obj pour l'assignation mat'
    • possibilité d'automatiser des taches comme le baking (sans passer par un plugin externe)
    • améliorer des addOns qu'on utilise (et y pousser ses suggestions et modifications bien entendu)

Crédits

Logiciels

Les logiciels et addOns utilisés sont :

  • Blender 2.79b, avec ces addons:

    • exporteur BabylonJS 5.6.3
    • le workflow du texture baking de base de Blender est tout pourri, c'est pourquoi je me suis acheté l'addOn Baketool, que j'ai utilisé ici. Notez qu'il est possible que les addOns gratuits suivants peuvent vous être utiles (je ne les aient pas tous essayés, n'hésitez pas à m'en suggérer d'autres) : TexTools, BRM-BakeUI, Principled-Baker.
    • Materials Utils Specials (par défaut)
    • Layer Management (par défaut)
  • BabylonJS 3.3.0

  • Gimp 2.10. Je trouve Photoshop bien plus pratique, mais je n'ai pas de licence personnelle. Donc pourquoi ne pas profiter de l'écriture de ce tuto' pour tenter de mieux le maitriser. Krita vaut son test aussi.

  • EasyPhp Devserver 17.0 pour le server web local

  • Quelques éditeurs de code en vrac :

  • et pour écrire et préparer ce tuto, en vrac :

    • Typora, éditeur léger en markdown
    • ScreenToGif, capture d'écran et éditeur de gif
    • Greenshot, capture d'écran et édition rapide
    • XnView, visualiseur d'images et traitements rapides
    • OBS Studio, capture d'écran en vidéo
    • Moukey, affichage des actions claviers & souris (d'ailleurs, si vous avez mieux ?)

Ressources

Changelog

  • 2018-10-08:
    • ajout du changelog
    • ajout de la partie Finitions (tactile, post-processes)
    • mise à jour du zip des sources

 

11 commentaires

avatar
#1  - Cstfan a dit :

Merci Vincent pour ce premier tutoriel vraiment très instructif, je vais mettre maintenant en application pour progresser sous BabylonJS. Je débute, j'étais juste avant sous Blend4Web, qui a été abandonné à priori. J'attends avec impatience les suivants mais déjà un grand merci pour ce premier vraiment top.
Stéphan

› Répondre
avatar
#2  - Meteoritool a dit :

Merci pour ta générosité, j'ai appris plein de nouveaux trucs

› Répondre
avatar
#3  - V!nc3r a dit :

Merci pour vos commentaires, ça fait plaisir :)

› Répondre
avatar
#4  - Corentin a dit :

Hello, Yes ! Sympa comme tout ce tuto :)

› Répondre
avatar
#5  - V!nc3r a dit :

Cstfan me signale que dans la propriété de camera attachControl, il vaut mieux utiliser la valeur false que true pour éviter que le scrolling dans la scène 3D scrolle la page web aussi :
camera.attachControl(canvas, false);
Plus d'info :
http://doc.babylonjs.com/api/classes/babylon.arcrotatecamera#attachcontrol
https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault

› Répondre
avatar
#6  - Cstfan a dit :

Vincent,
Après avoir essayé avec le Baking par défaut de Blender, j'ai acheté BakeTool car il permet de générer des lightmaps complètes sur les objets possédants plusieurs matériaux (chose impossible avec le baking par défaut). De plus, une version pour Blender 2.8 est prévu (réponse donnée avec le développeur). Le système de Job est aussi bien pratique pour générer le Baking sur plusieurs objets en même temps, ça permet d'aller prendre un café avec les collègues, Blender bosse tout seul jusqu'au bout :-p
Voilà ce que j'arrive à faire de mieux :
http://3dmad.online.fr/WebGL/Steelcase_v0.2/index.html
Il me reste à corriger des imperfections que je n'arrive pas à comprendre sur le plateau du bureau (4 traits parasites, et un défaut, je pense de lighmaps, au niveau du passe-fils en dessous du bureau.
Peux-tu que tu as une idée ? Une piste qui pourrait m'aider :-p
Encore merci pour ce premier tutoriel.
J'attends avec impatience les suivants pour aller plus loin.
Cstfan

› Répondre
avatar
#7  - V!nc3r a dit :

Oui dès qu'il faut faire du baking en masse, on n'arrive plus à s'imaginer se passer de Baketool :)

Sur ta scène l'ambiance générale est sympa, voilà des petits conseils :
- ta lampe précalc' est positionnée à l'intérieur de l'abatjour ? Car je vois qu'elle ne projette pas correctement les ombres sur les murs. Si oui, ta scène est-elle à l'echelle ? (en mètres) Et ta lampe a-t-elle une size correcte ? ( = plus petite que le diametre de l'abatjour)
- à propos de tes imperfections de lightmap ( http://3dmad.online.fr/WebGL/Steelcase_v0.2/scenes/Steelcase/Lightmaps/Furnitures_LM.jpg ), je crois deviner que tu as utiliser le mode de déplié "lightmap" de Blender ? L'idée était bonne mais éclater les faces des objets en tout petit n'est pas une bonne idée, il vaut mieux conserver le plus possible d'island. Avec un smart UV ça devrait déjà être mieux, le top du top étant d'avoir configuré ses seams pour qu'un Unwrap fonctionne bien comme on le souhaite. Tente déjà avec un smart UV et laisse un peu de marge entre les island, ça devrait corriger pas mal d'artefacts.
- un dernier truc, je vois que certaines textures ne sont pas en taille de puissance de 2, c'est toujours préférable en temps réel de s'y conformer. Oublie pas du même coup d'optimiser leur taille (Book Edge4.jpg par exemple n'a certainement pas besoin d'être aussi grosse - et d'ailleurs, toujours éviter les espaces dans les noms d'objets/fichiers ;) - Abstract.jpg peut passer de 1024*1024px à 1024*512px, etc )
- on voit la compression jpg sur Pattern_03_Var_A_pr.jpg, c'est un peu dommage :)

› Répondre
avatar
#8  - Cstfan a dit :

Salut Vincent,
J'ai suivi tes conseils et appliqué tes remarques:
- Réduction de la Lamp point pour avoir un éclairage correct
- J'ai fait un smart UV avec marge, là plus de problème au niveau des textures :-p
- et pour finir j'ai repris la taille des textures, puisse de 2 et nommage sans espace, et diminuer compression texture papier peint.
Ça donne ça: http://3dmad.online.fr/WebGL/Steelcase_v0.4/index.html
J'y suis arrivé, merci ton aide.
J'attends maintenant la suite avec impatience :-)
Stéphan
@+

› Répondre
avatar
#9  - V!nc3r a dit :

Yes bien joué, ça rend bien mieux avec la lumière bloquée par l'abat-jour. Content que ce tuto t'ait été utile :)

› Répondre
avatar
#10  - BlackShadauw a dit :

Super tutoriel merci beaucoup, bien écrit, assez claire sans rentrer dans les détails, c'est top.
ça m'a permis, pour ma part, de mieux comprendre le workflow.

J'ai 1 question qui m'est venue en lisant. Dans ta scène tu n'as qu'un seul objet regroupant les fournitures, tu as précisé que c'est ainsi plus simple pour les LM. Mais imaginons que j'ai l'entier d'une pièce avec plein de fourniture (et donc de texture et de mesh différents), si tout est regroupé sous un seul objet ça devient problématique pour les éditer par la suite. Une alternative à cela ? Du coup, pourquoi ne pas avoir fait une seul LM en groupant mur et fourniture sur ton exemple, il y a une raison à cela?

Merci encore

› Répondre
avatar
#11  - V!nc3r a dit :

- en effet il serait beaucoup plus simple de ne pas attacher les objets ensemble mais de tout de même partager la même lightmaps entre certains. La première limitation est le fait de partager la planche UV sous Blender, c'est possible avec l'addon par défaut Texture Atlas mais très relou - la 2.8 autorise l'édition multi-objets, ça va faire du bien. Ensuite il faudrait aussi réaffiner la convention de nommage pour assigner par code la bonne lightmap aux bons meshes, c'est faisable mais le code aurait été plus compliqué.
- et je n'ai pas groupé les murs et les meubles pour avoir un exemple contenant plus d'un objet - et qui s'applique donc sur des scènes plus complexes aussi. Ici on aurait en effet très bien pu tout attacher, il faut noter qu'on aurait alors perdu en définition (puisqu'un pixel de la lightmap servirait alors à stocker une information de surface plus grande)

› Répondre

Écrire un commentaire

Quelle est le troisième caractère du mot znsup ?

Fil RSS des commentaires de cet article