mardi 23 juillet 2019

Unity - Instance dans le monde - partie 2

J'aimerais procéder en mentionnant que j'ai aisément corrigé mon problème d'orientation dans le script d'instanciation. Pour se faire, il m'a simplement fallu supprimer l'information de la collision précédente qui restait enregistrée dans la variable. J'ai intié le script en rendant la rotation locale :
       
             var _maisonRot = _maison.transform.localRotation.eulerAngles;

Et après:

            //changer la rotation du même objet
            var _maisonRot = _maison.transform.rotation;
            _maison.transform.rotation = Quaternion.Euler(-90.0f, Random.Range(-90.0f, 90.0f), 0.0f);
       
J'ai simplement rajouté :

            //repositionner la collision
            _collision = _maison.GetComponent<BoxCollider> ();

qui agit comme un reset.

La suite m'a permis de peaufiner la grosseur des collisions lorsqu'elles varient. J'utilise la propriété .OverlapBox qui, selon ma référence vient générer la collision à l'intérieur du BoxCollider .

OverlapBox agit sur la section encadrée rouge


Dans un premier temps, je crée une variable pour séparer les bâtiments de la surface, ce qui éliminera les analyse superflues dans le cas présent:

  [...]
          
void Start()
            [...]
                  _coucheDeMasqueBatiments = LayerMask.GetMask("Buildings");
            [...]

----

  Dans un second temps, je construis une variable récupérant les coordonnées pour le box collider. Il utilise l'original (Il peut être de n'importe quelle taille, il sera modifié selon la "Bounding Box" ou limites de l'objet visuel.)

            [...]
          
void FixedUpdate()
    {
       
        //création de la boite de collision (nécessite une COLLISION BOX)
        //Démarrer en confirmant les collisions

        var _verifCollision = Physics.OverlapBox ( (_collision.center + _maisonPos), (_collision.size)*1.06f, (_maison.transform.rotation),(_coucheDeMasqueBatiments)); 

 
            [...]


   Ensuite, il est temps d'appliquer ceci dans notre calcul, pour faire en sorte que les maisons ne se touchent pas.

Le résultat nous sort 80% des bâtiments qui évitent de se toucher
Il me reste à investiguer plus en profondeur pour les bâtiments qui s'interpénètrent.
---

Le deuxième élément modifié sur le script me permet de progresser sur l'étape 2 de mon plan : Sur une surface.

Pour l'instant, la surface sera plane; la première étape étant de rendre l'opération modulaire. Si j'étire les X de la surface, les bâtiments pourront se rendre jusqu'à la nouvelle limite. Même situation pour les Z et même situation lorsque la surface est compressée. Mon carré d'origine est de 10 x 10. Voici un résultat avec un rectangle de 5 x 20 :

Les objets en soi ont la même configuration que sur la surface 10 x 10


Pour se faire, j'ai ajouté ces 4 variables au script :

    private BoxCollider _surfaceBox;
    private Vector3 _surfaceMatrix;
    private float _surfaceX;
    private float _surfaceZ;

 
Au démarrage :

         _surfaceBox = _surface.GetComponent<BoxCollider> ();
        _surfaceMatrix = _surfaceBox.bounds.size;
        _surfaceX = _surfaceMatrix.x;
        _surfaceZ = _surfaceMatrix.z;



au début de la mise à jour (rappel) :

        _surfaceX = _surfaceBox.bounds.size.x;
        _surfaceZ = _surfaceBox.bounds.size.z;


et durant la mise à jour, je rajoute ce bout de script :


            //Si la OverlapBox possède un plus grand X que Z; réduit la surface de X (meme chose pour s'il s'agit du Z)
            if (_collision.size.x > _collision.size.z)
            {
                _surfaceX -= (_collision.size.x/2.0f);
                _surfaceZ -= (_collision.size.x/2.0f);
            }
            else
            {
                _surfaceX -= (_collision.size.z/2.0f);
                _surfaceZ -= (_collision.size.z/2.0f);
            }


Ce dernier bout de script empêche l'objet de dépasser de la surface (en prenant en considération que le centre de l'objet se trouve au centre), utilisant soit la largeur soit la longueur dépendamment de quelle mesure est la plus grande. Ainsi, on utilise un seul paramètre pour les deux axes : X et Z.

