关于three.js在使用transformControl后的卡顿问题
关于three.js在使用transformControl后的卡顿问题
问题描述
- 变换控制器(
O
r
b
i
t
C
o
n
t
r
o
l
s
OrbitControls
OrbitControls)与轨道控制器(
T
r
a
n
s
f
o
r
m
C
o
n
t
r
o
l
s
TransformControls
TransformControls)之争
为什么在执行完transformControl.attach(object);
后,
轨道控制器
就变的卡顿,即使我用transformControl.detach();
之后,
还是卡顿异常
问题分析
关于这个 three.js 中遇到的问题,即在使用 TransformControl.attach(object) 之后 OrbitControl 变得卡顿,可能是由于几个潜在的原因引起的。考虑到我们已经在 dragging-changed 事件中禁用了 OrbitControl,
即,
transformControl.addEventListener('dragging-changed', function (event) {
controls.enabled = !event.value;
});
,这个问题可能不完全是由事件处理冲突导致的。以下是一些可能导致这种行为的原因及解决方案:
- 性能问题
- 复杂的场景渲染:如果您的场景非常复杂(许多物体或高多边形计数),那么在进行交互操作(如变换控制)时,性能可能会受到影响。
- 解决方案:优化场景,减少渲染负担,例如通过减少场景中物体的数量或细节。
- 过度渲染(经过九牛二虎之力,终于找到了问题的关键,就是过度渲染!!!)
- 频繁更新:render() 函数可能被频繁调用,尤其是在您的 transformControl 的 ‘change’ 事件监听器中。
- 解决方案:确保 render() 函数仅在必要时调用,或使用 requestAnimationFrame 来管理渲染循环。
- 资源未正确释放
- 资源管理:在使用 transformControl.detach() 后,某些资源可能没有被正确释放或重置。
- 解决方案:检查是否有任何未处理的资源或状态需要在分离操作后清理或重置。
- 内存泄漏
- 事件监听器:如果在使用过程中不断添加事件监听器而没有适当移除,可能会导致内存泄漏。
- 解决方案:确保添加的事件监听器在不需要时被移除,避免内存泄漏。
- GPU/CPU 负载
- 硬件限制:如果您的设备的 GPU 或 CPU 资源受限,可能会影响渲染性能。
- 解决方案:在不同的硬件上测试您的应用,查看问题是否依旧存在,以确认是否为硬件限制问题。
- 浏览器或框架兼容性问题
- 兼容性:可能存在浏览器或 three.js 版本的兼容性问题。
- 解决方案:尝试在不同的浏览器中运行您的应用,检查 three.js 是否是最新版本,或者是否有已知的兼容性问题。
找到问题,分析为什么会造成这个问题?
卡顿的代码是这样的:
let transformControl;//变换控制器
//创建一个 TransformControls 实例(将相机 camera 和渲染器的 DOM 元素 renderer.domElement 作为参数传递进去)
transformControl = new TransformControls(camera, renderer.domElement);
//接下来,我们添加了两个事件监听器
//1.当 TransformControls 的变换发生变化时,即用户交互操作导致物体进行平移、旋转或缩放时,触发 animate 函数重新渲染场景。
//即,模型改变=>重新渲染
transformControl.addEventListener('change', render);
//2.当用户开始或停止拖拽物体时,通过 event.value 判断拖拽状态,
//如果拖拽开始(event.value 为 true),则禁用其他的控制器(如鼠标控制器 controls),
//以防止在拖拽过程中出现与 TransformControls 的冲突。
transformControl.addEventListener('dragging-changed', function (event) {
controls.enabled = !event.value;
});
//添加到场景中
scene.add(transformControl);
在three.js中,remove
,detach
等类似的这些函数,仅仅只是在表面上移除可模型,但是仍旧存在于内存中.只是你在场景中看不到了而已.
也就是说,当你用detach移除变换控制器后,变换控制器只是你看不到了也点不到了,但是他依旧存在于整个内存中,其中有一个语句
transformControl.addEventListener('change', render);
,他是在变换控制器附着的模型有平移、旋转或缩放操作时,会被调用,所以当你取消对模型的附着,也就是调用transformControl.detach();后,表面上变换控制器没有附着在模型上了,但是实际上还在他在幕后监听着一些东西!
当你用detach()使得变换控制器消失后,它依旧监视着这个牙齿,所以之后你再用轨道控制器对整个场景进行平移、旋转或缩放操作时,这个颗之前被transformControl
附着的牙齿也属于场景中的一分子,所以,transformControl.addEventListener('change', render);
还是会被触发.so,频繁的调用render是场景卡顿的罪魁祸首!!!
问题解决
其实把transformControl.addEventListener('change', render);
这一行注释掉就好,防止重复渲染整个场景
/**********
* 转换控制器
*/
const pointer = new THREE.Vector2();//客户端坐标转化为设备坐标
// const onDownPosition = new THREE.Vector2();//鼠标按下的客户端坐标
// const onUpPosition = new THREE.Vector2();//鼠标抬起的客户端坐标
let transformControl;//变换控制器
//创建一个 TransformControls 实例(将相机 camera 和渲染器的 DOM 元素 renderer.domElement 作为参数传递进去)
transformControl = new TransformControls(camera, renderer.domElement);
//接下来,我们添加了两个事件监听器
//1.当 TransformControls 的变换发生变化时,即用户交互操作导致物体进行平移、旋转或缩放时,触发 animate 函数重新渲染场景。
//即,模型改变=>重新渲染
//transformControl.addEventListener('change', render);
//2.当用户开始或停止拖拽物体时,通过 event.value 判断拖拽状态,
//如果拖拽开始(event.value 为 true),则禁用其他的控制器(如鼠标控制器 controls),
//以防止在拖拽过程中出现与 TransformControls 的冲突。
transformControl.addEventListener('dragging-changed', function (event) {
controls.enabled = !event.value;
});
//添加到场景中
scene.add(transformControl);
// document.addEventListener('pointerdown', onPointerDown);
// document.addEventListener('pointerup', onPointerUp);
// document.addEventListener('pointermove', onPointerMove);
document.addEventListener('click', onClick);
document.addEventListener("keydown", handleKeyDown);
function onClick(event) {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
intersects = raycaster.intersectObjects(objects);
if (intersects.length > 0) {
var object = intersects[0].object;
if (object !== transformControl.object) {
transformControl.detach();
transformControl.attach(object);
transformControl.update();
//controls.enabled = false;
}
}
}
function handleKeyDown(event) {
if (event.key === 'q') {
if (transformControl.object) {
transformControl.detach();
transformControl.update();
//controls.enabled = true;
//render();
}
}
}