_gl.bufferData(_gl.ARRAY_BUFFER, neues Float32Array (Vertices), _gl.STATIC_DRAW);
Lassen Sie mich zunächst kurz erklären, dass es sich bei Vertices um ein Array handelt, das Scheitelpunkte speichert. Da jeder Scheitelpunkt drei Koordinaten hat: x, y und z, wird ein Array mit einer Größe von 3000 benötigt Der Befehl _gl.createBuffer öffnet einen Puffer im Videospeicher, um Scheitelpunktdaten zu speichern, und verwendet dann _gl.bufferData, um eine Kopie der generierten Scheitelpunktdaten vom Speicher in den Videospeicher zu übertragen. Es wird hier davon ausgegangen, dass es 1000 Objekte mit 1000 Scheitelpunkten in einer Szene gibt. Jeder Scheitelpunkt besteht aus 3 Float-Daten mit 32 Bits und 4 Bytes. Nach der Berechnung sind es fast 1000 x 1000 x 12 = 11 MB. 15 ms Zeit, hier sehen Sie vielleicht, dass 15 ms nur eine so kleine Zeitspanne sind, aber wenn Sie für ein Echtzeitprogramm eine Bildrate von 30 fps sicherstellen möchten, sollte die für jedes Bild erforderliche Zeit auf etwa 30 ms eingestellt werden. Das bedeutet, dass die Übertragung der Daten nur die Hälfte der Zeit in Anspruch nimmt. Sie müssen wissen, dass die Zeichenvorgänge in der GPU und die verschiedenen Verarbeitungsvorgänge in der CPU geizig sein sollten.
Daher sollte die Anzahl der Übertragungen in diesem Schritt minimiert werden. Tatsächlich können alle Vertex- und Texturdaten vom Speicher in den Videospeicher übertragen werden. Dies ist, was three.js jetzt tut . Zu diesem Zeitpunkt werden die Scheitelpunktdaten des zu zeichnenden Objekts (Geometrie) in den Anzeigespeicher übertragen und der Puffer wird in der Geometrie.__webglVertexBuffer zwischengespeichert. Anschließend wird jedes Mal das verticesNeedUpdate-Attribut der Geometrie beurteilt Wenn keine Aktualisierung erforderlich ist, wird der aktuelle Cache verwendet. Wenn Sie sehen, dass verticesNeedUpate wahr ist, werden die Scheitelpunktdaten in Geometry erneut übertragen für statische Objekte, aber wenn wir auf Objekte stoßen, deren Scheitelpunkte sich häufig ändern, verwenden Sie beispielsweise Partikelsysteme, die Scheitelpunkte als Partikel verwenden, und Mesh, das Skelettanimationen verwendet. Diese Objekte ändern ihre Scheitelpunkte in jedem Frame, daher müssen sie ihre verticesNeedUpdate-Eigenschaft festlegen um jeden Frame wahr zu machen, um dem Renderer mitzuteilen, dass ich die Daten erneut übertragen muss.
Tatsächlich wird die Position der Scheitelpunkte in WebGL-Programmen meist im Vertex-Shader geändert, um Partikeleffekte und Skelettanimationen zu vervollständigen. Aufgrund der begrenzten Rechenleistung von JavaScript ist die Erweiterung jedoch einfacher, wenn sie auf der CPU-Seite berechnet wird , Die meisten dieser rechenintensiven Vorgänge werden auf der GPU-Seite ausgeführt. In diesem Fall besteht keine Notwendigkeit, die Scheitelpunktdaten erneut zu übertragen, sodass der obige Fall in tatsächlichen Programmen tatsächlich nicht häufig verwendet wird. Der Cache von Texturen und Materialien wird häufiger aktualisiert.
Der obige Fall beschreibt hauptsächlich eine Szene, in der Vertexdaten übertragen werden. Zusätzlich zu den Vertexdaten gibt es auch einen großen Teil der Textur. Eine Textur im R8G8B8A8-Format belegt bis zu 4 MB Speicher folgendes Beispiel
var canvas = document.createElement('canvas');
var _gl = canvas.getContext('experimental-webgl');
var img = new Image; img. onload = function(){
console.profile('texture test');
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);
}
Es ist nicht nötig, es 1000 Mal zu wiederholen. Es dauert 30 ms, um eine 10241024-Textur einmal zu übertragen, und fast 2 ms für eine 256256-Textur. Daher wird die Textur zu Beginn nur einmal übertragen. Wenn dann die Eigenschaft „texture.needsUpdate“ nicht manuell auf „true“ gesetzt ist, wird die Textur, die in den Videospeicher übertragen wurde, direkt verwendet.
Welche Caches müssen aktualisiert werden? Das Obige beschreibt anhand von zwei Fällen, warum three.js ein solches NeedsUpdate-Attribut hinzufügen muss. Als Nächstes listen wir einige Szenarien auf, um zu erfahren, unter welchen Umständen es benötigt wird Diese Caches müssen manuell aktualisiert werden.
Asynchrones Laden von Texturen Dies ist eine kleine Gefahr, da die Frontend-Bilder asynchron geladen werden. Wenn Sie Texture.needsUpdate=true direkt nach dem Erstellen des IMG schreiben, wird three.js verwendet Der Renderer verwendet _gl.texImage2D, um die leeren Texturdaten in diesem Frame in den Videospeicher zu übertragen, und setzt dieses Flag dann auf „false“. Danach werden die Videospeicherdaten nicht aktualisiert, bis das Bild geladen ist damit das gesamte Bild im Onload-Ereignis geladen wird, bevor Texture.needsUpdate = true
Video Texture Die meisten Texturen laden und übertragen das Bild nur einmal wie im obigen Fall, aber nicht für Videotexturen, da es sich bei dem Video um einen Bildstream handelt und das anzuzeigende Bild in jedem Frame unterschiedlich ist. Daher muss NeedsUpdate in jedem Frame auf „True“ gesetzt werden, um die Texturdaten in der Grafikkarte zu aktualisieren.
Renderpuffer verwenden Renderpuffer ist ein spezielles Objekt. Im Allgemeinen überträgt das Programm die gesamte Szene direkt auf den Bildschirm, aber wenn mehr Nachbearbeitung oder dieser bildschirmbasierte xxx erfolgt (z Bei einer bildschirmbasierten Umgebungsokklusion müssen Sie die Szene zunächst in einen Renderpuffer zeichnen. Dieser Puffer ist eigentlich eine Textur, wird jedoch durch die vorherige Zeichnung generiert und nicht von der Festplatte geladen. Es gibt ein spezielles Texturobjekt WebGLRenderTarget in three.js, um den Renderbuffer zu initialisieren und zu speichern. Diese Textur muss außerdem in jedem Frame auf true gesetzt werden. js wird durch THREE.Material beschrieben. Tatsächlich müssen keine Daten übertragen werden, aber warum müssen wir hier auch über den Shader sprechen? Die in der GPU vorgesehene Möglichkeit zur Verarbeitung von Scheitelpunkten und Pixeln ist der Begriff „Schattierung“, der die Schattierung in der GPU darstellt Drücken Sie das Material des Objekts aus, da der Shader wie alle Programme einmal zur Laufzeit kompiliert und verknüpft werden muss. Daher ist es am besten, das Programm einmal zu kompilieren und auszuführen. Daher wird in three.js das Shader-Programm kompiliert und verknüpft, wenn das Material initialisiert wird, und das nach der Kompilierung und Verknüpfung erhaltene Programmobjekt wird zwischengespeichert. Im Allgemeinen muss ein Material nicht den gesamten Shader neu kompilieren. Um das Material anzupassen, müssen Sie nur die einheitlichen Parameter des Shaders ändern. Wenn Sie jedoch das gesamte Material ersetzen, z. B. den ursprünglichen Phong-Shader durch einen Lambert-Shader ersetzen, müssen Sie material.needsUpdate auf true setzen und neu kompilieren. Diese Situation kommt jedoch selten vor, und die häufigere Situation ist die unten beschriebene.
Hinzufügen und Löschen von Lichtern Dies sollte in Szenen relativ häufig vorkommen. Vielleicht fallen viele Leute, die gerade erst mit der Verwendung von three.js begonnen haben, in diese Grube und fügen der Szene nach der Einstellung dynamisch Lichter hinzu Ich habe festgestellt, dass das Licht nicht funktioniert, wenn der integrierte Shader von Three.js verwendet wird, z. B. Phong und Lambert. Wenn Sie sich den Quellcode im Renderer ansehen three.js befindet sich im integrierten Shader-Code. Verwenden Sie #define, um die Anzahl der Lichter in der Szene festzulegen. Der Wert dieses #define wird bei jeder Aktualisierung des Materials durch String-Splicing-Shader ermittelt
Code kopieren"#define MAX_SPOT_LIGHTS "parameters.maxSpotLights,
"#define MAX_HEMI_LIGHTS "parameters.maxHemiLights,
Es stimmt, dass diese Schreibweise die Verwendung von GPU-Registern effektiv reduzieren kann. Wenn nur ein Licht vorhanden ist, können Sie nur eine einheitliche Variable deklarieren, die für ein Licht erforderlich ist, aber jedes Mal, wenn sich die Anzahl der Lichter ändert, insbesondere wenn Hinzufügen Sie müssen den Shader neu zusammenfügen, kompilieren und verknüpfen. Zu diesem Zeitpunkt müssen Sie auch material.needsUpdate aller Materialien auf true setzen.
Textur ändern Die Textur hier ändern Dies bedeutet nicht, dass die Texturdaten aktualisiert werden, sondern weil das Originalmaterial Texturen verwendet und diese dann nicht mehr verwendet, oder weil das Originalmaterial keine Texturen verwendet und diese später hinzugefügt hat. Wenn Sie die Materialien nicht zwangsweise manuell aktualisieren, wird dies der endgültige Effekt sein Die Gründe für dieses Problem sind wie folgt: Das Hinzufügen von Lichtern oben ist fast dasselbe, da dem Shader ein Makro hinzugefügt wird, um zu bestimmen, ob Texturen verwendet werden:
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" : "",
Jedes Mal, wenn Map, EnvMap oder LightMap den wahren Wert ändern, muss das Material geändert werden aktualisiert werden
Änderungen in anderen Scheitelpunktdaten Eigentlich verursacht die obige Texturänderung auch ein Problem. Der Hauptgrund ist, dass während der Initialisierung keine Textur vorhanden ist, diese jedoch später dynamisch hinzugefügt wird In dieser Umgebung reicht es nicht aus, material.needsUpdate auf true zu setzen. Warum tritt dieses Problem immer noch auf, wenn das Programm optimiert wird? Geometrie und Material werden zum ersten Mal im Renderer initialisiert. Wenn festgestellt wird, dass keine Textur vorhanden ist, enthalten die Daten im Speicher zwar UV-Daten für jeden Scheitelpunkt, aber three.js kopiert diese Daten immer noch nicht in das Video Speicher Die ursprüngliche Absicht sollte darin bestehen, wertvollen Videospeicherplatz zu sparen, aber nach dem Hinzufügen der Textur überträgt die Geometrie die UV-Daten nicht intelligent zur Texturverwendung. Wir müssen uvsNeedsUpdate manuell festlegen, um ihm mitzuteilen, dass es Zeit für eine Aktualisierung ist Das UV. Dieses Problem hat mich am Anfang wirklich lange beschäftigt.
Informationen zum Attribut „needUpdate“ mehrerer Vertexdaten finden Sie in dieser Ausgabe
https://github.com/mrdoob/ three.js/wiki/Updates
Finally drei Die Optimierung von .js ist gut, aber verschiedene Optimierungen bringen verschiedene Fallstricke mit sich. In diesem Fall ist es am besten, sich den Quellcode oder die Dateiprobleme auf Github anzusehen.