J'aimerais vous revenir très bientôt avec plus de détail sur l'exercice. D'ici là, je vous dis à bientôt,

David


lundi 25 février 2019

Unity - Instance et position dans le monde

Partie 1

Bonjour à tous,

aujourd'hui, j'inspecte l'instanciation dans Unity afin de reproduire un projet que j'ai conçu manuellement sur 3DSMax. Souhaitant reproduire dans Unity, il s'agira de produire une ville procédurale qui sera façonnée automatiquement. Ceci permettra ainsi de se concentrer sur des effets de lumière. Je souhaite, au moment de la conception, avoir un environnement qui fait du sensé.


Mon plan pour la première partie :











Pour se repérer au départ j'ai installé un script dans mon Objet de GameConfig.
Il est nommé Building Spawn:


-Maison : placer l'objet avec collision qui sera Instancié. Dans ce cas-ci j'ai mis l'objet ci-haut.
- Surface : Terrain sur lequel les objets se trouveront.
Pour le moment, j'ai placé un Plane sur le monde et je l'ai mis dans ce paramètre.
- Maison Pos, Nbre Batiments, Nbre Batiments Actuel : Pourraient être privés, mais je les ai mis publics pour que vous ayez une idée de ce qui est analysé. Ces chiffres changeront à chaque update.
- Nbre Batiments Min, Nbre BatimentsMax : Comme les noms indiquent, ce sont les variations du nombres d'instances de l'objet dans Maison qui se retrouveront dans la scène.
- Collision : Automatiquement, à l'ouverture du jeu, le Box Collider* de l'objet dans le paramètre Maison sera attaché.

*Pas besoin d'un Mesh Collider puisque ce sont des objets très cubiques et l'analyse que Unity fait peut devenir un peu trop compliquée sur des collisions à géométrie complexe.

 1- Générer 1 objet ...

J'ai rapidement mis en place des bâtiments à la forme allongée qui doit avoir une boîte de collision (Box Collision) pour la suite.



C'est une étape qui se termine avec deux lignes de script :
    // Paramètre avec l'objet - Variable
    public GameObject _maison;

Pour l'instant, on peut mettre cette ligne dans la fonction On Start :
    //L'instanciation - FixedUpdate
    Instantiate(_maison);


2 et 3- ...sur la surface ET au hasard ...

Le produire SUR la surface s'avérera un étape supplémentaire qui n'a pas encore été abordée dans ce projet. Bien s'assurerque le centre de l'objet s'appuie au sol dans le logiciel de modélisation (au bas du BoxCollider). Pour tout le reste, il me faudra un article complet sur le sujet.

Autrement; nous passons à l'étape 3 : je désire générer l'objet au hasard, ailleurs qu'au centre du monde.

Vient l'utilité de Maison Pos :
         // créer les coordonnées - Variable
         public Vector3 _maisonPos;
         // dire que ce sont les mêmes coordonnées que l'objet dans Maison - On Start             
         _maison.transform.position = _maisonPos;


Pour l'instant, on peut mettre cette ligne dans la fonction On Start :
         // changer la position de l'objet maitre - Update
          _maisonPos.x = Random.Range(-5.0f, 5.0f);
          _maisonPos.z = Random.Range(-5.0f, 5.0f);
          _maison.transform.position = _maisonPos;

4-  ... et le multiplier...

Cette fois, nous auront toutes nos Variables - je les ai toutes mises publiques mais elle peuvent être privées (private) :

    public GameObject _maison;
    public GameObject _surface;
    public Vector3 _maisonPos;
    public int _nbreBatiments;
    public int _nbreBatimentsActuel = 0;
    public int _nbreBatimentsMin = 1;
    public int _nbreBatimentsMax = 10;
    public BoxCollider _collision;


   void Start()
    {
        //ajustés
        _nbreBatiments = Random.Range(_nbreBatimentsMin, _nbreBatimentsMax);
        _maison.transform.position = _maisonPos;
        _collision = _maison.GetComponent<BoxCollider> ();
       
        //créés
        var _maisonRot = _maison.transform.localRotation.eulerAngles;
       
    }

    void Update()
    {
       
        // Layer 10 et layer 9 ne se voient pas (collisions)
        _maison.layer = 10;
        _surface.layer = 10;
       
    }
    void FixedUpdate()
    {
        //Pour suivre le nombre de bâtiments (PEUT ÊTRE REMPLACÉ PAR UNE FOR LOOP)
        if ( _nbreBatimentsActuel < _nbreBatiments )
        {

                //isole et isole les collisions - layer 10 ne voit pas layer 9 - avant l'instanciation
                _maison.layer = 9;

                //L'instanciation
                Instantiate(_maison);
                

                //remet la layer - layer 9 voit la layer 9 - après l'instanciation
                _maison.layer = 10;
               
                //Ajout d'un token - Nombre de bâtiment (peut se faire en for loop)
                _nbreBatimentsActuel += 1;
               
               //changer la position de l'objet maitre
            _maisonPos.x = Random.Range(-5.0f, 5.0f);
            _maisonPos.z = Random.Range(-5.0f, 5.0f);
            _maison.transform.position = _maisonPos;
               
            //changer la rotation du même objet
            var _maisonRot = _maison.transform.rotation;
            _maison.transform.rotation = Quaternion.Euler(-90.0f, Random.Range(-90.0f, 90.0f), 0.0f);
           
        }
        else
        {
            print("Il y a " + _nbreBatiments + " bâtiments dont " + _nbreBatimentsActuel + " sont    apparus.");
        }
           
        //Éviter de laisser la maison à (0, 0, 0)   
        if (_maison.transform.position.x == 0.0f  & _maison.transform.position.z == 0.0f )
        {
            _maison.transform.position = _maisonPos;
        }
    }


J'ai déjà mis la variable de "_collision" ici pour préparer l'étape suivante. Ce qui est le plus important ici c'est l'ordre dans lequel nous procédons.

Nous verrons ici des problème de "groupes de maison interpénétrant" l'une dans l'autre.



Également, nous aurons un problème de rotation. En faisant un troubleshoot, j'ai remarqué que le système n'avait gardé que les rotation entre 0.3 et 0.7 dans la traduction en Quaternion, alors je vais rechercher pour une solution plus appropriée. Certains confirment la complexité des Quaternions dans leur réponse comme l'une des pistes ici envoyée par "rutter" :
https://answers.unity.com/questions/790877/random-rotation-in-z-axis.html

5- ... sans qu'ils ne se touchent.

Ici, nous répondons au problème des groupes de maisons avec un système utilisant les physiques "Physics.OverlapBox". Étant donné que la physique procède par moment de façon erratique avec la fonction d'Update, je me vois suggéré d'utiliser la fonction de FixedUpdate, environnement contrôlé pour la physique.

Bref, voici ce dont nous avons besoin de nouveau (et ou le placer entre les commentaires) :

        // [...]créés
        var _enableMeshCollider = _surface.GetComponent<MeshCollider>().enabled ;

        //var _maisonRot = _maison.transform.localRotation.eulerAngles; [...]


 
         //[...]if ( _nbreBatimentsActuel < _nbreBatiments )
        //{


            //mise à jour de la position de la COLLISION BOX
            _collision = _maison.GetComponent<BoxCollider> ();
           
            //création de la boite de collision (nécessite une COLLISION BOX)
            //Démarrer en confirmant les collisions

            var _verifCollision = Physics.OverlapBox ( (_collision.center + _maisonPos), (_collision.size), (_maison.transform.rotation));
           
            //Continuer en vérifiant les contacts des collisions
            if (_verifCollision.Length == 1 || _verifCollision.Length == 0)
            {


                print ("ce qui est écrit " + _verifCollision.Length + "- ne touche rien");

               //troubleshooting coordonnées de la hitbox
                print ("non-hit ET le centre X : " + (_collision.center.x + _maisonPos.x) + " ET la taille X : " + _collision.size.x + " ET le centre Z : " + (_collision.center.z + _maisonPos.z) + " ET la taille Z : " + _collision.size.z + " ET la rotation : " + _maison.transform.rotation);


                //isole et ferme les collisions - layer 10 par rapport à la layer 9 - avant l'instanciation
                //_maison.layer = 9;
[...]




               // [...]Ajout d'un token
               //_nbreBatimentsActuel += 1;
               
           
}
            else
            {
               
                print ("ce qui est écrit " + _verifCollision.Length );
                
//troubleshooting coordonnées de la hitbox
                print("hit build"
ET le centre X : " + (_collision.center.x + _maisonPos.x) + " ET la taille X : " + _collision.size.x + " ET le centre Z : " + (_collision.center.z + _maisonPos.z) + " ET la taille Z : " + _collision.size.z + " ET la rotation : " + _maison.transform.rotation);

               
            }


            //changer la position de l'objet maître
            _maisonPos.x = Random.Range(-5.0f, 5.0f);
[...]





             //[...]_maison.transform.rotation = Quaternion.Euler(-90.0f, Random.Range(-90.0f, 90.0f), 0.0f);
            
            //repositionner la collision
          
 _collision = _maison.GetComponent<BoxCollider> ();

        //}
       //else
[...]


Ce sont des étapes cruciales ou l'objet "Maison", qui sera instancié, parcourt l'environnement à la recherche d'un endroit ou rien n'est placé (collision).  









Les maisons se tiennent plus à distance mais semblent garder relativement le même angle

---

Retour

Utilisation de la loop avec "for" : Je crois que je dois transférer à la "for loop" plutôt que de continuer directement sur l'Update. Cela va me permettre d'isoler mes problèmes suivants.Imprécision de la vérification des Collisions : Notez que j'ai mis " if (_verifCollision.Length == 1 || _verifCollision.Length == 0)" comme condition pour créer une instance, ce qui signifie qu'il peut y avoir 1 collision. _verifCollision est la OverlapBox (Zone qui détecte des superpositions de Collisions). Si je mets uniquement "_verifCollision.Length == 0", ce qui signifie que l'Array des Collisions serait vide, je me retrouve avec une boucle infinie ou la recherche d'un endroit sans collision ne se termine jamais. Je suppose que le layer mask ne s'active pas au bon moment ou pas du tout. Je crois qu'il me faut mentionner à la OverlapBox quelle layer elle doit analyser. Elle semble mettre l'objet maître dans les collisions.

Rotations d'objet et d'OverlapBox différentes : À quelques endroits, les objets s'interpénètrent . Selon mes recherches, il s'agirait encore d'un problème lié aux rotations Quaternion vs Euler. Il est certain que je vais regarder cela de près pour la suite.

Partie 2 à venir...

Je vous souhaite la meilleure journée,
À bientôt. 

jeudi 31 janvier 2019

Unreal - Conception d'un noise procédural pour les normales

Bonjours à tous,

je me lance dans cette année 2019 avec comme initiative de développer mes talents de Unreal 4.

J'ai regardé un peu pour le sujet à aborder et j'ai constaté que si je veux me donner un défi, je peux me créer un modèle sans texture diffuse (ton de gris) et l'éclairer. Avant de commencer, je constate que le shader par défaut ne me convient pas et qu'il mérite des petits rajouts de détails. Habituellement, c'est dans la diffuse que nous rajoutons un noise de façon procédurale. Cette fois-ci j'ai préféré me tenter à l'expérience d'une normal map.
 



































































Noise normalisé dans un environnement non-linéaire

Avec une normalisation du noise, je reçoit ce type d'information:

Évidement le moindrement que vous regardez une map de normal,  vous constatez que celle-ci est faussée par des coordonnées du monde. Je ne peux que la restreindre à des informations qui imitent les tangeantes.

Comment faire? Voici :
- Je sépare mes trois canaux (RVB)
- J'isole mon Z (B) pour qu'il soit pure : n'oubliez pas qu'une normale, de base, possède un canal bleu 100% duquel nous soustrayons. Dans la tangeante, j'ai constaté qu'une valeur décente où cesser est lorsque le bleu est à 66% (deux tiers arrondis).
- Les autres couleurs (RV) seront mises à 50% : le but ici étant de produire un canevas "blanc pour la normale supplémentaire, bref:
- RÉSUMÉ - R:50% V: 50% B:100%

- On additionne le tout ensemble
La normale de base dans un environnement non-linéaire
- On se crée un LERP afin de jouer correctement avec l'intensité du "noise" (canal "Alpha" de la node).
- Je me permets de mettre le multiplicateur de chaque canal (RG) ainsi que l'intensité dans le LERP (alpha) en tant que paramètres pour la suite.

- L'alpha du LERP est primordial car c'est ce qui empêche la normale de présenter des information que j’appellerais World-based (en opposition à Tangent-based). Je le restreint entre 66% et 100% car c'est dans cette région que se situe l'écart qui nous est nécessaire (pour la tangente).






https://upload.wikimedia.org/wikipedia/commons/c/c4/Ripple_disc_norm.jpg
C'est grâce à ce disque ci-contre, trouvé sur le web, que j'ai pu confirmer mes restrictions.












Noise normalisé (tangente) dans un environnement non-linéaire
- Voici ce que le mélange donne à 66% d'alpha :


















- Voici le résultat sur une sphère 
- Une fois paramétré dans une instance, je l'intègre à un mur. Voici le résultat :


Avant
Après


Une image du Graph du "Normnoise"


Dôme avec le "normnoise"

 La prochaine étape sera de l'intégrer avec une autre normale sur l'objet. Cela signifie qu'il me faudra utiliser des canaux UV séparés.

D'ici là, je vous souhaite une bonne journée,

Vôtre,
David



Suite au Game Jam 2019


Salut à tous,

j'ai retardé mes posts puisque je voulais discuter d'un évènement auquel j'ai participé : le Global Game Jam 2019.

C'est mon 4e Game Jam et je suis toujours bien excité à l'idée de créer un jeu de A à Z en une fin de semaine.

Cette fois-ci comme auparavant, je me suis greffé à une équipe comportant des amis (à chaque fois c'est une surprise d'en rencontrer). L'équipe était constituée uniquement de programmeur; mais dans un game jam, on prend rapidement un autre poste.

C'est la première fois que je me retrouve à être l'unique artiste (dans l'officiel). Cela m'a créé de réels défis que même en "scoping" je n'aurais pu concevoir. Design de persos (2D et 3D), création de shaders, "rigging" et "skinning", modélisation, texture et animation. C'était un peu trop malgré qu'on me diminuait constamment la charge, soit en prenant une partie du boulot (merci à l'équipe) soit en coupant sur certaines fonctionnalités qui ne seraient plus visuelles.

Le Jeu

Il s'intitule Haunted(Not) House. Il est fait sur le thème de "Ce que signifie la maison pour vous" que l'équipe a reviré en : "Ce que vous signifiez pour la maison".

Vous jouez maintenant la maison qui se trouve a être "hantée" par vous même. Vous tentez de protéger la personne qui y habite. C'est un vieil homme qui se nomme Herméningilde.

De mon côté, il me fallait le rendre sympatique. Certain membre de l'équipe voulaient une sonorité "Wacky". J'ai alors pensé aux Munny; en me rétractant rapidement puisque nous voulions garder l'aspect amusant uniquement pour les animation. J'ai donc gardé l'idée des grandes têtes de Munny et j'y ai juxtaposé des corps plus élancés, un peu comme dans " Last Day of June".
Sketch
3D




















Inspiration principale : Last Day of June














Shader//Ergonomie

J'ai également tenté de produire un shader avec lequel nous pourrions montrer la progression de la crainte. Je voulais une donnée paramétrable sur les Y qui monte et descend créant une jauge et, visuellement, montrant une crainte grimpante.

Pour ainsi faire j'ai utilisé le Fresnel en modifiant le canal View vers le Monde. Faisant ainsi, il est possible de manipuler les dégradés du Fresnel pour qu'ils puisse partir du bas d'un axe et rejoindre le haut de celui-ci. Les nodes "add" sont ainsi rajoutées afin de créer les déplacements. La contrainte devient celle-ci: les réplacement se font tels des rotations puisque nous utilisons 3 canaux qui sont ajustés sur un objet dans les 3 dimensions!
Section 1 du canal d'émission : De l'oeil à l'objet

Quelques considération ont dû être prises lors de l'application du "Shader". D'un premier temps, l'objet est importé de Max vers Unity, ce qui crée une rotation de l'axe de Y sur les X de 90 degrés, ce qui déplace l'axe des Y et des Z (problème bien connu et dû à la fonction primitive de 3DSmax de travail en l'isométrique).



