云图三维 连接你·创造的世界 致力于打造国内第一家集查看、建模、装配和渲染于一体的“云端CAD”协作设计平台。
应读者的要求,希望我们成立一个专业的、面向成渝地区的前端开发人员的webgl、Threejs行业QQ交流群,便于大家讨论问题。群里有研究webgl、Threejs大佬哦,欢迎大家加入!——点击链接加入群聊【three.js/webgl重庆联盟群】:jq.qq.com/?_wv=1027&k…
按需加载
在three.js中,他们设置了一个 requestAnimationFrame
循环或rAF 循环
这样的东西
function render() {
...
requestAnimationFrame(render);
}
requestAnimationFrame(render);
复制代码
对于有动画效果的东西来说这是有道理的,但是对于没有动画效果的东西呢?在这种情况下,连续渲染会浪费设备电源,如果用户使用便携式设备,则会浪费用户的电池。
解决这个问题最明显的方法是在开始时渲染一次,然后仅在发生变化时才渲染。更改包括最终加载的纹理或模型、来自某些外部来源的数据、用户调整设置或相机提供其他相关输入。
首先,我们将控制器添加进来,OrbitControls
以便我们可以响应一些可以更改的内容。
import * as THREE from './resources/three/r132/build/three.module.js';
import {OrbitControls} from './resources/threejs/r132/examples/jsm/controls/OrbitControls.js';
复制代码
并设置它们
const fov = 75;
const aspect = 2; // 画布默认
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const controls = new OrbitControls(camera, canvas);
controls.target.set(0, 0, 0);
controls.update();
复制代码
由于我们将不再为立方体设置动画,因此我们不再需要跟踪它们
makeInstance(geometry, 0x44aa88, 0);
makeInstance(geometry, 0x8844aa, -2);
makeInstance(geometry, 0xaa8844, 2);
复制代码
我们可以删除动画立方体的代码和调用 requestAnimationFrame
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
}
复制代码
我们需要随时渲染OrbitControls
更改相机设置。幸运的是,OrbitControls
只要有change
事情发生变化,就会调度一个事件。
controls.addEventListener('change', render);
复制代码
我们还需要处理用户调整窗口大小的情况。这在之前是自动处理的,因为我们是连续渲染的,但现在我们不需要在窗口改变大小时渲染。
window.addEventListener('resize', render);
复制代码
有了这个,我们得到了一些按需渲染的东西。如下图所示
在OrbitControls
中可以选择添加一种惯性,使他们感觉不那么生硬。我们可以通过将enableDamping
属性设置为 true来启用此功能。
controls.enableDamping = true;
复制代码
随着enableDamping
我们需要调用controls.update
的渲染函数,以便在OrbitControls
平滑移动时可以继续为我们提供新的相机设置。但是,这意味着我们不能在render
中直接调用change
事件,因为我们最终会陷入无限循环。控件将向我们发送一个change
事件和render
,render
将调用controls.update
. controls.update
将发送另一个change
事件。
我们可以通过调用requestAnimationFrame
来解决这个问题,render
但是我们需要确保我们只在没有被请求的情况下才请求一个新的帧,我们可以通过保留一个变量来跟踪我们是否已经请求了一个帧。
let renderRequested = false;
function render() {
renderRequested = false;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
}
render();
function requestRenderIfNotRequested() {
if (!renderRequested) {
renderRequested = true;
requestAnimationFrame(render);
}
}
controls.addEventListener('change', requestRenderIfNotRequested);
复制代码
我们也可以使用requestRenderIfNotRequested
来调整大小
window.addEventListener('resize', requestRenderIfNotRequested);
复制代码
可能很难看出区别。尝试单击下面的示例并使用箭头键来回移动或拖动旋转。然后尝试单击上面的示例并执行相同的操作,您应该能够分辨出不同之处。当您按箭头键或拖动时,上方的会捕捉,下方的会滑动。
我们还添加一个简单的 dat.GUI GUI 并使其更改按需呈现。
import * as THREE from './resources/three/r132/build/three.module.js';
import {OrbitControls} from './resources/threejs/r132/examples/jsm/controls/OrbitControls.js';
import {GUI} from '../3rdparty/dat.gui.module.js';
复制代码
我们可以设置每个立方体的颜色和 x 比例。为了能够设置颜色,我们将使用ColorGUIHelper
来创建。
首先我们需要创建一个GUI
const gui = new GUI();
复制代码
然后对于每个多维数据集,我们将创建一个文件夹并添加 2 个控件,一个用于 material.color
,另一个用于cube.scale.x
。
function makeInstance(geometry, color, x) {
const material = new THREE.MeshPhongMaterial({color});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
cube.position.x = x;
const folder = gui.addFolder(`Cube${x}`);
folder.addColor(new ColorGUIHelper(material, 'color'), 'value')
.name('color')
.onChange(requestRenderIfNotRequested);
folder.add(cube.scale, 'x', .1, 1.5)
.name('scale x')
.onChange(requestRenderIfNotRequested);
folder.open();
return cube;
}
复制代码
你可以看到上面的 dat.GUI 控件有一个onChange
方法,当 GUI 改变一个值时,你可以传递一个回调来调用。在我们的例子中,我们只需要在requestRenderIfNotRequested
中调用folder.open
使文件夹开始扩展。
我希望这能让你对如何让three.js 按需渲染而不是连续渲染有所了解。按需呈现three.js 的应用程序/页面不像大多数使用three.js 的页面那样常见,要么是游戏要么是3D 动画艺术,但可以更好地按需呈现的页面示例可以说是地图查看器、3d 编辑器、 3d 图形生成器、产品目录等...
写在最后
本文介绍了Three.js按需加载相关的内容,希望对你有帮助。
本文发布自 云图三维大前端团队,文章未经授权禁止任何形式的转载。