You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

492 lines
31 KiB

<!DOCTYPE html><html lang="ru"><head>
<meta charset="utf-8">
<title>Оптимизация большого количества объектов</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@threejs">
<meta name="twitter:title" content="Three.js – Оптимизация большого количества объектов">
<meta property="og:image" content="https://threejs.org/files/share.png">
<link rel="shortcut icon" href="/files/favicon_white.ico" media="(prefers-color-scheme: dark)">
<link rel="shortcut icon" href="/files/favicon.ico" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="/manual/resources/lesson.css">
<link rel="stylesheet" href="/manual/resources/lang.css">
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "../../build/three.module.js"
}
}
</script>
</head>
<body>
<div class="container">
<div class="lesson-title">
<h1>Оптимизация большого количества объектов</h1>
</div>
<div class="lesson">
<div class="lesson-main">
<p>Эта статья является частью серии статей о three.js. Первая статья - <a href="fundamentals.html">основы Three.js</a>.
Если вы еще не читали это, и вы новичок в three.js, вы можете начать там. </p>
<p>Есть много способов оптимизировать вещи для three.js. Один из способов часто называют геометрией слияния.
Каждая созданная вами сетка и three.js представляют 1 или более запросов системы на что-то нарисовать.
Рисование 2 вещей имеет больше затрат, чем рисование 1,
даже если результаты одинаковы, поэтому одним из способов оптимизации является объединение сеток. </p>
<p>Давайте посмотрим пример, когда это хорошее решение для проблемы. Давайте заново создадим <a href="https://globe.chromeexperiments.com/">WebGL Globe</a>.</p>
<p>Первое, что нам нужно сделать, это получить данные. WebGL Globe сказал, что данные, которые они используют, взяты из <a href="http://sedac.ciesin.columbia.edu/gpw/">SEDAC</a>.
Проверяя сайт, я увидел <a href="https://beta.sedac.ciesin.columbia.edu/data/set/gpw-v4-basic-demographic-characteristics-rev10">демографические данные в виде сетки</a>.
Я загрузил данные с 60-минутным разрешением. Затем я посмотрел на данные </p>
<p>Это выглядит так </p>
<pre class="prettyprint showlinemods notranslate lang-txt" translate="no"> ncols 360
nrows 145
xllcorner -180
yllcorner -60
cellsize 0.99999999999994
NODATA_value -9999
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
9.241768 8.790958 2.095345 -9999 0.05114867 -9999 -9999 -9999 -9999 -999...
1.287993 0.4395509 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999...
-9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 -9999 ...
</pre>
<p>Есть несколько строк, похожих на пары ключ / значение, за которыми следуют строки со значением для каждой точки сетки, по одной строке для каждой строки точек данных. </p>
<p>Чтобы убедиться, что мы понимаем данные, давайте попробуем построить их в 2D. </p>
<p>Сначала немного кода для загрузки текстового файла </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">async function loadFile(url) {
const res = await fetch(url);
return res.text();
}
</pre>
<p>Приведенный выше код возвращает <code class="notranslate" translate="no">Promise</code> с содержимым файла по адресу <code class="notranslate" translate="no">url</code>; </p>
<p>Тогда нам нужен код для разбора файла </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function parseData(text) {
const data = [];
const settings = {data};
let max;
let min;
// split into lines
text.split('\n').forEach((line) =&gt; {
// split the line by whitespace
const parts = line.trim().split(/\s+/);
if (parts.length === 2) {
// only 2 parts, must be a key/value pair
settings[parts[0]] = parseFloat(parts[1]);
} else if (parts.length &gt; 2) {
// more than 2 parts, must be data
const values = parts.map((v) =&gt; {
const value = parseFloat(v);
if (value === settings.NODATA_value) {
return undefined;
}
max = Math.max(max === undefined ? value : max, value);
min = Math.min(min === undefined ? value : min, value);
return value;
});
data.push(values);
}
});
return Object.assign(settings, {min, max});
}
</pre>
<p>Приведенный выше код возвращает объект со всеми парами ключ / значение из файла,
а также свойство <code class="notranslate" translate="no">data</code> со всеми данными в одном большом массиве и значениями <code class="notranslate" translate="no">min</code> и <code class="notranslate" translate="no">max</code>, найденными в данных. </p>
<p>Тогда нам нужен код для рисования этих данных </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function drawData(file) {
const {min, max, data} = file;
const range = max - min;
const ctx = document.querySelector('canvas').getContext('2d');
// make the canvas the same size as the data
ctx.canvas.width = ncols;
ctx.canvas.height = nrows;
// but display it double size so it's not too small
ctx.canvas.style.width = px(ncols * 2);
ctx.canvas.style.height = px(nrows * 2);
// fill the canvas to dark gray
ctx.fillStyle = '#444';
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// draw each data point
data.forEach((row, latNdx) =&gt; {
row.forEach((value, lonNdx) =&gt; {
if (value === undefined) {
return;
}
const amount = (value - min) / range;
const hue = 1;
const saturation = 1;
const lightness = amount;
ctx.fillStyle = hsl(hue, saturation, lightness);
ctx.fillRect(lonNdx, latNdx, 1, 1);
});
});
}
function px(v) {
return `${v | 0}px`;
}
function hsl(h, s, l) {
return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
</pre>
<p>И, наконец, склеив все это </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')
.then(parseData)
.then(drawData);
</pre>
<p>Дает нам этот результат </p>
<p></p><div translate="no" class="threejs_example_container notranslate">
<div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/gpw-data-viewer.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/gpw-data-viewer.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
</div>
<p></p>
<p>Так что, кажется, это работает. </p>
<p>Давайте попробуем это в 3D. Начиная с кода от <a href="rendering-on-demand.html">рендеринга по требованию</a> мы сделаем один блок на данные в файле. </p>
<p>Сначала давайте сделаем простую сферу с текстурой мира. Вот текстура </p>
<div class="threejs_center"><img src="../examples/resources/images/world.jpg" style="width: 600px"></div>
<p>И код для его настройки. </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
const loader = new THREE.TextureLoader();
const texture = loader.load('resources/images/world.jpg', render);
const geometry = new THREE.SphereGeometry(1, 64, 32);
const material = new THREE.MeshBasicMaterial({map: texture});
scene.add(new THREE.Mesh(geometry, material));
}
</pre>
<p>Обратите внимание на вызов для рендеринга после завершения загрузки текстуры. Нам это нужно, потому что мы выполняем <a href="rendering-on-demand.html">рендеринг по требованию</a>
а не постоянно, поэтому нам нужно рендерить один раз при загрузке текстуры. </p>
<p>Затем нам нужно изменить код, который рисует точку на точку данных выше, чтобы вместо этого создать прямоугольник для точки данных. </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function addBoxes(file) {
const {min, max, data} = file;
const range = max - min;
// make one box geometry
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
// make it so it scales away from the positive Z axis
geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5));
// these helpers will make it easy to position the boxes
// We can rotate the lon helper on its Y axis to the longitude
const lonHelper = new THREE.Object3D();
scene.add(lonHelper);
// We rotate the latHelper on its X axis to the latitude
const latHelper = new THREE.Object3D();
lonHelper.add(latHelper);
// The position helper moves the object to the edge of the sphere
const positionHelper = new THREE.Object3D();
positionHelper.position.z = 1;
latHelper.add(positionHelper);
const lonFudge = Math.PI * .5;
const latFudge = Math.PI * -0.135;
data.forEach((row, latNdx) =&gt; {
row.forEach((value, lonNdx) =&gt; {
if (value === undefined) {
return;
}
const amount = (value - min) / range;
const material = new THREE.MeshBasicMaterial();
const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
const saturation = 1;
const lightness = THREE.MathUtils.lerp(0.1, 1.0, amount);
material.color.setHSL(hue, saturation, lightness);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// adjust the helpers to point to the latitude and longitude
lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
// use the world matrix of the position helper to
// position this mesh.
positionHelper.updateWorldMatrix(true, false);
mesh.applyMatrix4(positionHelper.matrixWorld);
mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
});
});
}
</pre>
<p>Код в основном прямо из нашего тестового кода рисования. </p>
<p>Мы делаем одну коробку и корректируем ее центр так, чтобы она масштабировалась от положительного Z.
Если бы мы этого не делали, она бы масштабировалась от центра, но мы хотим, чтобы они росли от начала координат. </p>
<div class="spread">
<div>
<div data-diagram="scaleCenter" style="height: 250px"></div>
<div class="code">default</div>
</div>
<div>
<div data-diagram="scalePositiveZ" style="height: 250px"></div>
<div class="code">adjusted</div>
</div>
</div>
<p>Конечно, мы могли бы также решить эту проблему, добавив в родительский блок больше объектов
THREE.Object3D, как мы покрывали в <a href="scenegraph.html">графах сцены</a>
но чем больше узлов мы добавляем в граф сцены, тем медленнее он становится. </p>
<p>Мы также настраиваем эту небольшую иерархию узлов <code class="notranslate" translate="no">lonHelper</code>, <code class="notranslate" translate="no">latHelper</code> и <code class="notranslate" translate="no">positionHelper</code>.
Мы используем эти объекты, чтобы вычислить положение вокруг сферы, чтобы разместить коробку. </p>
<div class="spread">
<div data-diagram="lonLatPos" style="width: 600px; height: 400px;"></div>
</div>
<p>Над <span style="color: green;">зелёной полосой</span> изображен <code class="notranslate" translate="no">lonHelper</code> и используется для поворота в направлении долготы на экваторе.
<span style="color: blue;">Синяя полоса</span> обозначает <code class="notranslate" translate="no">latHelper</code> который используется для поворота на широту выше или ниже экватора.
<span style="color: red;">Красная сфера</span> представляет смещение, которое обеспечивает этот <code class="notranslate" translate="no">positionHelper</code>.</p>
<p>Мы могли бы сделать всю математику вручную, чтобы выяснить позиции на земном шаре, но, делая это таким образом,
мы оставляем большую часть математики самой библиотеке, поэтому нам не нужно иметь с ней дело. </p>
<p>Для каждой точки данных мы создаем <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> и <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>, а затем запрашиваем мировую матрицу
<code class="notranslate" translate="no">positionHelper</code> и применяем ее к новой <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>. Наконец мы масштабируем сетку в новой позиции. </p>
<p>Как и выше, мы могли бы также создать <code class="notranslate" translate="no">latHelper</code>, <code class="notranslate" translate="no">lonHelper</code> и <code class="notranslate" translate="no">positionHelper</code> для каждого нового окна, но это было бы еще медленнее. </p>
<p>Мы собираемся создать до 360х145 коробок. Это до 52000 коробок.
Поскольку некоторые точки данных помечены как «NO_DATA», фактическое
количество блоков, которые мы собираемся создать, составляет около 19000.
Если бы мы добавили 3 дополнительных вспомогательных объекта в блок,
это было бы почти 80000 узлов графа сцены, которые THREE.js должен был бы
вычислить. позиции для. Вместо этого, используя один набор помощников,
чтобы просто расположить сетки, мы экономим около 60000 операций. </p>
<p>Примечание о <code class="notranslate" translate="no">lonFudge</code> и <code class="notranslate" translate="no">latFudge</code>. <code class="notranslate" translate="no">lonFudge</code> равен π / 2, что составляет четверть оборота.
В этом есть смысл. Это просто означает, что текстура или координаты текстуры начинаются
с другого смещения по всему земному шару. <code class="notranslate" translate="no">latFudge</code> с другой стороны, я понятия не имею,
почему он должен быть π * -0,135, это просто количество, которое выровняло боксы с текстурой. </p>
<p>Последнее, что нам нужно сделать, это вызвать нашего загрузчика </p>
<pre class="prettyprint showlinemods notranslate notranslate" translate="no">loadFile('resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')
.then(parseData)
- .then(drawData)
+ .then(addBoxes)
+ .then(render);
</pre><p>После того, как данные закончили загрузку и анализ, нам нужно выполнить рендеринг хотя бы один раз, поскольку мы выполняем
<a href="rendering-on-demand.html">рендеринг по требованию</a>.</p>
<p></p><div translate="no" class="threejs_example_container notranslate">
<div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lots-of-objects-slow.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/lots-of-objects-slow.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
</div>
<p></p>
<p>Если вы попытаетесь повернуть приведенный выше пример, перетащив образец, вы, вероятно, заметите, что он медленный. </p>
<p>Мы можем проверить частоту кадров, <a href="debugging-javascript.html">открыв devtools</a> и включив индикатор частоты кадров браузера.</p>
<div class="threejs_center"><img src="../resources/images/bring-up-fps-meter.gif"></div>
<p>На моей машине я вижу частоту кадров ниже 20 кадров в секунду. </p>
<div class="threejs_center"><img src="../resources/images/fps-meter.gif"></div>
<p>Это не очень хорошо для меня, и я подозреваю, что многие люди имеют более медленные машины, которые сделали бы это еще хуже. Нам лучше взглянуть на оптимизацию. </p>
<p>Для этой конкретной задачи мы можем объединить все блоки в одну геометрию.
В настоящее время мы рисуем около 19000 коробок. Объединяя их в одну геометрию, мы удалили 18999 операций. </p>
<p>Вот новый код для объединения блоков в одну геометрию. </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">function addBoxes(file) {
const {min, max, data} = file;
const range = max - min;
- // make one box geometry
- const boxWidth = 1;
- const boxHeight = 1;
- const boxDepth = 1;
- const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
- // make it so it scales away from the positive Z axis
- geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5));
// these helpers will make it easy to position the boxes
// We can rotate the lon helper on its Y axis to the longitude
const lonHelper = new THREE.Object3D();
scene.add(lonHelper);
// We rotate the latHelper on its X axis to the latitude
const latHelper = new THREE.Object3D();
lonHelper.add(latHelper);
// The position helper moves the object to the edge of the sphere
const positionHelper = new THREE.Object3D();
positionHelper.position.z = 1;
latHelper.add(positionHelper);
+ // Used to move the center of the box so it scales from the position Z axis
+ const originHelper = new THREE.Object3D();
+ originHelper.position.z = 0.5;
+ positionHelper.add(originHelper);
const lonFudge = Math.PI * .5;
const latFudge = Math.PI * -0.135;
+ const geometries = [];
data.forEach((row, latNdx) =&gt; {
row.forEach((value, lonNdx) =&gt; {
if (value === undefined) {
return;
}
const amount = (value - min) / range;
- const material = new THREE.MeshBasicMaterial();
- const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
- const saturation = 1;
- const lightness = THREE.MathUtils.lerp(0.1, 1.0, amount);
- material.color.setHSL(hue, saturation, lightness);
- const mesh = new THREE.Mesh(geometry, material);
- scene.add(mesh);
+ const boxWidth = 1;
+ const boxHeight = 1;
+ const boxDepth = 1;
+ const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
// adjust the helpers to point to the latitude and longitude
lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
- // use the world matrix of the position helper to
- // position this mesh.
- positionHelper.updateWorldMatrix(true, false);
- mesh.applyMatrix4(positionHelper.matrixWorld);
-
- mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
+ // use the world matrix of the origin helper to
+ // position this geometry
+ positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
+ originHelper.updateWorldMatrix(true, false);
+ geometry.applyMatrix4(originHelper.matrixWorld);
+
+ geometries.push(geometry);
});
});
+ const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(
+ geometries, false);
+ const material = new THREE.MeshBasicMaterial({color:'red'});
+ const mesh = new THREE.Mesh(mergedGeometry, material);
+ scene.add(mesh);
}
</pre>
<p>Выше мы удалили код, который изменял центральную точку геометрии бокса,
и вместо этого делаем это, добавляя <code class="notranslate" translate="no">originHelper</code>. Раньше мы использовали
одну и ту же геометрию 19000 раз. На этот раз мы создаем новую геометрию
для каждого отдельного блока, и, поскольку мы будем использовать <code class="notranslate" translate="no">applyMatrix</code>
для перемещения вершин каждой геометрии блока, мы могли бы сделать это один раз, а не два. </p>
<p>В конце мы передаем массив всех геометрий в <code class="notranslate" translate="no">BufferGeometryUtils.mergeBufferGeometries</code>, который объединит их все в одну сетку. </p>
<p>Нам также нужно включить <code class="notranslate" translate="no">BufferGeometryUtils</code> </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
</pre>
<p>И теперь, по крайней мере на моей машине, я получаю 60 кадров в секунду </p>
<p></p><div translate="no" class="threejs_example_container notranslate">
<div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lots-of-objects-merged.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/lots-of-objects-merged.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
</div>
<p></p>
<p>Так что это сработало, но поскольку это одна сетка, мы получаем только один материал,
что означает, что мы получаем только один цвет, тогда как раньше у нас был другой цвет
на каждой коробке. Мы можем исправить это, используя цвета вершин. </p>
<p>Цвета вершин добавляют цвет для каждой вершины. Установив все цвета каждой
вершины каждого блока на определенные цвета, каждый блок будет иметь другой цвет. </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const color = new THREE.Color();
const lonFudge = Math.PI * .5;
const latFudge = Math.PI * -0.135;
const geometries = [];
data.forEach((row, latNdx) =&gt; {
row.forEach((value, lonNdx) =&gt; {
if (value === undefined) {
return;
}
const amount = (value - min) / range;
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
// adjust the helpers to point to the latitude and longitude
lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;
// use the world matrix of the origin helper to
// position this geometry
positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
originHelper.updateWorldMatrix(true, false);
geometry.applyMatrix4(originHelper.matrixWorld);
+ // compute a color
+ const hue = THREE.MathUtils.lerp(0.7, 0.3, amount);
+ const saturation = 1;
+ const lightness = THREE.MathUtils.lerp(0.4, 1.0, amount);
+ color.setHSL(hue, saturation, lightness);
+ // get the colors as an array of values from 0 to 255
+ const rgb = color.toArray().map(v =&gt; v * 255);
+
+ // make an array to store colors for each vertex
+ const numVerts = geometry.getAttribute('position').count;
+ const itemSize = 3; // r, g, b
+ const colors = new Uint8Array(itemSize * numVerts);
+
+ // copy the color into the colors array for each vertex
+ colors.forEach((v, ndx) =&gt; {
+ colors[ndx] = rgb[ndx % 3];
+ });
+
+ const normalized = true;
+ const colorAttrib = new THREE.BufferAttribute(colors, itemSize, normalized);
+ geometry.setAttribute('color', colorAttrib);
geometries.push(geometry);
});
});
</pre>
<p>Приведенный выше код ищет количество или вершины,
необходимые для получения атрибута <code class="notranslate" translate="no">position</code> из геометрии.
Затем мы создаем <code class="notranslate" translate="no">Uint8Array</code> для размещения цветов.
Затем он добавляет это как атрибут, вызывая <code class="notranslate" translate="no">geometry.setAttribute</code>. </p>
<p>Наконец, нам нужно указать three.js использовать цвета вершин. </p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(
geometries, false);
-const material = new THREE.MeshBasicMaterial({color:'red'});
+const material = new THREE.MeshBasicMaterial({
+ vertexColors: true,
+});
const mesh = new THREE.Mesh(mergedGeometry, material);
scene.add(mesh);
</pre>
<p>И с этим мы получаем наши цвета обратно </p>
<p></p><div translate="no" class="threejs_example_container notranslate">
<div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/lots-of-objects-merged-vertexcolors.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/lots-of-objects-merged-vertexcolors.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
</div>
<p></p>
<p>Объединение геометрии является распространенным методом оптимизации.
Например, вместо 100 деревьев вы можете объединить деревья в одну геометрию,
кучу отдельных камней в одну геометрию камней, частокол из отдельных пикетов в одну сетку.
Другой пример в Minecraft - он не рисует каждый куб по отдельности, а создает
группы объединенных кубов, а также выборочно удаляет грани, которые никогда не видны. </p>
<p>Проблема создания всего одного меша состоит в том, что больше не легко перемещать какие-либо части, которые были ранее разделены.
В зависимости от нашего варианта использования, хотя есть творческие решения. Мы рассмотрим одну в
<a href="optimize-lots-of-objects-animated.html">другой статье</a>.</p>
<p><canvas id="c"></canvas></p>
<script type="module" src="../resources/threejs-lots-of-objects.js"></script>
</div>
</div>
</div>
<script src="/manual/resources/prettify.js"></script>
<script src="/manual/resources/lesson.js"></script>
</body></html>