Comme on parle de normales de l'objet, il faut modifier le canal rouge (des X) car l'axe des Y tombe  tombe sur cet axe X.



Section 2 du canal d'émission : De la normale à l'objet


Afin de terminer la "shader", je paramètre les évènements afin de pouvoir appeler les propriétés sur les personnages. Plus il est stressé plus la démarquation va être élevée.





Animations


Évidement, je ne peux partir sans mentionner l'intégration d'animations ainsi que le rigging et skinning durant cette fin de semaine.

Le rigging est on ne plus simple : le bon vieux biped de 3DSMax, merci bonsoir!

Le skinning m'a simplement demandé de faire quelques loops additionnel sur un mesh très simple, ce qui m'a pris peu de temps.

Pour l'animation, il y avait deux personnages a faire avec 3 animations de base pour chacun (6 animations). Notre vieux monsieur tient une posture fière. Son opposant, Path le malfrat, se tient torse bombé tel que quelqu'un affrontant des peur bravement. Évidement, chacun a son animation de course suite à la crainte! C'est à ce moment là que je me suis amusé à leur faire des mouvement plus clownesque.

Herméningilde appeuré


En bref...


C'est énormément de poids sur les épaules être le seul artiste visuel d'une équipe lorsque tu es entouré de programmeur. J'ai donc dû laisser aller le level art pour que l'on puisse réellement se concentrer sur les 3 C : Characters, Camera, Controls.

