简体   繁体   English

Three.js:设置`texture.needsUpdate = true`非常慢

[英]Three.js: Setting `texture.needsUpdate = true` is very slow

I'm working on a Three.js scene in which I'd like to update some textures after some time. 我正在开发一个Three.js场景,我想在一段时间后更新一些纹理。 I'm finding that updating the textures is very slow, however, and drags FPS to only 1-2 FPS for several seconds (when updating just a single texture). 我发现更新纹理的速度非常慢,并且将FPS拖动到1-2 FPS几秒钟(仅更新一个纹理时)。

Is there anything one can do to expedite texture updates? 有没有什么可以加快纹理更新? Any insights others can offer on this question would be very appreciated. 任何其他人可以提出的关于这个问题的见解将非常感激。

To see this behavior, click the window of the example below. 要查看此行为,请单击下面示例的窗口。 This will load the first texture update (another click will trigger the second texture update). 这将加载第一个纹理更新(另一次单击将触发第二个纹理更新)。 If you try to zoom after one of these clicks, you'll find the screen freezes and the FPS will drop terribly. 如果您尝试在其中一次点击后进行缩放,您会发现屏幕冻结,FPS会下降得非常严重。 Does anyone know how to fix this problem? 有谁知道如何解决这个问题?

 <html> <head> <style> html, body { width: 100%; height: 100%; background: #000; } body { margin: 0; overflow: hidden; } canvas { width: 100%; height: 100%; } </style> </head> <body> <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/88/three.min.js'></script> <script src='https://rawgit.com/YaleDHLab/pix-plot/master/assets/js/trackball-controls.js'></script> <script src='https://rawgit.com/mrdoob/stats.js/master/build/stats.min.js'></script> <script type='x-shader/x-vertex' id='vertex-shader'> precision highp float; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform vec3 cameraPosition; attribute vec3 position; // sets the blueprint's vertex positions attribute vec3 translation; // xy translation offsets for an instance attribute float texIdx; // the texture index to access varying float vTexIdx; void main() { // set point position vec3 pos = position + translation; vec4 projected = projectionMatrix * modelViewMatrix * vec4(pos, 1.0); gl_Position = projected; // assign the varyings vTexIdx = texIdx; // use the delta between the point position and camera position to size point float xDelta = pow(projected[0] - cameraPosition[0], 2.0); float yDelta = pow(projected[1] - cameraPosition[1], 2.0); float zDelta = pow(projected[2] - cameraPosition[2], 2.0); float delta = pow(xDelta + yDelta + zDelta, 0.5); gl_PointSize = 40000.0 / delta; } </script> <script type='x-shader/x-fragment' id='fragment-shader'> precision highp float; uniform sampler2D a; uniform sampler2D b; varying float vTexIdx; void main() { int textureIndex = int(vTexIdx); vec2 uv = vec2(gl_PointCoord.x, gl_PointCoord.y); if (textureIndex == 0) { gl_FragColor = texture2D(a, uv); } else if (textureIndex == 1) { gl_FragColor = texture2D(b, uv); } } </script> <script> /** * Generate a scene object with a background color **/ function getScene() { var scene = new THREE.Scene(); scene.background = new THREE.Color(0xaaaaaa); return scene; } /** * Generate the camera to be used in the scene **/ function getCamera() { var aspectRatio = window.innerWidth / window.innerHeight; var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 100000); camera.position.set(0, 1, -6000); return camera; } /** * Generate the renderer to be used in the scene **/ function getRenderer() { // Create the canvas with a renderer var renderer = new THREE.WebGLRenderer({antialias: true}); // Add support for retina displays renderer.setPixelRatio(window.devicePixelRatio); // Specify the size of the canvas renderer.setSize(window.innerWidth, window.innerHeight); // Add the canvas to the DOM document.body.appendChild(renderer.domElement); return renderer; } /** * Generate the controls to be used in the scene **/ function getControls(camera, renderer) { var controls = new THREE.TrackballControls(camera, renderer.domElement); controls.zoomSpeed = 0.4; controls.panSpeed = 0.4; return controls; } /** * Generate the points for the scene **/ function addPoints(scene) { var BA = THREE.BufferAttribute; var IBA = THREE.InstancedBufferAttribute; var geometry = new THREE.InstancedBufferGeometry(); // add data for each observation var n = 10000; // number of observations var rootN = n**(1/2); var cellSize = 20; var translation = new Float32Array( n * 3 ); var texIdx = new Float32Array( n ); var translationIterator = 0; var texIterator = 0; for (var i=0; i<n*3; i++) { var x = Math.random() * n - (n/2); var y = Math.random() * n - (n/2); translation[translationIterator++] = x; translation[translationIterator++] = y; translation[translationIterator++] = Math.random() * n - (n/2); texIdx[texIterator++] = (x + y) > (n/8) ? 1 : 0; } var positionAttr = new BA(new Float32Array( [0, 0, 0] ), 3); var translationAttr = new IBA(translation, 3, 1); var texIdxAttr = new IBA(texIdx, 1, 1); positionAttr.dynamic = true; translationAttr.dynamic = true; texIdxAttr.dynamic = true; geometry.addAttribute('position', positionAttr); geometry.addAttribute('translation', translationAttr); geometry.addAttribute('texIdx', texIdxAttr); var canvases = [ getElem('canvas', { width: 16384, height: 16384, }), getElem('canvas', { width: 16384, height: 16384, }), ] var textures = [ getTexture( canvases[0] ), getTexture( canvases[1] ), ]; var material = new THREE.RawShaderMaterial({ uniforms: { a: { type: 't', value: textures[0], }, b: { type: 't', value: textures[1], } }, vertexShader: document.getElementById('vertex-shader').textContent, fragmentShader: document.getElementById('fragment-shader').textContent, }); var mesh = new THREE.Points(geometry, material); mesh.frustumCulled = false; // prevent the mesh from being clipped on drag scene.add(mesh); // on the first window click, paint red points // on the second window click, paint blue points var clicks = 0; window.addEventListener('click', function() { if (clicks == 0 || clicks == 1) { var canvas = canvases[clicks]; var ctx = canvas.getContext('2d'); ctx.fillStyle = clicks == 0 ? 'red' : 'blue'; ctx.rect(0, 0, 16384, 16384); ctx.fill(); textures[clicks].needsUpdate = true; clicks++; } }) } function getTexture(canvas) { var tex = new THREE.Texture(canvas); tex.needsUpdate = true; tex.flipY = false; return tex; } /** * Create an element **/ function getElem(tag, obj) { var obj = obj || {}; var elem = document.createElement(tag); Object.keys(obj).forEach(function(attr) { elem[attr] = obj[attr]; }) return elem; } /** * Add stats **/ function getStats() { var stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '65px'; stats.domElement.style.right = '5px'; stats.domElement.style.left = 'initial'; document.body.appendChild(stats.domElement); return stats; } /** * Render! **/ function render() { requestAnimationFrame(render); renderer.render(scene, camera); controls.update(); stats.update(); }; /** * Main **/ var stats = getStats(); var scene = getScene(); var camera = getCamera(); var renderer = getRenderer(); var controls = getControls(camera, renderer); addPoints(scene); render(); </script> </body> </html> 

Your canvases are 16384 by 16384. That's basically insanely large. 你的画布是16384到16384.这基本上是非常大的。

For RGBA format, that is 1073741824 bytes.. a gigabyte of texture data that is getting sent to your GPU from the CPU when you set that texture.needsUpdate = true 对于RGBA格式,即1073741824字节..当您设置纹理时,将从CPU发送到GPU的纹理数据的一千兆字节数.needsUpdate = true

You will definitely notice this getting uploaded to the card. 你肯定会注意到这个上传到卡。

If your use case absolutely requires textures that large.. then you may need to consider doing incremental updates via gl.texSubImage2D, or using a bunch of smaller textures and only updating one of the per frame, or only updating those textures at the start of your app, and not thereafter. 如果你的用例绝对需要大的纹理..那么你可能需要考虑通过gl.texSubImage2D进行增量更新,或者使用一堆较小的纹理,只更新每帧的一个,或者只更新那些纹理的开头你的应用程序,而不是之后。

For reference, there are very few cases i've seen where textures > 4k per side are needed. 作为参考,我见过的情况非常少,每侧需要纹理> 4k。

And that is about 1/16th the size of your textures. 这大约是纹理大小的1/16。

This has nothing to do with three.js btw. 这与three.js btw无关。 It's a fundamental characteristic of GPU/CPU interaction. 这是GPU / CPU交互的基本特征。 Uploads and state changes are slow and have to be carefully orchestrated and monitored. 上传和状态更改很慢,必须仔细协调和监控。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM