Skip to content

5. dat.gui 可视化调试

Published:

上节我们设置立方体大小、相机位置、光照强度等,都是直接设置了一个值。

但很多时候,我们并不知道这个值设置多少合适。

那怎么办呢?

这时候就可以用 dat.gui 来可视化的调试了。

创建项目:

mkdir data-gui
cd data-gui
npm init -y

image.png

安装 three.js 类型:

npm install --save-dev @types/three

然后写下 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>
<body>
    <script type="importmap">
    {
        "imports": {
            "three": "https://esm.sh/three@0.174.0/build/three.module.js",
            "three/addons/": "https://esm.sh/three@0.174.0/examples/jsm/"
        }
    }
    </script>
    <script type="module" src="./index.js"></script>
</body>
</html>

type=“module” 和 type=“importmap” 的 script 的方式引入 three.js

创建 index.js

import * as THREE from 'three';
import {
    OrbitControls
} from 'three/addons/controls/OrbitControls.js';

const scene = new THREE.Scene();

const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshLambertMaterial(({
    color: new THREE.Color('orange')
}));
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 0, 0);
scene.add(mesh);

const pointLight = new THREE.PointLight(0xffffff, 10000);
pointLight.position.set(80, 80, 80);
scene.add(pointLight);

const axesHelper = new THREE.AxesHelper(200);
scene.add(axesHelper);

const width = window.innerWidth;
const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 200, 200);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)

function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

document.body.append(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);

跑一下:

npx live-server

image.png

2025-03-15 11.15.08.gif

现在还是上节的代码。

我们引入 dat.gui

image.png

import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

创建 gui 对象,添加几个控件:

image.png

可视化的调节 mesh.material.color 和 mesh.position 来改变颜色、位置。

const gui = new GUI();
gui.addColor(mesh.material, 'color');
gui.add(mesh.position, 'x').step(10);
gui.add(mesh.position, 'y').step(10);
gui.add(mesh.position, 'z').step(10);

看下效果:

2025-03-15 11.25.59.gif

2025-03-15 11.27.27.gif

右上角出现了一个控制面板,可以可视化的调节立方体的颜色、位置等。

这样直观多了。

你可以在这里调完之后,再把调好的数值更新到代码里。

调节位置的时候,明显可以感觉到光影的变化:

image.png

image.png

灯光的位置、光照强度等显然也应该可视化的调一下:

image.png

调节灯光位置 light.position 和强度 light.intensity

step 是步长,也就是每条调多少

gui.add(pointLight.position, 'x').step(10);
gui.add(pointLight.position, 'y').step(10);
gui.add(pointLight.position, 'z').step(10);
gui.add(pointLight, 'intensity').step(1000);

看下效果:

先调一下灯光的强度:

2025-03-15 11.37.23.gif

再调一下灯光位置:

2025-03-15 11.39.00.gif

可以很直观感受到灯光强度和灯光位置的变化。

但现在有个问题:

image.png

调节立方体和调节灯光的控件混在一起,比较乱。

能不能分个组呢?

可以的。

这样:

image.png

通过 gui.addFolder 创建两个分组,然后把控件添加到不同分组下就可以了。

const meshFolder = gui.addFolder('立方体');
meshFolder.addColor(mesh.material, 'color');
meshFolder.add(mesh.position, 'x').step(10);
meshFolder.add(mesh.position, 'y').step(10);
meshFolder.add(mesh.position, 'z').step(10);
const lightFolder = gui.addFolder('灯光');
lightFolder.add(pointLight.position, 'x').step(10);
lightFolder.add(pointLight.position, 'y').step(10);
lightFolder.add(pointLight.position, 'z').step(10);
lightFolder.add(pointLight, 'intensity').step(1000);

看下效果:

2025-03-15 12.32.57.gif

接下来我们过一下其他控件类型,不同的场景需要不同的控件:

image.png

首先 addFolder 添加一个分组,然后添加各种控件。

其实用法都一样,都是 add,dat.gui 内部会自己根据属性的类型使用不同的控件。

如果是枚举值,需要用数组、对象的方式在第三个参数里列出来。

const otherFolder = gui.addFolder('其他控件类型');

const obj = {
    aaa: '天王盖地虎',
    bbb: false,
    ccc: 0,
    ddd: '111',
    fff: 'Bbb',
    logic: function () {
      alert('执行一段逻辑!');
    }
};

otherFolder.add(obj, 'aaa');
otherFolder.add(obj, 'bbb');
otherFolder.add(obj, 'ccc').min(-10).max(10).step(0.5);
otherFolder.add(obj, 'ddd', [ '111', '222', '333' ] );
otherFolder.add(obj, 'fff', { Aaa: 0, Bbb: 0.1, Ccc: 5 } );
otherFolder.add(obj, 'logic');

看下效果:

2025-03-15 13.50.17.gif

可以看到,分别用输入框、下拉列表、单选框、range 条等来展示。

点击 logic 按钮会执行对应的函数。

要注意的是 ccc 那个,如果没有指定范围,交互就和上面的数字输入框差不多,有了 min、max 之后,会多一个 range 条。

min、max 也可以这样写:

otherFolder.add(obj, 'ccc', -10, 10);

那这些自定义的值如何与三维场景联动呢?

image.png

可以给它添加一个 onChange 事件,在里面拿到 value 来修改三维场景的参数就可以了。

2025-03-15 13.58.03.gif

案例代码上传了小册仓库

总结

dat.gui 是可视化调试 3D 场景中一些参数的工具。

它可以通过 add 添加控件,通过 addFolder 对这些控件分组。

用法是这样 add(obj, ‘prop’),每个控件的类型是根据属性值的类型来确定的,如果是枚举值,可以在第三个参数通过数组、对象来声明。

你还可以添加一些非 3D 场景参数的控件,在 onChange 的回调函数里拿到 value 来修改 3D 场景的参数。

这个调试工具是非常常用的,在后面会大量用到。

评论