J'ai tout de même mis ma touche lumineuse APRÈS que la fin de semaine soit terminée :




Sur ce, je vous laisse la suite pour un prochain post de blogue,

Vôtre,
David

vendredi 21 décembre 2018

Commencer avec "Shader Graph" - Premiers tests


Rebienvenu à mon blog.


Voilà déjà 3 ans que je suis parti de mon blog. Ce fut un long voyage dans le domaine de l'emploi. Plusieurs travaux dans le monde de la 3D et quelques boulots concernant la bicyclette. Ce sont de belles expériences qui m'amènent à revisiter d'anciens projets, dont certain que je n'ai jamais mentionné ici.

Je le fais renaître dès maintenant!

Shader Graph

Maintenant que j'ai utilisé une multitude de logiciels pour l'attrait du public (Nuke, Maya, encore plus de Unity, Unreal 4 est apparu depuis le temps), me voilà revenu à l'aspect shader qui m’attirait tant en retour.

Ayant utilisé Strumpy Shaders ainsi que la manipulation de légères modifications via HLSL à l'époque, me voici de retour avec le dernier enfant des shaders de Unity, le ShaderGraph.

Évidement, pour bien démarrer en beauté, il me fallait tenter de reproduire le fameux shader créé dans l'une de mes premières publications : Le shader artistique-microscopique de Korrasso.


