Maison  >  Article  >  interface Web  >  Une brève discussion sur l'application des compétences du tutoriel needUpdate dans three.js_html5

Une brève discussion sur l'application des compétences du tutoriel needUpdate dans three.js_html5

WBOY
WBOYoriginal
2016-05-16 15:50:572637parcourir

De nombreux objets dans three.js ont un attribut needUpdate, qui est rarement mentionné dans la documentation (mais il n'y a pas beaucoup de documents pour three.js, et de nombreux problèmes doivent être résolus par des problèmes sur github dans divers tutoriels sur Internet). Je ne suis pas très doué pour écrire ceci, car pour un simple programme d'entrée de gamme, cet attribut n'est pas utilisé.
Alors, à quoi sert cet attribut ? En un mot, il indique au moteur de rendu que je dois mettre à jour le cache pour cette image, bien qu'il soit très simple à utiliser comme indicateur, car vous voulez savoir pourquoi le cache doit le faire. être mis à jour, vous devez savoir quels caches sont mis à jour, il est donc nécessaire de le comprendre attentivement.
Pourquoi needUpdate est nécessaire
Tout d'abord, examinons pourquoi le cache est nécessaire. Le cache existe généralement pour réduire le nombre de transmissions de données, réduisant ainsi le temps passé par le programme. transmission de données. Voici la même chose. Généralement, il n'est pas facile pour un objet (Mesh) d'être affiché avec succès sur l'écran. Il doit être transféré trois fois sur le champ de bataille
Tout d'abord, toutes les données de sommet et les données de texture sont. lire à partir du disque local dans la mémoire via le programme.
Ensuite, une fois que le programme a effectué le traitement approprié dans la mémoire, il transférera les données de sommet et les données de texture des objets qui doivent être dessinés à l'écran vers la mémoire vidéo.
Enfin, lors du rendu de chaque image, les données de sommet et les données de texture dans la mémoire vidéo sont transférées vers le GPU pour l'assemblage et le dessin.
Selon le modèle de transmission de données pyramidale, la première étape est évidemment la plus lente si elle est transmise via le réseau dans un environnement comme WebGL, elle sera encore plus lente. La deuxième étape est le temps de transmission de la mémoire vers la vidéo. mémoire. Un simple test de données sera effectué ultérieurement.
Ensuite, il y a la fréquence d'utilisation de ces trois étapes. Pour les petites scènes, la première étape est une opération unique, c'est-à-dire que toutes les données d'une scène seront chargées en mémoire à chaque initialisation du programme. Pour les scénarios à grande échelle, un chargement asynchrone peut être effectué, mais ce n'est pas envisagé actuellement. La fréquence de la deuxième étape devrait être la chose la plus importante dont il faut parler cette fois-ci. Tout d'abord, écrivez un programme simple pour tester la consommation provoquée par cette étape de transmission

Copier le code
Le code est le suivant :

var canvas = document.createElement('canvas'); 'experimental -webgl');
var vertices = [];
for(var i = 0; i < 1000*3; i ){
vertices.push(i * Math.random() );
}
var buffer = _gl.createBuffer();
console.profile('buffer_test');
bindBuffer(); 🎜> function bindBuffer(){
for(var i = 0; i < 1000; i ){
_gl.bindBuffer(_gl.ARRAY_BUFFER, buffer);
_gl.bufferData(_gl.ARRAY_BUFFER, nouveau Float32Array (sommets), _gl.STATIC_DRAW);
}
}


