Three.js零基础入门学习笔记四:使用GUI工具绘制图形界面

  • 原创
  • 作者:程序员三丰
  • 发布时间:2025-11-11 21:29
  • 浏览量:22
本文介绍如何使用GUI绘制图形界面,借助该图形界面可以更方便调整物体参数并实时看到效果,类似在浏览器的控制台调试CSS样式一样。

Three.js 文档:https://threejs.org/

哔哩哔哩配套视频学习教程地址:https://www.bilibili.com/video/BV1HPYQz7Ed2?spm_id_from=333.788.videopod.episodes&vd_source=628c735d0423e2bd1153ac15c47d3d87

GUI介绍

GUI 顾名思义就是图形用户界面,具体就是通过编程的方式在画布界面上出现图形界面,通过该界面可视化调整物体参数,达到预期效果后,记录图形界面参数值,然后把这些参数值写到代码中。具体效果请看下面的动图(右上角就是GUI):

其实就和浏览器网页开发中调整CSS样式一样,打开浏览器控制台的元素标签页,定位元素后就可以在控制台调整其CSS样式属性值,达到效果后再把控制台调整好的CSS复制或手动写到代码文件中。

注意:Threejs本身没有GUI库,需要借助第三方库 dat.gui 来实现。

下载使用

下载 dat.gui

NPM地址:https://www.npmjs.com/package/dat.gui

API文档地址:https://github.com/dataarts/dat.gui/blob/HEAD/API.md

在项目根目录下执行下面的命令进行安装 dat.gui:

npm install --save dat.gui

引入 dat.gui

在 main.js 中文件开头引入 dat.gui:

#file src/main.js

import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
// 引入 dat.gui
import * as dat from 'dat.gui'

创建 GUI 对象

在 main.js 增加一个名称为 createGUI() 的方法在其中来编写 GUI 相关代码:

#file src/main.js

// 创建 GUI 工具
function createGUI() {
    // 创建 GUI 对象
    const gui = new dat.GUI()
}

// …… 为了更好的阅读,此处省略更多代码,文末会提供完整示例代码

// 创建 GUI
createGUI()

// 渲染循环
renderLoop()

注意:调用createGUI()的位置必须是在GUI控制对象创建之后,渲染循环函数之前。下文中的实践代码都在该方法中实现。

常用方法

下面介绍几个常用的添加控制器的重要方法,如果需要了解更过阅读API文档:https://github.com/dataarts/dat.gui/blob/HEAD/API.md

gui.add() 方法

该方法向图形用户界面添加一个新的控制器。所创建控制器的类型根据控制对象[属性]的初始值推断得出。

注意:颜色属性,需要单独使用 addColor() 方法来添加。

语法如下:

gui.add(object, property, [min], [max], [step]) ⇒ Controller

参数说明:

  • 参数1 object:是要控制的对象,可以是DOM对象,JS对象,或者3D物体对象。
  • 参数2 property:是要控制的对象的某个属性,GUI工具会这个属性的值的类型在图形用户界面中渲染成不同的控制器(输入元素),然后通过改变这些元素的值从而调整这个属性的值。常用有如下几种形式:
    • 属性值为字符串时,创建的控制器渲染为输入框;
    • 属性值为布尔值时,创建的控制器渲染为一个单选框,具体操作就是勾选或取消勾选;
    • 属性值为函数或方法时,创建控制器渲染为按钮。
    • 属性值为数字时,结合后面3个参数,创建控制器渲染为数字滑动选择器 slider 外加一个输入框,参数3 min 限定允许滑动或输入的最小值,参数4 max 限定允许的滑动或输入的最大值,参数5 step 则为 slider 的滑动步长。
  • 参数3:将该参数值设置为对象,创建控制器渲染为选择下拉框,对象的键名渲染为下拉菜单的选项名,对象的值为下拉菜单的值,第1个参数为初始值会初始化下拉框的默认选中项。

gui.addColor() 方法

该方法可以添加一个颜色选择控制器到图形界面。语法如下:

gui.addColor(object, property) ⇒ Controller

参数说明:

  • 参数1 object:是要控制的对象,可以是DOM对象,JS对象,或者3D物体对象。
  • 参数2 property:是要控制的对象的某个颜色属性。

addFolder() 方法

该方法创建一个新的子文件夹图形用户界面实例,可以在该实例下继续添加控制器。

通俗讲,就是说在GUI图形界面上创建了一个分组,在分组下可以继续添加控制对象。语法如下:

gui.addFolder(name) ⇒ dat.gui.GUI

参数说明:

  • 注意:name 不可重复。

实践应用

以下示例代码都是在main.js 中的 createGUI() 的方法中来编写的。每个示例代码块只展示与本示例相关的代码,本文最后章节会提供完整的代码。

添加输入框

示例1:添加一个输入框控制器,来控制 document 对象的标题属性title

function createGUI() {
  const gui = new dat.GUI()
  gui.add(document, 'title')
}

添加单选框