Reproduire l'effet de mon ancien shader avec le ShaderGraph de Unity 2018
plutôt que Strumpy shader
Souvenez vous de mon intention de départ : créer ma propre interprétation du microscopique -
en m'inspirant des extrêmophiles et la coloration artistique des Sea Slugs.

Limace des mers - https://www.dailymail.co.uk/

Two heads are better than one! Double-headed psychedelic sea slug is discovered off the coast of Borneo


Tardigrade "extrêmophile" - http://www.humanite-biodiversite.fr/
Publié dans


le 23.01.16 

 Pour se faire j'ai recommencé à zéro sur un nouveau projet. Après avoir recréé ce qui me manquait (textures et shaders), retrouvé mes anciens objets (Korrasso) et installé le nécessaire (Unity 2018, shaderGraph et Lightweight), me voilà parti sur la création d'un premier shader simple.

Le graph est plutôt simple et modifiable
Contrairement à la dernière itération de Strumpy Shaders, c'est bien plus épuré avec moins de commandes modifiées (par exemple au niveau du jeu de la transparence via le Fresnel. Il me reste à vérifier pour l'orientation de l'effet artistique qui doit passer au centre de la zone transparente.

Je tenterai de jouer avec les niveaux de gris des deux Fresnel pour créer un nouveau canal Alpha qui comprendra l'opacité dans le "sample texture 2D" en noir et blanc. Tout ceci sera une question de mesures.

Pour l'instant, le shader donne un bel effet de Lava Lamp!

Je vous écris bientôt,
Joyeux Noël!

David

lundi 22 décembre 2014

PBS - du néant jusqu'à l'outil complété

Voici un vidéo qui présente de A à Z mon processus afin d'amener mes outils jusqu'à leur présentation - avec l'application des 2 mêmes textures d'un objet à un autre.


Merci et bon visionnement,
je vous reviens plus tard.

vendredi 12 décembre 2014

Optimisation des PBS // Intro


Bonjour à tous, j'entre immédiatement dans les détails de mes récentes recherches :

Que signifie PBS, à la base :
Physically-based Shading



Avec l'arrivée de Direct X11 et des consoles de notre génération, la toute dernière barrière à franchir afin d'accéder au système PBS est ouverte. Cette étape était celle d'intégrer des réflexions temps réel dans tous les matériaux, et ce, sans affecter les performances des jeux. Il faut avoir une bonne compréhension que tous les matériaux de notre environnement réflète la lumière, à la différence de ce que nous voyons dans les anciens systèmes de rendu temps réel où la réflexion n'est qu'une reproduction d'image.(1) De nos jours, nous pouvons sans crainte opérer des calculs et algorythmes se basant sur des vecteurs s'orientant de la source de lumière à l'objet puis de l'objet à la caméra.



Voici le set-up optimisé que je propose pour un projet : 2 TEXTURES

Texture no.: 1 - 128x128 au plus
Le dégradé ou les variances lumière - objet - objet - oeil

Gloss - UV channel 1 - 
Color channel choix de R, G, B ou A

Spec - UV channel 2 - 
Le color channel doit rester en RGB (car c'est un système qui lit la couleur - s'il y a sursaturation tel que dans les modes sRGB, il faut rajouter un paramètre de désaturation par la suite-)

Maintenant, pour donner un coup de main aux visuels de ce monde, voici la mise en place de base de la plage colorimétrique. Le carré dans le coin en bas à droit représente le canal UV 1 de mon objet.


Les canaux :

RGBA (en sRGB)                              Alpha                                             Rouge
                    

                         Vert                                                    Bleu

Texture no.: 2 - résolution au choix
Détails uniques ou propriété distinctive dans la colorimétrie d'un objet

Albedo - UV channel 3 - 
Le Color channel doit rester en RGB (il agit comme l'ancienne diffuse - garde plus pâle qu'une diffuse conventionnelle - certains angles d'incidence obtus vont porter l'objet à réagir de façon très sombre, et ce serait de même dans la nature également)

Occlusion Ambiante - UV channel 4 - 
Color channel A (alpha dans la texture de base)