Permettez-moi d'abord d'expliquer brièvement ce programme. Vertices est un tableau qui stocke les sommets. Ici, 1 000 sommets sont générés de manière aléatoire. Parce que chaque sommet a trois coordonnées : x, y et z, un tableau de 3 000 tailles est nécessaire. La commande _gl.createBuffer ouvre un tampon dans la mémoire vidéo pour stocker les données de sommet, puis utilise _gl.bufferData pour transférer une copie des données de sommet générées de la mémoire vers la mémoire vidéo. On suppose ici qu'il y a 1000 objets avec 1000 sommets dans une scène. Chaque sommet est constitué de 3 données flottantes de 32 bits et 4 octets. Après calcul, cela fait presque 1000 x 1000 x 12 = 11M de données. Temps de 15 ms, vous pouvez voir ici que 15 ms n'est qu'une très petite quantité de temps, mais pour un programme en temps réel, si vous souhaitez garantir une fréquence d'images de 30 ips, le temps requis pour chaque image doit être contrôlé à environ 30 ms, ce qui consiste à faire les données une seule fois. La transmission ne prend que la moitié du temps. Vous devez savoir que la grosse tête devrait être les opérations de dessin dans le GPU et les divers traitements dans le CPU. Chaque étape de l'ensemble du processus de rendu doit être avare.
Le nombre de transmissions à cette étape doit donc être minimisé. En fait, toutes les données de sommet et les données de texture peuvent être transférées de la mémoire vers la mémoire vidéo dès qu'elles sont chargées. .Tout d'abord, à ce stade, les données de sommet de l'objet (géométrie) qui doivent être dessinées sont transférées vers la mémoire d'affichage et le tampon est mis en cache dans Geometry.__webglVertexBuffer. Ensuite, l'attribut verticesNeedUpdate de la géométrie sera jugé à chaque fois. il est dessiné. Si aucune mise à jour n'est nécessaire, le cache actuel sera utilisé directement. Si vous voyez que verticesNeedUpate est vrai, les données de sommet dans Geometry seront retransmises à Geometry.__webglVertexBuffer. pour les objets statiques, mais si nous rencontrons des objets dont les sommets changent fréquemment, par exemple, utilisez des systèmes de particules qui utilisent les sommets comme particules et un maillage qui utilise l'animation squelettique. Ces objets changeront leurs sommets à chaque image, ils doivent donc définir leur propriété verticesNeedUpdate. à true chaque image pour indiquer au moteur de rendu que je dois retransmettre les données.
En fait, dans les programmes WebGL, la position des sommets est principalement modifiée dans le vertex shader pour compléter les effets de particules et l'animation squelettique, bien qu'elle soit plus facile à étendre si elle est calculée côté CPU, en raison de la limitation de la puissance de calcul de JavaScript. , La plupart de ces opérations gourmandes en calcul seront effectuées du côté GPU. Dans ce cas, il n'est pas nécessaire de retransmettre les données des sommets, donc le cas ci-dessus n'est en réalité pas beaucoup utilisé dans les programmes réels. Plus souvent, le cache des textures et des matériaux sera mis à jour.
Le cas ci-dessus décrit principalement une scène de transmission de données de sommet. En plus des données de sommet, il y a aussi une grande partie de texture. Une texture au format R8G8B8A8 de taille 1024*1024 occupera jusqu'à 4 Mo de mémoire, alors regardez la. exemple suivant

Copiez le code
Le code est le suivant :

var canvas = document.createElement('canvas');
var _gl = canvas.getContext('experimental-webgl');
var texture =
var img = new Image; img. onload = function(){
console.profile('texture test');
bindTexture();
console.profileEnd('texture test'); src = 'test_tex.jpg';
function bindTexture(){
_gl.bindTexture(_gl.TEXTURE_2D, texture);
_gl.texImage2D(_gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA) , _gl .UNSIGNED_BYTE, img);
}


Il n'est pas nécessaire de le répéter 1000 fois ici. Il faut 30 ms pour transmettre une texture 10241024 une fois, et près de 2 ms pour une texture 256256. Donc, dans three.js, la texture n'est transmise qu'une seule fois au début. puis si la propriété texture.needsUpdate n'est pas définie manuellement sur true, la texture qui a été transférée vers la mémoire vidéo sera utilisée directement.
Quels caches doivent être mis à jour
Ce qui précède décrit à travers deux cas pourquoi three.js doit ajouter un tel attribut needUpdate. Ensuite, listons quelques scénarios pour savoir dans quelles circonstances il en a besoin. à faire manuellement. Mettre à jour ces caches.
Chargement asynchrone des textures
C'est un petit écueil, car les images frontales sont chargées de manière asynchrone Si vous écrivez texture.needsUpdate=true directement après avoir créé l'img, three.js Le. Le moteur de rendu utilisera _gl.texImage2D pour transférer les données de texture vides vers la mémoire vidéo dans cette image, puis définira cet indicateur sur false. Après cela, les données de la mémoire vidéo ne seront pas mises à jour tant que l'image ne sera pas chargée. pour que l'image entière soit chargée dans l'événement onload avant d'écrire texture.needsUpdate = true
Texture vidéo
La plupart des textures chargent et transfèrent simplement l'image une fois comme dans le cas ci-dessus, mais pas pour. textures vidéo, car la vidéo est un flux d'images et l'image à afficher est différente dans chaque image, donc needUpdate doit être défini sur true dans chaque image pour mettre à jour les données de texture dans la carte graphique.
Utiliser le tampon de rendu
Le tampon de rendu est un objet spécial. Généralement, le programme affiche toute la scène directement à l'écran, mais s'il y a plus de post-traitement ou cet écran xxx (comme par exemple). occlusion ambiante basée sur l'écran), vous devez d'abord dessiner la scène dans un tampon de rendu. Ce tampon est en fait une texture, mais il est généré par le dessin précédent au lieu d'être chargé à partir du disque. Il existe un objet de texture spécial WebGLRenderTarget dans three.js pour initialiser et enregistrer le tampon de rendu. Cette texture doit également définir needUpdate sur true dans chaque image
Besoins du matériauMise à jour
Le matériau est en trois. js est décrit via THREE.Material.En fait, le matériel n'a aucune donnée à transmettre, mais pourquoi avons-nous besoin de créer un needUpdate?Nous voulons également parler ici du shader. La traduction littérale de shader est shader, qui est fourni dans le GPU. La possibilité de programmer pour traiter les sommets et les pixels. En peinture, il existe un terme d'ombrage pour représenter la méthode d'ombrage de la peinture. L'ombrage dans le GPU est similaire à celui de l'ombrage. exprimer le matériau de l'objet. ok, puisque le shader est une exécution. Les programmes sur le GPU, comme tous les programmes, doivent être compilés et liés une fois. Dans WebGL, le programme du shader est compilé au moment de l'exécution, ce qui prend bien sûr du temps. il est donc préférable de compiler et d'exécuter le programme une fois terminé. Par conséquent, dans three.js, le programme shader est compilé et lié lorsque le matériel est initialisé et l'objet programme obtenu après compilation et liaison est mis en cache. Généralement, un matériau n'a pas besoin de recompiler l'intégralité du shader. Pour ajuster le matériau, il suffit de modifier les paramètres uniformes du shader. Mais si vous remplacez l'intégralité du matériel, par exemple en remplaçant le shader phong d'origine par un shader lambert, vous devez définir Material.needsUpdate sur true et recompiler. Cependant, cette situation est rare et la situation la plus courante est celle mentionnée ci-dessous.
Ajout et suppression de lumières
Cela devrait être relativement courant dans les scènes. Peut-être que de nombreuses personnes qui viennent de commencer à utiliser three.js tomberont dans cette fosse et ajouteront dynamiquement des lumières à la scène. J'ai allumé une lumière, j'ai découvert que la lumière ne fonctionnait pas. Cependant, c'était lors de l'utilisation du shader intégré de three.js, tel que phong et lambert. Si vous regardez le code source dans le moteur de rendu, vous constaterez que. three.js est dans le code du shader intégré. Utilisez #define pour définir le nombre de lumières dans la scène, et la valeur de ce #define est obtenue par le shader d'épissage de chaîne à chaque mise à jour du matériau. Le code est le suivant.