示例2:添加一个单选框控制器,来控制立方体对象 cube 对象的显示控制属性visible,从而控制立方体的显示与隐藏。

function createGUI() {
  const gui = new dat.GUI()
  gui.add(cube, 'visible')
}

添加按钮

示例3:添加一个按钮控制器,来调用控制轨道控制器回归初始角度的方法 reset()

function createGUI() {
  const gui = new dat.GUI()
  gui.add(controls, 'reset')
}

添加数字滑动选择器

示例4:添加一个数字滑动控制器,来控制立方体对象 cube 对象的 x 轴坐标的位置属性position.x,从而控制立方体在 x 轴上的位置。

function createGUI() {
  const gui = new dat.GUI()
  gui.add(cube.position, 'x', 0, 5, 0.1)
}

添加下拉框

示例5:添加一个下拉选择框控制器,通过监听下拉框选中值的变化,从而通过代码实现对 cube 对象的位置坐标进行控制。

function createGUI() {
  const gui = new dat.GUI()
  gui.add({ type: '1' }, 'type', { 方案一: '1', 方案二: '2' }).onChange((val) => {
    switch (val) {
      case '1':
        cube.position.set(0, 0, 0)
        break
      case '2':
        cube.position.set(1, 1, 1)
        break
    }
  })
}

添加颜色选择器

示例6:添加一个颜色选择控制器,来控制 cube 对象的颜色。

注意:由于 Threejs 中对 cube 颜色的处理相对复杂,还要兼容不同格式的颜色值,所以处理上与上面示例稍有不同。

function createGUI() {
  const gui = new dat.GUI()
  // 借助一个中间色值储存对象变量
  const colorObj = {
    col: '#' + cube.material.color.getHexString(),
  }
  // GUI控制上面创建的中间对象变量
  gui.addColor(colorObj, 'col').onChange((val) => {
    // 通过颜色控制器的事件,及时获取颜色控制器的选择值的变化,并转化处理色值的格式,然后更新立方体的颜色属性
    cube.material.color = new THREE.Color(val)
  })
}

创建分组

示例7:添加一个分组控制器,然后在该分组下添加分别控制立方体 cube 在各个坐标轴的位置。

分组的意义就是,根据某个规则把多个控制器放到一起,更容易使用。

function createGUI() {
  const gui = new dat.GUI()
  const cubePoiFolder = gui.addFolder('cube_position')
  cubePoiFolder.add(cube.position, 'x', 0, 5, 0.1)
  cubePoiFolder.add(cube.position, 'y', 0, 5, 0.1)
  cubePoiFolder.add(cube.position, 'z', 0, 5, 0.1)
}

综合代码

下面为综合示例代码:

import './style.css'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as dat from 'dat.gui'

// 场景,摄像机,渲染器
let scene, camera, render
// 轨道控制器
let controls
// 立方体
let cube

// 初始化三要素
function init() {
    // 1. 创建场景对象
    scene = new THREE.Scene()

    // 2. 创建摄像机
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
    // 移动摄像机 z 轴 5 个单位(默认摄像机和物体的坐标轴的都在原点)
    camera.position.z = 5

    // 3. 创建渲染器
    render = new THREE.WebGLRenderer({
        antialias: true, // 开启抗锯齿 - 优化
    })
    render.setSize(window.innerWidth, window.innerHeight) // 设置画布大小
    document.body.append(render.domElement)
}

/**
 * 绘制立方体步骤:
 * 1. 创建图形,宽高深为 1 单位
 * 2. 创建材质,颜色为绿色 0x00ff00
 * 3. 创建网格物体对象,传入图形和材质
 * 4. 把物体对象添加到场景
 * 注意:
 * 1. 默认物体中心与坐标轴重合,所以摄像机需要位移才能看到物体,也就是摄像机需要拉远一些才能看到物体
 * 2. 渲染器需要调用 render 才能渲染画面(等待物体添加到场景后,再调用)
 */
function createCube() {
    // 1. 创建图形,宽深高为 1 单位(立体缓冲几何体)
    const geometry = new THREE.BoxGeometry(1, 1, 1)
    // 2. 创建材质,颜色为绿色 0x00ff00 (网格基础材质 - 线面纯颜色描绘表面)
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
    // 3. 创建网格物体对象,传入图形和材质(网格物体对象)
    cube = new THREE.Mesh(geometry, material)
    // 4. 把物体加入到场景中
    scene.add(cube)
}

/**
 * 轨道控制器:
 * 使摄像机围绕目标进行轨道运动
 * 作用:右键拖动,左键旋转,滚轮拉进,拉远摄像机
 * 使用:
 * 1. 单独引入 OrbitControls 轨道控制器构造函数
 * 2. 创建轨道控制器
 * 3. 在 渲染循环中更新场景渲染
 */
