提问人:Joseph Astrahan 提问时间:4/5/2023 更新时间:4/5/2023 访问量:195
从类内调用 requestAnimationFrame 时,threejs 动画的动画速度非常慢
Animation extremely slow for threejs animation when calling requestAnimationFrame from within class
问:
描述此问题的最好方法是签出 JSFiddle。当您尝试在鼠标周围移动或执行任何操作时,动画将非常不稳定。我已经将范围缩小到与分类 this.requestAnimationFrame(this.animation) 有关,起初不起作用,但在查看 stackoverflow 后,我发现了一个修复程序,说这让它工作,但速度非常慢!如何在保留课程的同时加快速度?this.animate = this.animate.bind(this);
JSFiddle:
https://jsfiddle.net/gentleman_goat66/o5wn3bpf/100/
这是我的代码如下:
[HTML全文]
<script type="importmap">
{
"imports":
{
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"OrbitControls": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<div id="container">
</div>
JS系列
import * as THREE from 'three';
import { OrbitControls } from 'OrbitControls';
class Heatmap {
constructor(){
console.log("Heatmap - constructor()");
//ThreeJS Variables
this.camera = null;
this.scene = null;
this.renderer = new THREE.WebGLRenderer({antialias: true});
this.orbital_controls = null;
this.ambientLight = null;
this.heatmap = new THREE.Object3D(); //This will hold all the meshes that make up the heatmap
this.animate = this.animate.bind(this);
//Animation Related Variables
this.clock = new THREE.Clock();
this.delta = 0;
this.fps = 60; //60 fps
this.interval = 1;
this.seconds = 2; //seconds for animation
this.timeSoFar = 0;
this.targetTime = 3;
//Setup
this.scene = new THREE.Scene();
this.setUpCamera();
this.setUpRenderer();
this.setUpLighting();
this.setUpOrbitalControls();
//Add optional GridHelper for Debug
this.scene.add(new THREE.GridHelper());
//TODO: Create the heatmap here
this.createDataRectangle();
//Add the finished heatmap to the scene
this.scene.add(this.heatmap);
this.render();
this.animate();
}
setUpRenderer(){
console.log("Heatmap - setUpRenderer()");
let width = $('#container').width();
let height = $('#container').height();
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(width,height);
this.renderer.setClearColor(0x404040);
$("#container").html(this.renderer.domElement);
window.addEventListener("resize", event => {
this.camera.aspect = innerWidth / innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(innerWidth, innerHeight);
});
}
setUpCamera(){
console.log("Heatmap - setUpCamera()");
this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 100);
this.camera.position.z = 4;
}
setUpLighting(){
console.log("Heatmap - setUpLighting()");
this.ambientLight = new THREE.AmbientLight(0xffffff, 1);
this.scene.add(this.ambientLight);
}
setUpOrbitalControls(){
console.log("Heatmap - setUpOrbitalControls()");
this.orbital_controls = new OrbitControls( this.camera, this.renderer.domElement );
this.orbital_controls.target.set(0, 0, 0);
this.orbital_controls.enableDamping = true;
this.orbital_controls.dampingFactor = 0.025;
this.orbital_controls.maxPolarAngle = Math.PI/2.0;
this.orbital_controls.minDistance = 2.0;
this.orbital_controls.maxDistance = 80.0;
this.orbital_controls.update();
this.orbital_controls.addEventListener( 'change', function(){
if (this.target.y < 0){
this.target.y = 0;
/* camera.position.y = -1; */
} else if (this.target.y > 1){
this.target.y = 1;
/* camera.position.y = 1; */
}
});
}
createDataRectangle(){ //TODO: Make this create at a position specified
console.log("Heatmap - createDataRectangle()");
const geometry = new THREE.BufferGeometry();
const vertices = [
-2, 0, 1,//0, floor, bottom left
-1, 0, 1,//1, floor, bottom right
-1, 0, -1,//2, floor, top right
-2, 0, -1,//3 floor, top left
-2, 0, 1,//4 roof, bottom left
-1, 0, 1,//5 roof, bottom right
-1, 0, -1,//6 roof, top right
-2, 0, -1//7 roof, top left
];
const indices = [
0, 1, 2,//floor, first triangle
2, 3, 0,//floor, second triangle
4, 5, 6,//roof, first triangle
6, 7, 4,//roof, second triangle
3, 0, 4,//west wall, first triangle
3, 4, 7,//west wall, second triangle
0, 1, 5,//south wall, first triangle
0, 5, 4,//south wall, first triangle
1, 2, 6,//east wall, first triangle
1, 6, 5,//east wall, second triangle
2, 3, 7,//north wall, first triangle
2, 7, 6,//north wall, second triangle
];
const colors = [
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0, // top left
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0 // top left
];
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometry.computeVertexNormals();
const material = new THREE.MeshStandardMaterial({side:THREE.FrontSide,vertexColors:true,color: 0xFFFFFF });
const sample_rectangle = new THREE.Mesh(geometry, material);
this.heatmap.add(sample_rectangle);
}
animate(){
requestAnimationFrame( this.animate);
this.orbital_controls.update();//Required for Damping on OrbitControls
this.delta += this.clock.getDelta();
if (this.delta > this.interval) {
//this.timeSoFar = this.timeSoFar + (1 * morphDirection);
this.render();
this.delta = this.delta % this.interval;
}
}
render(){
this.renderer.render(this.scene, this.camera);
}
}
$(document).ready(function(){
console.log("DOM ready!");
new Heatmap();
});
CSS的
body,html{
margin: 0;
width: 100%;
height: 100%;
}
#container {
width: 100%;
height: 100%;
background-color: black;
margin: 0;
}
答:
3赞
Kaiido
4/5/2023
#1
我不确定你期待什么
this.delta += this.clock.getDelta();
if (this.delta > this.interval) {
部分就可以了。但这是你的问题,这里的意思是块中的部分每秒只执行一次。
也许你想限制在一些 FPS,在这种情况下你应该有,但我看不出这样做的充分理由,因为限制在固定的 FPS 是非常困难的(你必须选择活动显示器的原生刷新率的倍数,并且使用可变速率显示器......
因此,最好的办法可能是完全摆脱这种检查,并以显示器和浏览器愿意的速度运行。this.interval = 1
if
this.interval = 1 / this.fps;
body,html{
margin: 0;
width: 100%;
height: 100%;
}
#container {
width: 100%;
height: 100%;
background-color: black;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="importmap">
{
"imports":
{
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"OrbitControls": "https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js"
}
}
</script>
<div id="container">
</div>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'OrbitControls';
class Heatmap {
constructor(){
console.log("Heatmap - constructor()");
//ThreeJS Variables
this.camera = null;
this.scene = null;
this.renderer = new THREE.WebGLRenderer({antialias: true});
this.orbital_controls = null;
this.ambientLight = null;
this.heatmap = new THREE.Object3D(); //This will hold all the meshes that make up the heatmap
this.animate = this.animate.bind(this);
this.seconds = 2; //seconds for animation
this.timeSoFar = 0;
this.targetTime = 3;
//Setup
this.scene = new THREE.Scene();
this.setUpCamera();
this.setUpRenderer();
this.setUpLighting();
this.setUpOrbitalControls();
//Add optional GridHelper for Debug
this.scene.add(new THREE.GridHelper());
//TODO: Create the heatmap here
this.createDataRectangle();
//Add the finished heatmap to the scene
this.scene.add(this.heatmap);
this.render();
this.animate();
}
setUpRenderer(){
console.log("Heatmap - setUpRenderer()");
let width = $('#container').width();
let height = $('#container').height();
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(width,height);
this.renderer.setClearColor(0x404040);
$("#container").html(this.renderer.domElement);
window.addEventListener("resize", event => {
this.camera.aspect = innerWidth / innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(innerWidth, innerHeight);
});
}
setUpCamera(){
console.log("Heatmap - setUpCamera()");
this.camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 100);
this.camera.position.z = 4;
}
setUpLighting(){
console.log("Heatmap - setUpLighting()");
this.ambientLight = new THREE.AmbientLight(0xffffff, 1);
this.scene.add(this.ambientLight);
}
setUpOrbitalControls(){
console.log("Heatmap - setUpOrbitalControls()");
this.orbital_controls = new OrbitControls( this.camera, this.renderer.domElement );
this.orbital_controls.target.set(0, 0, 0);
this.orbital_controls.enableDamping = true;
this.orbital_controls.dampingFactor = 0.025;
this.orbital_controls.maxPolarAngle = Math.PI/2.0;
this.orbital_controls.minDistance = 2.0;
this.orbital_controls.maxDistance = 80.0;
this.orbital_controls.update();
this.orbital_controls.addEventListener( 'change', function(){
if (this.target.y < 0){
this.target.y = 0;
/* camera.position.y = -1; */
} else if (this.target.y > 1){
this.target.y = 1;
/* camera.position.y = 1; */
}
});
}
createDataRectangle(){ //TODO: Make this create at a position specified
console.log("Heatmap - createDataRectangle()");
const geometry = new THREE.BufferGeometry();
const vertices = [
-2, 0, 1,//0, floor, bottom left
-1, 0, 1,//1, floor, bottom right
-1, 0, -1,//2, floor, top right
-2, 0, -1,//3 floor, top left
-2, 0, 1,//4 roof, bottom left
-1, 0, 1,//5 roof, bottom right
-1, 0, -1,//6 roof, top right
-2, 0, -1//7 roof, top left
];
const indices = [
0, 1, 2,//floor, first triangle
2, 3, 0,//floor, second triangle
4, 5, 6,//roof, first triangle
6, 7, 4,//roof, second triangle
3, 0, 4,//west wall, first triangle
3, 4, 7,//west wall, second triangle
0, 1, 5,//south wall, first triangle
0, 5, 4,//south wall, first triangle
1, 2, 6,//east wall, first triangle
1, 6, 5,//east wall, second triangle
2, 3, 7,//north wall, first triangle
2, 7, 6,//north wall, second triangle
];
const colors = [
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0, // top left
2,0,0, // bottom left
0,2,0, // bottom right
0,2,0, // top right
2,0,0 // top left
];
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
geometry.computeVertexNormals();
const material = new THREE.MeshStandardMaterial({side:THREE.FrontSide,vertexColors:true,color: 0xFFFFFF });
const sample_rectangle = new THREE.Mesh(geometry, material);
this.heatmap.add(sample_rectangle);
}
animate(){
requestAnimationFrame(this.animate);
this.orbital_controls.update();//Required for Damping on OrbitControls
this.render();
}
render(){
this.renderer.render(this.scene, this.camera);
}
}
$(document).ready(function(){
console.log("DOM ready!");
new Heatmap();
});
</script>
评论
0赞
Joseph Astrahan
4/6/2023
对不起,我觉得自己像个白痴,当我转换到一个班级时应该是/ fps,我忘记了这一步......但是,我确实找到了一种更好的方法来执行 this.animate 部分,它不会给那些想知道的人带来错误, requestAnimationFrame(() => { this.animate() });,这样做可以摆脱该错误,并且仍然非常快。但实际上,主要问题是你指出的!
0赞
Kaiido
4/6/2023
使用 .bind 或匿名函数是相对相同的,绑定函数每帧产生的垃圾略少,这从来都不是您的性能瓶颈。
0赞
Joseph Astrahan
4/6/2023
是的,你是对的,这不是性能瓶颈,只是指出,以这种方式完成的this.animate似乎有效,但如果绑定更好,我很好奇为什么会这样?如果是这样,我可以把它放回原来的样子。
0赞
Kaiido
4/6/2023
两者都不比另一个“更好”,它们相对相同。使用将避免每帧创建一个新函数,这意味着要收集的垃圾更少,因此,可能减少垃圾回收器的中断,可能会阻塞 CPU 超过一帧。但要想在现代设备上做到这一点,你必须运行动画几个小时。如果你想要我的意见,我的首选方法是从类中提取动画循环,并让它调用实例更新方法。这样就没有问题了。bind()
this
评论