Copier le code
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,

Il est vrai que cette façon d'écrire peut réduire efficacement l'utilisation des registres GPU. S'il n'y a qu'une seule lumière, vous ne pouvez déclarer qu'une variable uniforme requise pour une seule lumière, mais à chaque fois le nombre de lumières change, surtout quand ajout Vous devez recoudre, compiler et lier le shader. À ce stade, vous devez également définir Material.needsUpdate of all materials sur true
Changer la texture
Changer la texture ici le fait ; cela ne signifie pas mettre à jour les données de texture, mais c'est parce que le matériau d'origine a utilisé des textures puis a cessé de les utiliser, ou que le matériau d'origine n'a pas utilisé de textures et les a ensuite ajoutées plus tard. Si vous ne mettez pas à jour manuellement les matériaux de force, l'effet final sera obtenu. être différent de ce que vous pensiez. Les raisons de ce problème sont les suivantes. L'ajout de lumières ci-dessus est presque le même, car une macro est ajoutée au shader pour déterminer si des textures sont utilisées,

Copier le code
Code comme suit :

parameters.map "#define USE_MAP" : "",
parameters.envMap ? "#define USE_ENVMAP" : "",
parameters.lightMap ? "#define USE_LIGHTMAP" : "",
parameters.bumpMap ? "#define USE_BUMPMAP" : "",
parameters.normalMap ? Define USE_NORMALMAP" : "",
parameters.specularMap ? "#define USE_SPECULARMAP" : "",

Donc, chaque fois que map, ou envMap ou lightMap change la vraie valeur, le matériau doit être mis à jour
Modifications dans d'autres données de sommet
En fait, le changement de texture ci-dessus posera également un problème. La raison principale est qu'il n'y a pas de texture lors de l'initialisation, mais qu'elle est ajoutée dynamiquement plus tard. Dans cet environnement, il ne suffit pas de définir Material.needsUpdate sur true. Vous devez également définir Geometry.uvsNeedsUpdate sur true. Pourquoi ce problème se produit-il toujours à cause de l'optimisation du programme par three.js. la géométrie et le matériau sont initialisés pour la première fois dans le moteur de rendu, s'il est jugé qu'il n'y a pas de texture, bien que les données en mémoire contiennent des données UV pour chaque sommet, mais three.js ne copiera toujours pas ces données dans la vidéo. L'intention initiale devrait être d'économiser un espace précieux dans la mémoire vidéo, mais après avoir ajouté la texture, la géométrie ne retransmettra pas intelligemment les données UV pour l'utilisation de la texture. Nous devons définir manuellement uvsNeedsUpdate pour lui dire qu'il est temps de mettre à jour. les uv. Ce problème m'a vraiment gêné pendant longtemps au début.
Pour plus d'informations sur l'attribut needUpdate de plusieurs données de sommet, veuillez consulter ce numéro
https://github.com/mrdoob/trois.js/wiki/Updates
Enfin
trois L'optimisation de .js est bonne, mais diverses optimisations entraînent divers pièges. Dans ce cas, le meilleur moyen est de regarder le code source ou de classer les problèmes sur github.
Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn