Forums Développement Multimédia

Aller au contenu

- - - - -

Minko Google Globe // Explications

scene.mesh.PointCloudMesh CODE Actionscript

4 réponses à ce sujet

#1 Dermiste

    Ceinture Marron

  • Members
  • PipPipPipPipPipPip
  • 176 messages

Posté 12 March 2012 - 11:49 AM

Hello!

J'ai téléchargé les sources de la démo visible à cette adresse: http://aerys.in/portfolio/google-globe pour jeter un oeil au code et essayer de comprendre.

J'ai voulu essayer de créer de étoiles à la places des cubes dans la classe PointsCloudMesh mais je me suis heurté à plusieurs incompréhensions:
-
private static const INDICES            : Vector.<uint>         = Vector.<uint>([
                        5, 7, 6, 5, 4, 7, 0, 4, 5, 0, 5, 1, 1, 5, 2, 5, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0
                ])
De quoi s'agit-t-il exactement? J'ai pas réussi a lier le nombre de valeurs (30) au reste
-
private static const VERTICES           : Vector.<Number>        = Vector.<Number>([
                        .5, .5, .5, -.5, .5, .5, -.5, .5, -.5, .5, .5, -.5,
                        .5, -.5, .5, -.5, -.5, .5, -.5, -.5, -.5, .5, -.5, -.5
                ]);
Si je comprend bien, il s'agit là d'une liste de valeurs qui seront découpées "par 3" ensuite pour trouver les points. Vrai? Si je veux créer une étoile en volume par example, je pense que je devrais avoir quelque chose comme 3 x 20 points = 60 valeurs, pour une étoile à 5 branches ? (une base comprenant 10 points)
- Y a-t-il un ordre pour poser les vertex ? Pour faire ca propre, dois-je commencer par la base de l'étoile, puis ensuite faire la partie haute?
- indexStream.push(INDICES, _numPoints * 8); je suppose que 8 ici = VERTICES.length/3 ? Vrai ?


D'avance merci pour vos réponses. J'espère que je suis assez clair ...

#2 Jean-Marc Le Roux

    Ceinture Noire

  • Minko
  • PipPipPipPipPipPipPip
  • 210 messages

Posté 15 March 2012 - 23:01 PM

Salut,

Citation

De quoi s'agit-t-il exactement?

Le tableau VERTICES : Vector.<Number> contient les données brutes de l'ensemble des points qui constituent la géométrie. Ici, chaque point de la géométrie n'est défini que par sa position dans l'espace.

Le tableau INDICES : Vector.<uint> contient les données brutes des indices des vertex à utiliser pour dessiner des triangles à partir de la géométrie donnée dans VERTICES. Chaque triplet d'indice dans INDICES décrit donc quels sont les 3 vertices qui constituent le triangle à dessiner lors du rendu.

On sépare les indices pour ne définir chaque point de la géométrie qu'une seule et unique fois (si possible) et le réutiliser plusieurs fois pour plusieurs triangles différents. Si tu considères un cube par exemple, il a 6 faces carrées donc 12 triangles donc à priori 36 vertices. Cependant, un cube à 8 coins. On peut donc ne définir que 8 vertices, mais les utiliser plusieurs fois en insérant plusieurs fois leurs indices respectifs dans INDICES.

Il s'agit là d'une manière standard de définir une géométrie 3D. Tu pourras trouver beaucoup d'infos sur le web si tu cherches "vertex buffer" ou "index buffer".

Dans Minko, ces buffers (ici des vector) ne sont pas utilisés tels quels:
- ce sont des types trop primitifs pour être facilement manipulés
- ils ne sont pas "observable" : il est impossible de savoir quand un Vector est modifié, donc on ne peut pas uploader les données modifiées sur le GPU lorsque c'est nécessaire

Donc ces données "brutes" sous formes de Vector sont utiliséer pour créer des "stream", respectivement des "vertex stream" ou "index stream". Ces streams permettent alors un accès "sécurisé" aux données brutes, gèrent leur allocation, leur upload, leur mise à jour et leur libération sur le GPU.

Grâce aux itérateurs, tu peux avoir un accès très haut niveau aux streams. Bien plus haut niveau qu'avec un Vector, et ça marche avec tous les streams, quelque soit la complexité de ce qu'ils stockent. Tu peux trouver plus d'explications ici :

VertexStream and IndexStream objects" sur le Hub

Créer sa géométrie à la main - si elle est suffisamment simple ou procédurale - est un très bon entrainement (même si tu commences un peu bas dans l'API je trouve).

Une autre solution - plus haut niveau et plus abordable pour débuter - consiste à charger un objet 3D depuis un fichier (3DS ou Collada par exemple).

Dans la démo du Globe, le défi est de générer procéduralement la géométrie qui représente les points géolocalisés. En effet, on est obligé de créer des gros mesh - appelé ici PointsCloudMesh - qui vont contenir beaucoup (beaucoup) de barres. Sinon, afficher 40 000 barres serait beaucoup trop couteux (même avec Minko 2). C'est ce que fait la classe que tu cites.

Si tu veux remplacer les barres par des étoiles, voilà ce que tu peux faire :
- avoir un vertex/index stream global qui va contenir toutes tes étoiles
- charger un modèle 3D d'étoile une fois et récupérer son vertex/index stream
- pour chaque "point" ajouté, tu dois transformer le vertex stream de ton étoile avec la transformation 3D, l'ajouter au vertex stream global et tu dois également ajouter l'index stream à l'index stream global)

Tu auras alors deux streams qui définissent la géométrie d'un gros mesh contenant plein d'étoiles placées aux bons endroits.

Citation

- indexStream.push(INDICES, _numPoints * 8); je suppose que 8 ici = VERTICES.length/3 ? Vrai ?

Non. Le deuxième argument est ici un offset qui sera ajouté à chaque indices avant d'être inséré dans le stream. En effet, si on ajoute les mêmes indices encore et encore, on va se contenter de dessiner les triangles utilisant les premiers vertices et donc dessiner le premier cube encore et encore... En offsetant de _numPoints * 8, on insère dans l'index stream une série de triangles qui utilisera les vertices que l'on vient de mettre dans le vertex stream.

Ca peut paraitre compliqué, mais encore une fois tu t'intéresse à une problématique relativement bas niveau : la génération procédurale de géométrie.

N'hésite pas si tu as d'autres question.

A bientôt,

Modifié par Jean-Marc Le Roux, 19 March 2012 - 17:31 PM.


#3 Dermiste

    Ceinture Marron

  • Members
  • PipPipPipPipPipPip
  • 176 messages

Posté 18 March 2012 - 22:58 PM

Jean-Marc tu gères! T'es la seule personne que je connaisse qui soit capable de fournir une bible de réponse pour chaque soucis, ça fait grand plaisir :)

Je vais essayer de mettre en application tout ça !!

Encore merci pour cette réponse longue et complète :)

#4 Dermiste

    Ceinture Marron

  • Members
  • PipPipPipPipPipPip
  • 176 messages

Posté 19 March 2012 - 22:15 PM

D'ailleur, je me demandais, est-ce compliqué d'ajouter un éclairages à ce CloudMesh ? J'entend un éclairage qui se projete un peu sur le Globe?

J'ai regardé le code de Minko Gravity (les cubes de couleur avec le son), et j'ai l'impression que je peux juste ajouter ce genre d'effet Mesh Par Mesh, c'est bien le cas ? Je sais pas si je m'embarque potentiellement dans une lourde tache, ou pas.

[Edit] Et j'aurais besoin des normales pour chaque barre aussi non?

#5 Jean-Marc Le Roux

    Ceinture Noire

  • Minko
  • PipPipPipPipPipPipPip
  • 210 messages

Posté 20 March 2012 - 19:01 PM

TL;DR

Utilise le LightingEffect sur le globe/les barres.
Sinon attends Minko 2 pour appliquer les ombres sur le globe.

Réponse longue :

Voir le messageDermiste, le 19 March 2012 - 22:15 PM, dit :

D'ailleur, je me demandais, est-ce compliqué d'ajouter un éclairages à ce CloudMesh ? J'entend un éclairage qui se projete un peu sur le Globe?

