Я пытаюсь создать текстовые метки. Мне нужно использовать шейдерный материал, чтобы лучше контролировать метку во время рендеринга.
Я заметил, что память продолжает расти, хотя я очищаю старые ярлыки.
Я создал пример jsfiddle, который не похож: https://threejs.org/examples/#webgl_test_memory
Следующий код использует объект canvas для создания текстуры, которая содержит текст, который будет отображаться как метка:
Пожалуйста, будьте осторожны, эти вычисления тяжелы и делают вкладку совершенно невосприимчивой.
var container;
var camera, scene, renderer;
var labels;
var canvas;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 200;
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
labels = new THREE.Object3D();
canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// get text metrics
var fontface = 'Arial';
var fontSize = 60;
context.font = fontSize + "px " + fontface;
var width = context.measureText(text).width;
// add text
var text = 'abcdef';
canvas.width = width;
canvas.height = fontSize*1.3;
context.textAlign = "center";
context.font = fontSize + "px " + fontface;
context.fillStyle = "white";
context.fillText(text, canvas.width / 2, canvas.height / 2);
}
function createLabels() {
for(var i = 0; i < 10000 ; i++) {
createTextMesh();
}
scene.add( labels );
}
function createTextMesh() {
// canvas contents will be used for a texture
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var uniforms = {
text: {
type: 't',
value: texture
}
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertex-shader' ).textContent,
fragmentShader: document.getElementById( 'fragment-shader' ).textContent
} );
var geometry = new THREE.PlaneBufferGeometry(15, 15);
var label = new THREE.Mesh( geometry, material );
labels.add(label);
}
function clearLabels() {
for(var i = 0; i < labels.children.length; i++) {
var label = labels.children[i];
if(label.material.uniforms) {
label.material.uniforms.text.value.dispose();
}
label.material.dispose();
label.geometry.dispose();
labels.remove(label);
}
scene.remove( labels );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
// build GL objects
createLabels();
renderer.render( scene, camera );
// clean up
clearLabels();
}
body {
margin:0;
}
<script src="/three.min.js"></script>
<script id="fragment-shader" type="x-shader/x-fragment">
uniform sampler2D text;
varying vec2 vUv;
void main() {
vec4 finalColor = texture2D(text, vUv);
gl_FragColor = finalColor;
}
</script>
<script id="vertex-shader" type="x-shader/x-fragment">
varying vec2 vUv;
void main() {
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<canvas></canvas>
Вы можете использовать инструменты chromes dev, чтобы оценить увеличение использования перцептивной памяти.
Я бы рекомендовал использовать что-то вроде собственного диспетчера задач Window, чтобы увидеть увеличение памяти.
Вы можете уменьшить скорость создания метки, хотя это, естественно, означает, что на вкладке закончится нехватка памяти.
Я неправильно делаю очистку ресурсов?
ура
Не создавайте новую сетку 1000 раз каждый тик, объедините их. Не создавайте 1000 геометрий, которые являются одной и той же плоскостью. Вероятно, это самый крупный преступник. Создайте только один, когда-либо, и передайте его в сетки. Текстуры, я не так уверен в этом. Я думаю, вы не должны создавать новую текстуру из контекста canvas, создавать один раз и обновлять холст.
Это исчезнет с изменениями, но хорошо заметить, что для производительности вы хотите избежать создания этого объекта формы в цикле.
РЕДАКТИРОВАТЬ
Вы создаете много вещей. Ширина метки равна 263, и если нужно хранить три консольных журнала, они изменяются до 256x64. Таким образом, вы получаете 480 МБ данных, хотя, с альфа-каналом, это может быть 600 МБ. Я предполагаю, что ваша демо никогда не попадает в распоряжение. Он просто разбил браузер на моем конце.
Три также жалуются на то, что текстуры являются NPOT, поэтому он пытается выписать десять тысяч консольных журналов.
Привет, попробуйте следующий код, поскольку я добавил временной промежуток в функцию анимации, которая вызывает функцию рендеринга.
есть одна переменная fps в настоящее время 24, вы можете ее изменить.
<!DOCTYPE html>
<html lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>three.js - shader material memory leak</title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
background:#fff;
padding:0;
margin:0;
overflow:hidden;
}
</style>
</head>
<body>
<script src="/three.js"></script>
<script id="fragment-shader" type="x-shader/x-fragment">
uniform sampler2D text;
varying vec2 vUv;
void main() {
vec4 finalColor = texture2D(text, vUv);
gl_FragColor = finalColor;
}
</script>
<script id="vertex-shader" type="x-shader/x-fragment">
varying vec2 vUv;
void main() {
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script>
var container,
camera, scene, renderer,
labels,
canvas,
lastTime, fps = 24;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 200;
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
labels = new THREE.Object3D();
canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// get text metrics
var fontface = 'Arial';
var fontSize = 60;
context.font = fontSize + "px " + fontface;
var width = context.measureText(text).width;
// add text
var text = 'abcdef';
canvas.width = width;
canvas.height = fontSize*1.3;
context.textAlign = "center";
context.font = fontSize + "px " + fontface;
context.fillStyle = "white";
context.fillText(text, canvas.width / 2, canvas.height / 2);
}
function createLabels() {
for(var i = 0; i < 10000 ; i++) {
createTextMesh();
}
scene.add( labels );
}
function createTextMesh() {
// canvas contents will be used for a texture
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var uniforms = {
text: {
type: 't',
value: texture
}
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertex-shader' ).textContent,
fragmentShader: document.getElementById( 'fragment-shader' ).textContent
} );
var geometry = new THREE.PlaneBufferGeometry(15, 15);
var label = new THREE.Mesh( geometry, material );
labels.add(label);
}
function clearLabels() {
for(var i = 0; i < labels.children.length; i++) {
var label = labels.children[i];
if(label.material.uniforms) {
label.material.uniforms.text.value.dispose();
}
label.material.dispose();
label.geometry.dispose();
labels.remove(label);
}
scene.remove( labels );
}
// Add time gap render will call with 24 fps. or you can slow down speed to check.
function animate() {
var curTime = new Date();
if(lastTime === undefined || (Math.round(curTime - lastTime)/1000) >= fps)
{
render();
lastTime = curTime;
}
requestAnimationFrame( animate );
}
function render() {
// build GL objects
createLabels();
renderer.render( scene, camera );
// clean up
clearLabels();
}
</script>
<div>
<canvas width="1920" height="974" style="width: 1920px; height: 974px;"></canvas>
</div>
</body>
</html>