Материал шейдера для управления памятью three.js

1

Я пытаюсь создать текстовые метки. Мне нужно использовать шейдерный материал, чтобы лучше контролировать метку во время рендеринга.

Я заметил, что память продолжает расти, хотя я очищаю старые ярлыки.

Я создал пример 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="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/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, чтобы увидеть увеличение памяти.

Вы можете уменьшить скорость создания метки, хотя это, естественно, означает, что на вкладке закончится нехватка памяти.

Я неправильно делаю очистку ресурсов?

ура

Теги:
memory-leaks
three.js
webgl

2 ответа

0

Не создавайте новую сетку 1000 раз каждый тик, объедините их. Не создавайте 1000 геометрий, которые являются одной и той же плоскостью. Вероятно, это самый крупный преступник. Создайте только один, когда-либо, и передайте его в сетки. Текстуры, я не так уверен в этом. Я думаю, вы не должны создавать новую текстуру из контекста canvas, создавать один раз и обновлять холст.

Это исчезнет с изменениями, но хорошо заметить, что для производительности вы хотите избежать создания этого объекта формы в цикле.

РЕДАКТИРОВАТЬ

Вы создаете много вещей. Ширина метки равна 263, и если нужно хранить три консольных журнала, они изменяются до 256x64. Таким образом, вы получаете 480 МБ данных, хотя, с альфа-каналом, это может быть 600 МБ. Я предполагаю, что ваша демо никогда не попадает в распоряжение. Он просто разбил браузер на моем конце.

Три также жалуются на то, что текстуры являются NPOT, поэтому он пытается выписать десять тысяч консольных журналов.

  • 0
    Этот фрагмент кода был разработан, чтобы проверить наличие утечки памяти и сделать это быстро. Это не сценарий реального мира. Он был построен, чтобы имитировать структуру: threejs.org/examples/#webgl_test_memory .
  • 0
    Я вижу, вы пытаетесь провести стресс-тестирование и подтвердить, что вы правильно распределяете ресурсы. Кажется, это правильный способ избавления от вещей.
Показать ещё 2 комментария
0

Привет, попробуйте следующий код, поскольку я добавил временной промежуток в функцию анимации, которая вызывает функцию рендеринга.

есть одна переменная 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="https://threejs.org/build/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>
  • 0
    Ваш код все еще генерирует утечку памяти, и вкладка все еще умирает. Это займет больше времени, чтобы сделать это.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню