[{"data":1,"prerenderedAt":229},["ShallowReactive",2],{"blog-de-threejs-performance":3},{"id":4,"title":5,"body":6,"cover":213,"date":214,"description":215,"draft":216,"extension":217,"locale":218,"meta":219,"navigation":220,"path":221,"seo":222,"stem":223,"tags":224,"__hash__":228},"blog\u002Fblog\u002Fde\u002Fthreejs-performance.md","Three.js-Performance: flüssig auf jedem Gerät",{"type":7,"value":8,"toc":209},"minimark",[9,13,18,21,27,35,173,184,188,195,202,205],[10,11,12],"p",{},"Eine 3D-Szene, die auf meinem Entwicklungsrechner mit 120 Bildern pro Sekunde läuft, sagt mir wenig. Die ehrliche Frage lautet: Wie verhält sie sich auf dem drei Jahre alten Smartphone eines Kunden? Aus den Projekten der letzten Jahre habe ich gelernt, dass Performance in Three.js selten eine Frage roher Rechenkraft ist — sondern eine des Maßhaltens.",[14,15,17],"h2",{"id":16},"weniger-draw-calls-mehr-ruhe","Weniger Draw Calls, mehr Ruhe",[10,19,20],{},"Der größte Hebel sind fast immer die Draw Calls. Jedes Mesh, das einzeln gezeichnet wird, kostet die CPU einen Gesprächsgang zur GPU. Tausend einzelne Objekte bedeuten tausend dieser Gespräche pro Bild — und genau hier brechen mobile Geräte ein.",[22,23,24],"blockquote",{},[10,25,26],{},"Performance ist selten ein einzelner Trick. Sie ist die Summe vieler kleiner Entscheidungen, die man von Anfang an mitdenkt.",[10,28,29,30,34],{},"Wenn ich dieselbe Geometrie hundertfach brauche — Bäume, Partikel, Bausteine — greife ich zu ",[31,32,33],"code",{},"InstancedMesh",". Aus hundert Draw Calls wird einer.",[36,37,42],"pre",{"className":38,"code":39,"language":40,"meta":41,"style":41},"language-js shiki shiki-themes github-light github-dark","const mesh = new THREE.InstancedMesh(geometry, material, 1000);\nconst matrix = new THREE.Matrix4();\nfor (let i = 0; i \u003C 1000; i++) {\n  matrix.setPosition(positions[i]);\n  mesh.setMatrixAt(i, matrix);\n}\n","js","",[31,43,44,82,104,143,155,167],{"__ignoreMap":41},[45,46,49,53,57,60,63,66,70,73,76,79],"span",{"class":47,"line":48},"line",1,[45,50,52],{"class":51},"szBVR","const",[45,54,56],{"class":55},"sj4cs"," mesh",[45,58,59],{"class":51}," =",[45,61,62],{"class":51}," new",[45,64,65],{"class":55}," THREE",[45,67,69],{"class":68},"sVt8B",".",[45,71,33],{"class":72},"sScJk",[45,74,75],{"class":68},"(geometry, material, ",[45,77,78],{"class":55},"1000",[45,80,81],{"class":68},");\n",[45,83,85,87,90,92,94,96,98,101],{"class":47,"line":84},2,[45,86,52],{"class":51},[45,88,89],{"class":55}," matrix",[45,91,59],{"class":51},[45,93,62],{"class":51},[45,95,65],{"class":55},[45,97,69],{"class":68},[45,99,100],{"class":72},"Matrix4",[45,102,103],{"class":68},"();\n",[45,105,107,110,113,116,119,122,125,128,131,134,137,140],{"class":47,"line":106},3,[45,108,109],{"class":51},"for",[45,111,112],{"class":68}," (",[45,114,115],{"class":51},"let",[45,117,118],{"class":68}," i ",[45,120,121],{"class":51},"=",[45,123,124],{"class":55}," 0",[45,126,127],{"class":68},"; i ",[45,129,130],{"class":51},"\u003C",[45,132,133],{"class":55}," 1000",[45,135,136],{"class":68},"; i",[45,138,139],{"class":51},"++",[45,141,142],{"class":68},") {\n",[45,144,146,149,152],{"class":47,"line":145},4,[45,147,148],{"class":68},"  matrix.",[45,150,151],{"class":72},"setPosition",[45,153,154],{"class":68},"(positions[i]);\n",[45,156,158,161,164],{"class":47,"line":157},5,[45,159,160],{"class":68},"  mesh.",[45,162,163],{"class":72},"setMatrixAt",[45,165,166],{"class":68},"(i, matrix);\n",[45,168,170],{"class":47,"line":169},6,[45,171,172],{"class":68},"}\n",[10,174,175,176,179,180,183],{},"Wo Objekte statisch sind und nicht einzeln bewegt werden müssen, führe ich Geometrien stattdessen zusammen — mit ",[31,177,178],{},"mergeGeometries"," aus den ",[31,181,182],{},"BufferGeometryUtils"," wird aus vielen Meshes ein einziges. Auch das Teilen von Material-Instanzen statt ständiger Neuanlage spart spürbar.",[14,185,187],{"id":186},"aufräumen-ist-teil-der-arbeit","Aufräumen ist Teil der Arbeit",[10,189,190,191,194],{},"Was viele unterschätzen: Three.js räumt nicht von allein auf. Geometrien, Materialien und Texturen leben im GPU-Speicher weiter, bis ich sie explizit mit ",[31,192,193],{},".dispose()"," entlasse. Bei Single-Page-Apps, die Szenen wechseln, habe ich so schon Speicherlecks gesehen, die nach Minuten zum Absturz führten. Seitdem ist Aufräumen für mich kein nachträglicher Gedanke, sondern Teil jeder Komponente.",[10,196,197,198,201],{},"Für mobile Geräte gehe ich noch einen Schritt weiter. Ich begrenze die ",[31,199,200],{},"pixelRatio"," auf maximal 2, denn ein Retina-Display mit Faktor 3 zu bedienen kostet enorm viele Pixel. Ich erkenne schwächere Geräte und reduziere dort Schatten, Postprocessing und Polygonzahl. Eine Szene muss nicht überall gleich aussehen — sie muss überall flüssig laufen.",[10,203,204],{},"Am Ende geht es nicht um die maximale Szene, die irgendwo läuft. Es geht um die richtige Szene, die überall läuft.",[206,207,208],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":41,"searchDepth":84,"depth":84,"links":210},[211,212],{"id":16,"depth":84,"text":17},{"id":186,"depth":84,"text":187},null,"2023-05-18","Aus Jahren mit Three.js habe ich gelernt, dass flüssige 3D-Szenen keine Frage roher Leistung sind, sondern von Draw Calls, Instancing und sauberem Aufräumen.",false,"md","de",{},true,"\u002Fblog\u002Fde\u002Fthreejs-performance",{"title":5,"description":215},"blog\u002Fde\u002Fthreejs-performance",[225,226,227],"Three.js","Performance","3D","3JDGwtEGOSgyyUHe72m8y9RaJ6OnUktD-n-l1LHjgrY",1781596071532]