De nombreux objets en trois.js ont une propriété BesanSupdate, et ils sont rarement écrits dans les documents (mais il n'y a pas beaucoup de documents en trois.js, et de nombreux problèmes doivent encore s'appuyer sur des problèmes sur GitHub). Ils ne savent pas comment écrire ceci dans divers tutoriels en ligne, car pour des programmes d'introduction simples, cette propriété ne peut pas être utilisée.
Alors, à quoi sert cet attribut? En un mot, je dis au rendu que je devrais mettre à jour le cache dans ce cadre. Bien qu'il soit très simple à utiliser comme bit de drapeau, car vous devez savoir pourquoi vous devez mettre à jour le cache et les caches à mettre à jour, il est toujours nécessaire de le comprendre attentivement.
Pourquoi les besoins à épuiserTout d'abord, examinons pourquoi Cache est nécessaire. L'existence de cache est généralement de réduire le nombre de temps de transmission de données, réduisant ainsi le temps consacré à la transmission des données. Ici, il est également vrai qu'il n'est pas facile qu'un objet (MESH) soit affiché avec succès à l'écran à la fin. Il doit être transféré à trois reprises sur le champ de bataille.
La première consiste à lire toutes les données de vertex et les données de texture du disque local dans la mémoire via le programme.
Ensuite, une fois que le programme a effectué un traitement approprié en mémoire, il doit transférer les données de sommet et les données de texture des objets qui doivent être dessinés vers l'écran vers la mémoire vidéo.
Enfin, lors du rendu de chaque trame, les données de sommet et les données de texture dans la mémoire vidéo sont rincées dans le GPU pour l'assemblage et le dessin.
Selon le modèle de transmission de données de type pyramide, la première étape est évidemment la plus lente. S'il est transmis via le réseau dans un environnement comme WebGL, il sera encore plus lent. Le second est le temps de la mémoire à la mémoire vidéo, qui sera un test de données simple plus tard.
Ensuite, il y a la fréquence d'utilisation de ces trois étapes. Pour les petits scénarios, la première étape est unique, c'est-à-dire que chaque fois que le programme est initialisé, toutes les données d'un scénario seront chargées dans la mémoire. Pour les grands scénarios, certains chargements asynchrones peuvent être effectués, mais ce n'est pas actuellement le problème que nous considérons. La fréquence de la deuxième étape devrait être la chose la plus importante à parler de cette fois. Tout d'abord, rédigez un programme simple pour tester la consommation causée par le fait de faire cette étape de transmission.
var canvas = document.CreateElement ('Canvas');
var _gl = canvas.getContext ('expérimental-webgl');
Var Vertices = [];
pour (var i = 0; i <1000 * 3; i ++) {
vertices.push (i * math.random ());
}
var buffer = _gl.createBuffer ();
console.profile ('buffer_test');
bindBuffer ();
console.profileend ('buffer_test');
fonction bindBuffer () {
pour (var i = 0; i <1000; i ++) {
_gl.bindbuffer (_gl.array_buffer, tampon);
_gl.bufferdata (_gl.array_buffer, new float32Array (Vertices), _gl.static_draw);
}
}
Expliquons brièvement ce programme en premier. Les sommets sont un tableau qui sauve des sommets. Ici, 1 000 sommets sont générés au hasard. Parce que chaque sommet a trois coordonnées x, y et z, un tableau de 3000 taille est nécessaire. La commande _gl.CreateBuffer ouvre un cache pour stocker des données de sommet dans la mémoire vidéo, puis utilise _gl.bufferdata pour transférer les données de sommet générées de la mémoire à la mémoire vidéo. Ici, nous supposons qu'il y a 1000 objets avec 1000 sommets dans une scène, chaque sommet est de 3 octets 32 bits 4 de données flottantes. Calculez les données de près de 1000 x 1000 x 12 = 11m. Le profil prend environ 15 ms. Ici, nous pouvons voir comment 15 ms ne sont qu'un peu de temps. Cependant, pour un programme en temps réel, si vous souhaitez vous assurer une fréquence d'images de 30 images par seconde, le temps requis pour chaque trame doit être contrôlé à environ 30 ms. Comment peut-il prendre la moitié du temps pour simplement faire la transmission des données? Vous devez savoir que la grande tête devrait être les opérations de dessin dans le GPU et divers traitements dans le CPU, et vous devriez être avare à chaque étape de l'opération dans tout le processus de rendu.
Par conséquent, le nombre de transmissions dans cette étape doit être minimisé. En fait, il peut être utilisé pour transférer toutes les données du sommet et les données de texture de la mémoire à la mémoire vidéo lorsqu'elle est chargée. C'est ce que fait trois.js maintenant. Les données de sommet de l'objet qui doivent être dessinées sont transférées à la mémoire vidéo pour la première fois et mettent en cache le tampon en géométrie .__ WebGlvertexBuffer. Après cela, chaque fois que vous dessinez, vous jugerez la propriété VerticesNeedUpdate de géométrie. Si vous n'avez pas besoin de mettre à jour, utilisez directement le cache actuel. Si vous voyez que VerticesNeedUpate est vrai, les données de sommet dans la géométrie seront transférées à la géométrie .__ WebGlvertexBuffer. Généralement, nous n'avons pas besoin de cette étape pour les objets statiques. Cependant, si nous rencontrons des objets qui changent fréquemment, comme l'utilisation des sommets comme systèmes de particules, et le maillage qui utilise des animations squelettes, ces objets changeront leurs sommets dans chaque trame, de sorte que chaque trame doit définir ses vertices newEupdate la propriété pour dire au rendu que je dois recommencer les données!
En fait, dans les programmes WebGL, plus de positions de sommet seront modifiées dans le vertex shader pour terminer les effets des particules et l'animation du squelette. Bien qu'il soit plus facile de se développer s'il est placé du côté CPU pour calculer, en raison des limites de la puissance de calcul de JavaScript, davantage de ces opérations de calcul seront placées du côté GPU. Dans ce cas, il n'est pas nécessaire de retransmettre des données de sommet, de sorte que le cas ci-dessus n'est pas beaucoup utilisé dans le programme réel, et il s'agit davantage de mettre à jour le cache de texture et de matériel.
Le cas ci-dessus décrit principalement un scénario où les données de sommet sont transmises. En plus des données de sommet, il y a aussi une grande tête qui est la texture. Une texture de format R8G8B8A8 de taille 1024 * 1024 doit occuper une taille de mémoire jusqu'à 4 m, alors regardez l'exemple suivant.
var canvas = document.CreateElement ('Canvas');
var _gl = canvas.getContext ('expérimental-webgl');
var texture = _gl.createTexture ();
var img = nouvelle image;
img.onload = function () {
console.profile («test de texture»);
bindTexture ();
console.profileend («test de texture»);
}
img.src = 'test_tex.jpg';
fonction bindTexture () {
_gl.bindTexture (_gl.Texture_2d, texture);
_gl.texinage2d (_gl.Texture_2d, 0, _gl.rgba, _gl.rgba, _gl.unsigned_byte, img);
}
Il n'est pas nécessaire de répéter les 1000 fois pervertis ici. Il faut 30 ms pour transmettre la texture de 10241024 à la fois, et une image 256256 est presque 2 ms. Par conséquent, dans trois.js, la texture ne doit être transmise qu'une seule fois au début. Après cela, si la propriété texture.needSupdate n'est pas définie manuellement sur true, la texture qui a été transférée à la mémoire vidéo sera utilisée directement.
Quels caches doivent être mis à jourCe qui précède décrit pourquoi trois.js doivent ajouter un tel attribut de busité à travers deux cas. Ensuite, énumérez plusieurs scénarios à savoir dans les circonstances dont vous avez besoin pour mettre à jour manuellement ces caches.
Chargement asynchrone des texturesIl s'agit d'une petite fosse, car l'image frontale est chargée de manière asynchrone. Si vous écrivez texture.needSupdate = true directement après la création d'IMG, le rendu trois.js utilisera _gl.texiny2d pour transférer des données de texture vides dans la mémoire vidéo dans ce cadre, puis définir ce drapeau sur false. Ensuite, lorsque l'image est chargée, les données de mémoire vidéo ne seront pas mises à jour. Par conséquent, vous devez attendre que l'image entière soit chargée dans l'événement Onload avant d'écrire la texture.NeedSupdate = True
Texture vidéoLa plupart des textures sont comme le cas ci-dessus pour charger et transmettre des images directement, mais pas pour les textures vidéo, car la vidéo est un flux d'image, et l'image à afficher dans chaque trame est différente, vous devez donc définir les besoins en update sur true pour chaque trame pour mettre à jour les données de texture dans la carte graphique.
Utiliser le tampon de renduLe tampon de rendu est un objet relativement spécial. Généralement, le programme rincera directement à l'écran après que la scène entière est dessinée. Cependant, s'il y a plus de post-traitement ou de xxx basé sur l'écran (comme l'occurrence ambiante basée sur l'écran), la scène doit être dessinée en premier sur un tampon de rendu. Ce tampon est en fait une texture, mais il est généré par le dessin précédent, non chargé à partir du disque. Il existe un objet de texture spécial webglRendertarget dans trois.js pour initialiser et enregistrer RenderBuffer. Cette texture doit également être définie sur true dans chaque trame.
Les besoins du matérielLe matériau est décrit en trois.js à trois. En fait, le matériau n'a pas beaucoup de données à transmettre, mais pourquoi avez-vous besoin de faire un besoin-dat? Ici, je vais parler du shader. Shader est traduit par un shader, qui offre la possibilité de programmer des sommets et des pixels dans GPU. Il y a un terme ombragé dans la peinture pour représenter la méthode de peinture claire et sombre. L'ombrage dans le GPU est similaire. La lumière et l'obscurité de la lumière sont calculées par le programme pour exprimer le matériau d'un objet. OK, puisque Shader est un programme exécuté sur le GPU, comme tous les programmes, il est nécessaire d'effectuer une opération de compilation et de liaison. Dans WebGL, le programme Shader est compilé lors de l'exécution, ce qui prend bien sûr le temps, il est donc préférable d'être compilé et d'exécuter jusqu'à la fin du programme. Ainsi, lorsque le matériau est initialisé en trois.js, le programme Shader est compilé et lié et l'objet de programme obtenu après la mise en cache de la liaison de compilation. Généralement, un matériau n'a plus besoin de recompiler tout le shader. Pour ajuster le matériau, il vous suffit de modifier les paramètres uniformes du shader. Cependant, si vous remplacez l'intégralité du matériau, comme le remplacement du shader Pong d'origine par un shader Lambert, vous devez définir du matériel. Cependant, cette situation est rare et la plus courante est celle mentionnée ci-dessous.
Ajouter et supprimer les lumièresCela devrait être plus courant dans la scène. Peut-être que beaucoup de gens qui viennent de commencer à utiliser trois.js tomberont dans cette fosse. Après avoir ajouté une lumière à la scène dynamiquement, ils constatent que la lumière ne fonctionne pas. Cependant, lorsque vous utilisez le shader construit en trois.js, par exemple, Phong, Lambert, en regardant le code source dans le rendu, vous constaterez que Three.js utilise #define dans le code de shader intégré pour définir le nombre de lumières dans la scène. La valeur de ce #define est obtenue par String Splicing Shader chaque fois que le matériau est mis à jour. Le code est le suivant.
"#define max_dir_lights" + paramètres.maxdirlights,
"#define max_point_lights" + paramètres.maxpointlights,
"#define max_spot_lights" + paramètres.maxspotlights,
"#define max_hemi_lights" + paramètres.maxhemilights,
En effet, cette méthode d'écriture peut réduire efficacement l'utilisation des registres GPU. S'il n'y a qu'une seule lumière, vous ne pouvez déclarer que la variable uniforme requise pour une lumière. Cependant, lorsque le nombre de lumières change, en particulier lors de l'ajout, vous devez recoucher et compiler et lier le shader. Pour le moment, vous devez également définir le matériau.
Changer de textureLe changement de texture ici ne signifie pas la mise à jour des données de texture, mais plutôt que le matériau d'origine a utilisé la texture, mais n'a pas été utilisé plus tard, ou le matériau d'origine n'a pas utilisé la texture, puis l'a ajouté. Si le matériel n'est pas mis à jour manuellement, l'effet final sera différent de ce que vous pensez. La raison de ce problème est similaire à l'éclairage mentionné ci-dessus, et c'est également parce qu'une macro a été ajoutée au shader pour déterminer si la texture a été utilisée.
paramètres.map? "#define use_map": "",
paramètres.envmap? "#define use_envmap": "",
paramètres.lightmap? "#define use_lightmap": "",
Paramètres.bumpMap? "#define use_bumpmap": "",
Paramètres.Normalmap? "#define use_normalmap": "",
Paramètres.SpecularMap? "#define use_speculairemap": "",
Par conséquent, chaque fois que la carte, les envmaps ou lesmaps changent la valeur vraie, vous devez mettre à jour le matériel
Modifications dans d'autres données de sommetEn fait, le changement de texture ci-dessus créera un problème. C'est principalement parce qu'il n'y a pas de texture lors de l'initialisation. Cependant, dans cet environnement ajouté dynamiquement, il ne suffit pas de définir du matériel. Il doit également définir la géométrie.UVSNeedSupdate à True. Pourquoi y a-t-il un tel problème? C'est en raison de l'optimisation du programme par trois.js. Lors de l'initialisation de la géométrie et du matériel pour la première fois dans Renderer, s'il est jugé qu'il n'y a pas de texture, bien qu'il y ait chaque données UV de sommet dans les données en mémoire, trois.js ne copieront pas ces données dans la mémoire vidéo. L'intention d'origine devrait être de sauvegarder un espace mémoire vidéo précieux. Cependant, après avoir ajouté de la texture, la géométrie ne transférera pas intelligemment ces données UV pour une utilisation de texture. Nous devons définir manuellement les UVSNeedSupdate pour informer qu'il est temps de mettre à jour les UV, cette question m'a vraiment fait tricher pendant longtemps au début.
Pour les attributs NeedUpdate de plusieurs types de données de sommet, vous pouvez voir ce problème
https://github.com/mrdoob/tthree.js/wiki/Updates
enfinL'optimisation de trois.js fait du bon travail, mais elle apporte divers pièges qui peuvent être touchés par diverses optimisations. La meilleure façon de le faire est de regarder le code source, ou d'aller à GitHub pour mentionner les problèmes