function createControls() {
    controls = new OrbitControls(camera, render.domElement)

    // 启用阻尼
    controls.enableDamping = true

    /*

    // 自动微淘目标旋转旋转
    controls.autoRotate = false
    controls.autoRotateSpeed = 5

    // 垂直角度范围的控制(0  上面,Math.PI 下面)
    controls.maxPolarAngle = Math.PI
    controls.minPolarAngle = 0
    // 水平角度方位的控制,以下配置不能看到正面
    controls.maxAzimuthAngle = 1.5 * Math.PI
    controls.minAzimuthAngle = 0.5 * Math.PI

    // 摄像机移动范围控制(拉进、拉远)
    controls.minDistance = 2
    controls.maxDistance = 10

    */
}

/**
 * 渲染循环
 */
function renderLoop() {
    render.render(scene, camera)

    // 手动 JS 代码更新过摄像机信息,必须调用轨道控制器 update 方法
    controls.update()

    // cube.rotation.x += 0.01

    // 根据当前计算机浏览器刷新帧率(默认 60 次/秒),不断递归调用此函数渲染最新的画面状态
    // 好处:当前页面切换到后台,暂停递归
    requestAnimationFrame(renderLoop)
}

function createHelper() {
    /**
     * 用于简单模拟3个坐标轴的对象.
     * 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
     */
    const axesHelper = new THREE.AxesHelper(5)
    scene.add(axesHelper)
}

/**
 * 优化 - 适配场景大小
 * 目标:浏览器窗口,尺寸改变时,画布自适应
 * 步骤:
 * 1. 创建适配函数,监听浏览器 resize 事件
 * 2. 调整渲染器画布宽高,摄像机宽高比和更新视椎体空间
 */
function resizeRender() {
    window.addEventListener('resize', () => {
        render.setSize(window.innerWidth, window.innerHeight)
        camera.aspect = window.innerWidth / window.innerHeight
        camera.updateProjectionMatrix()
    })
}

/**
 * 移动立方体
 * 使用:所有物体继承自 Object3D 类
 * 1. 位移 position 属性
 * 2. 旋转 rotation 属性
 * 3. 缩放 scale 属性
 */
function moveCube() {
    // cube.position.x = 2
    // cube.position.set(2, 0, 0)
    // 旋转属性值为 Eular 角对象(弧度制),在轴正方形来看物体,默认是绕着轴进行逆时针旋转
    // cube.rotation.x = Math.PI / 4
    //cube.rotation.set(Math.PI / 4, 0, 0)
    //cube.scale.set(1, 1, 2)
}

// 创建 GUI 工具
function createGUI() {
    // 创建 GUI 对象
    const gui = new dat.GUI()

    // 添加具体控制对象
    // 示例1:添加控制 document 对象的标题属性 (属性值为字符串时,图形界面渲染为输入框)
    gui.add(document, 'title')
    // 示例2:添加控制立方体显示与隐藏(属性值为布尔值时,图形界面渲染为多选框)
    gui.add(cube, 'visible')
    // 示例2:添加控制轨道控制器回归初始角度(属性值为函数或方法时,图形界面渲染为按钮)
    gui.add(controls, 'reset')

    // 示例3:控制立方体的颜色
    const colorObj = {
        col: '#' + cube.material.color.getHexString(),
    }
    gui.addColor(colorObj, 'col').onChange((val) => {
        cube.material.color = new THREE.Color(val)
    })

    // 示例4:添加GUI分组 - 控制立方体的位移
    const cubePoiFolder = gui.addFolder('cube_position')
    cubePoiFolder.add(cube.position, 'x', 0, 5, 0.1)
    cubePoiFolder.add(cube.position, 'y', 0, 5, 0.1)
    cubePoiFolder.add(cube.position, 'z', 0, 5, 0.1)

    // 示例4:添加下拉菜单
    gui.add({ type: '1' }, 'type', { 方案一: '1', 方案二: '2' }).onChange((val) => {
        switch (val) {
            case '1':
                cube.position.set(0, 0, 0)
                break
            case '2':
                cube.position.set(1, 1, 1)
                break
        }
    })
}

// 初始化三要素
init()

// 轨道控制器
createControls()

// 坐标轴
createHelper()

// 视频浏览器视口
resizeRender()

// 创建立方体
createCube()
// 变换立方体
moveCube()

// 添加GUI
createGUI()

// 渲染循环
renderLoop()

尾语

至此本文结束了,希望有助于您!

声明:本文为原创文章,51blog.xyz和作者拥有版权,如需转载,请注明来源于51blog.xyz并保留原文链接:https://www.51blog.xyz/article/96.html

文章归档

推荐文章

buildadmin logo
Thinkphp8 Vue3 Element PLus TypeScript Vite Pinia

🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。

热门标签

PHP ThinkPHP ThinkPHP5.1 Go Mysql Mysql5.7 Redis Linux CentOS7 Git HTML CSS CSS3 Javascript JQuery Vue LayUI VMware Uniapp 微信小程序 docker wiki Confluence7 学习笔记 uView ES6 Ant Design Pro of Vue React ThinkPHP6.0 chrome 扩展 翻译工具 Nuxt SSR 服务端渲染 scrollreveal.js ThinkPHP8.0 Mac webman 跨域CORS vscode GitHub ECharts Canvas vue3 three.js