---

Je mentionne jusqu'ici les canaux UV : Pour aller les chercher dans 3DSMax, il s'agit d'un paramètre à changer dans le "Unwrap UV Modifier"



J'ai déplacé mes UVs pour le bien de l'exercice. Voici mon Canal UV 2 pour le "Plane" - Bleu marin


















Pour Marmoset, le premier canal (ce qu'ils nomment "UV set") est le 0 - bref : UV set 0 = Couleur Verdâtre ; UV set 1 = Couleur Bleuetée

Le meilleur exemple de jouer avec les Canaux dans Marmoset est l'Occlusion Ambiante;
Si je prend le Canal Bleu (jusqu'à présent, il est sombre pour le UV set 0 et pâle pour le UV set 1) l'occlusion ambiante, expliquée plus en profondeur ci-bas, paraîtra mieux sur le UV set 1







---

GLOSS - Canal UV 1

Voici une mise en scène de base pour présenter les réactions de la "Plane"

Le Gloss fonctionne à la façon réelle. Il considère la dispersion des rayons de lumière lors de leur impact sur l'objet (2). Il vient également affecter la qualité du grain dans la réflexion.

Ici,  j'ai choisi le Color Channel Vert qui est le plus pâle sur notre UV set 0. 
Plus pâle = plus concentré
Ici,  j'ai choisi le Color Channel Rouge qui est le plus sombre sur notre UV set 0.
Plus sombre = plus dispersé


SPECULAIRE - RÉFLECTIVITÉ - Canal UV 2 (dans Marmoset, il existe que 2 canaux; dans les engins de jeux, il y en a une panoplie)

La map spéculaire vient chercher les tonalités additives du gloss des sources de lumière et des bounces lumineuse (analysé de base en niveaux de gris) pour venir teinter les zone de la couleur dans la map (ici le "plane" se trouve dans la zone bleu-vert). Plus on se trouve dans une zone sombre, plus l'objet va chercher la teinte opposée à la couleur originale (on voit ici le magenta ressortir sur l'objet).


Cette map est avant tout utile pour coloriser l'objet de façon réaliste; elle peut être manié pour faire réagir l'albedo et prendre sa teinte opposée si l'intensité lumineuse réfléchie est sombre.

Ici, l’albédo est vert-bleuté (voir le canal RGBA) et la tonalité dans la map spéculaire fait ressortir la réflexion du décor dans ses régions pâle et vient chercher la teinte dans l’albédo lorsque la map est foncée. Ceci est due à la perte de réflection qui nous ramène une couleur plus près de celle qui est caractéristique à l'objet (albédo).


ALBEDO - Canal UV 3 (dans Marmoset, il existe que 2 canaux; dans les engins de jeux, il y en a une panoplie)

Couleur caractéristique de l'objet. Si la lumière rebondit rapidement sur un objet, c'est qu'elle ne capture pas la couleur de l’albédo et renvoie mieux la réflexion rebondie.

Ici, l'albedo est rouge et paraît aisément sur une surface où la map spéculaire est plus faible


OCCLUSION AMBIANTE - AO - Canal UV 4 (Canal UV 1 dans Marmoset)






Map qui vient simplement émettre une négation dans la tonalité ET dans la teinte. Ici j'ai mis l'intensité de la AO à 4, ce qui tourne la couleur orangée à vert (à l'opposée dans le cercle chromatique), 3 fois plus contrasté que l’albédo d'origine ( 100% - 400% = -300% ).

Affecte uniquement l'albedo.

---

Je résume en vidéo mes explications :

 

Pour la suite je pars à la recherche d'un logiciel qui me permet les Canaux UV multiples pour la malléabilité de la propriété des différents matériaux sur une même texture.

Merci à tous et je vous reviens,
David


---

(1) Andrew Maximov, Physically Based Rendering for Artists, https://www.youtube.com/watch?v=LNwMJeWFr0U
(2) Gloss Introduction, http://www.gloss-meters.com/GlossIntro.html#1