Alors c'est là que ça devient "rigolo". Ce que tu veux faire ce sont des ombres portées.
La technique classique c'est le shadow mapping. C'est une techique en 2 passes:
- une pré-passe qui va rendre dans une texture la profondeur de chaque pixel vu depuis la camera
- une passe qui va render la scène "normalement", mais va lire la texture de profondeur pour savoir si le pixel est dans l'ombre ou pas

Je passe les détails sordides...

Il faut que tous les objets qui "émettent" une ombre effectuent la pré-passe. Pire, ils doivent l'effectuer avant tous les objets qui vont recevoir cette ombre. Pire encore, cette pré-passe doit être effectuée d'un seul coup, car Stage3D n'autorise pas à switcher de cible de rendu en cours de route sans en effacer le contenu.

Tous les objets qui vont recevoir l'ombre auront - eux - besoin de lire dans cette texture rendue en pré-passe par les objets qui reçoivent l'ombre.

Bref. Les problématiques :
1 - comment gérer plusieurs passes
2 - comment gérer l'ordre de ces passes
3 - comment garantir l'ordre de ces passes malgré le fait que la construction de ma scène n'a aucune contrainte (en gros, si on doit mettre "en premier" dans la scène les objets qui projettent des ombres et les autres après, c'est vite infernal, et on ne peut pas gérer les objets qui émettent ET reçoivent des ombres...)
4 - comment procéder lorsque je veux des ombres en plus d'un shader spécial, comme c'est le cas ici sur le globe

1 - Gérer plusieurs passes

Minko gère plusieurs passes. Dans nos démos on utilise souvent le SinglePassRenderingEffect, mais tu peux éteindre IRenderingEffect pour créer des effets avec plusieurs passes.

2/3 - (Garantir) l'ordre des passes

Chaque passe à une "priorité". C'est rudimentaire, mais ça marche. La priorité est un Number : plus la priorité est haute, plus la passe de rendue sera effectuée en premier. Avant de rendre, on trie tout, et on exécute les passes dans leur ordre de priorité, de la plus grande à la plus petite.

Du coup, il s'uffit de mettre une priorité plus haute à une passe qui est requise pour une autre.

4 - Rajouter les ombres par dessus un shader existant

Là, on tombe dans le truc "gore". En gros, on veut combiner le shader qui existe déjà et rajouter par dessus l'ombre projetée. En gros on veut une fonction "doShadowMapping()" magique qu'on peut réutiliser partout.

Et ça, que ce soit en Flash ou avec le UDK, c'est LA MORT. Les shaders sont des programmes exécutés sur le GPU et définis avec des langages généralement C-like mais qui ont la fâcheuse tendance d'ignorer totalement le principe de "dépendance" ou de "déclaration". En gros, si tu veux utiliser une fonction dans ton shader, tu dois l'avoir dans ton shader.

Il existe - selon les technos - des outils d'une complexité inimaginable qui permettent de "mixer" les shaders. Avec de la pré-recompilation paramétrique par exemple. Dans le Source Engine, ils ont un précompilateur qui fait ce genre de choses il me semble. C'est beaucoup plus compliqué à utiliser que tout ce qu'on fait habituellement en programmation. Et je ne parle même pas de la gestion des dépendances sur les ressources : comment affecter les bons buffers et les bonnes textures au bon shaders en fonction des sous-programmes qu'ils utilisent, etc....

Bref : c'est pourri. Tout pourri. Tout ? Non ! Car un village d'irrésistible Gaulois a codé un compilateur JIT qui transforme l'actionscript en bytecode pour ton GPU. En fait, le village Gaulois y'a qu'un seul mec et c'est Romain. Il a fait les compilateurs, celui de Minko 1 et celui de Minko 2). Et justement, la flexibilité, l'encapsulation et la factorisation sont des atouts majeurs des "ActionScript Shaders".

Pourquoi ? Par ce que comme je code mon shader en ActionScript, je peux créer des méthodes, tout simplement, qui vont effectuer des tâches particulières et que je pourrais réutiliser partout. Le compilateurs/la VM AS3 s'occupe de tout, l'exécution finale du code donnant lieu à un shader tout beau tout propre compilé dynamiquement.

Nous avons donc créé un système de "shader parts". Chaque shader part est une sorte de helper dédié à une fonctionnalité qu'il permet de retrouver dans tous vos petits shaders persos sans avoir à rien recoder. Exemple: AnimationShaderPart. Son rôle dans la vie, c'est de réaliser toutes les opérations de skinning/morphing. Tu as un exemple d'utilisation dans le BasicShader qui tout naturellement gère les animations.

Qu'est ce que ça veut dire ? Ca veut dire concrètement que toute fonctionnalité d'un shader peut-être le plus simplement du monde encapsulée dans une méthode/classe et réutiliser à l'infini comme vous le souhaitez. Dans le cas du AnimationShaderPart, ça veut dire que les gens qui utilisent Minko n'auront jamais à recoder quoique ce soit en rapport avec les animations dans les shaders, mais juste à appeler AnimationShaderPart.getVertexPosition(). Tout ce qu'on fait dans la vraie programmation habituelle, mais appliqué au GPU.

Mieux ! Le compilateur de Minko optimise tes shaders. Imaginons que tes shaders parts, codés par quelqu'un d'autre, effectue des opérations qu'un autre shader parts ou ton shader principal effectue déjà. Le compilateur va le détecter, refactorer automatiquement ton shader et n'effectuer l'opération qu'une seule fois. Modularité + optimisation à la volée. Le compilateur de Minko 2 fait des trucs encore plus foufou, par exemple il détecte les opérations "constantes" et les déporte automatiquement sur le CPU pour gagner en perfs/instructions.

Du coup LA question : existe-t-il un shader part qui gère la lumière ? Oui !
Est-il dans Minko 1 ? Non... Pour la release de Minko 1, on a pas eu le temps de porter le LightingEffect en AS Shaders. Il utilise une couche plus primitive et ne définit aucun shader parts.

Mais dans Minko 2, le lighting effect a été entièrement recodé, et tu as plein de shader parts pour gérer les réflexions, les ombres, l'éclairage, les projections, etc... Et donc gérer l'éclairage avec un simple appel de fonction dans tous tes petits shaders les plus fous :)

En gros : attends Minko 2, ça se fera en 10 minutes. Ou alors essaie d'implémenter le shadow mapping, ce qui est au demeurrant un très bon exercice pour comprendre les entrailles de l'API de rendu.

Citation

J'ai regardé le code de Minko Gravity (les cubes de couleur avec le son), et j'ai l'impression que je peux juste ajouter ce genre d'effet Mesh Par Mesh, c'est bien le cas ? Je sais pas si je m'embarque potentiellement dans une lourde tache, ou pas.

Le code de gravity est intéressant par ce qu'il se sert des objets "lumières" dans la scène, mais dans un shader complètement hacké pour avoir l'effet obtenu à l'écran. Encore une bonne démonstration du besoin évident de créer/manipuler des shaders pour arriver à ses fins.

Dans Minko 1, on peut appliquer un effect à tout un groupe de mesh.
Dans Minko 2, les effects seront "à priori" sur les mesh eux même. Et là c'est encore plus particulier par ce que dans cette nouvelle version, on ne parcourt même pas la scène pour la dessiner (ça prend trop de temps), donc l'API de rendu a subit un petit lifting mais le principe des priorités est toujours là.

Cordialement,

Modifié par Jean-Marc Le Roux, 21 March 2012 - 12:22 PM.




1 utilisateur(s) li(sen)t ce sujet

0 membre(s), 1 invité(s), 0 utilisateur(s) anonyme(s)

authorised training centre

Centre de Formation Mediabox - Adobe et Apple Authorised Training Center.

Déclaré auprès de la Direction du Travail et de la Formation Professionnelle

Mediabox : SARL au capital de 62.000€ - Numéro d'activité : 11 75 44555 75 - SIRET : 49371646800035

MEDIABOX, 23, rue de Bruxelles, 75009 PARIS

FFP