THREEJS
安装
下载
npm install three
引入
import * as THREE from 'three';
const scene = new THREE.Scene();
基础流程
html部分
<div id="webgl"></div>
js部分
import * as THREE from 'three';
import { ref ,onMounted} from 'vue';
//创建一个场景
const scene = new THREE.Scene();
//创建一个形状
const geometry = new THREE.BoxGeometry( 100, 100, 100 );
//创建材质
const material = new THREE.MeshBasicMaterial({color: 0xff6b81} );
//形状和材质整合为物体
const mesh = new THREE.Mesh(geometry,material);
//调整位置
mesh.position.set(0,10,0);
//将物体添加到场景中
scene.add(mesh);
//创建相机
const camera = new THREE.PerspectiveCamera(30,width/height,1,3000 );
//调整相机位置
camera.position.set(200,200,200);
//相机向哪里看
camera.lookAt(mesh.position);
//创建渲染器
const renderer = new THREE.WebGLRenderer();
const width = 800; //宽度
const height = 500; //高度
//设置画布的canvas画布高度和宽度
renderer.setSize(width, height);
//进行渲染
renderer.render(scene, camera);
onMounted(()=>{
document.getElementById('webgl').appendChild(renderer.domElement);
})
组成成分
需要场景Scene、相机Camera、渲染器Renderer才能达到效果
三维场景Scene
//创建3D场景对象Scene
const scene = new THREE.Scene();
物体形状:几何体Geometry
Three.js提供了各种各样的几何体API,用来表示三维物体的几何形状。
文档搜索关键词geometry
你可以看到threejs提供各种几何体相关API,具体使用方法,也可以参考文档。
//创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(100, 100, 100);
物体外观:材质Material
如果你想定义物体的外观效果,比如颜色,就需要通过材质Material
相关的API实现。
threejs不同材质渲染效果不同,下面就以threejs最简单的网格基础材质MeshBasicMaterial (opens new window)为例给大家实现一个红色材质效果。
//创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
color: 0xff0000,//0xff0000设置材质颜色为红色
});
物体:网格模型Mesh
实际生活中有各种各样的物体,在threejs中可以通过网格模型Mesh (opens new window)表示一个虚拟的物体,比如一个箱子、一个鼠标。
// 两个参数分别为几何体geometry、材质material
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
模型位置.position
实际生活中,一个物体往往是有位置的,对于threejs而言也是一样的,你可以通过位置属性.position
定义网格模型Mesh
在三维场景Scene
中的位置。
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);
.add()
方法
在threejs中你创建了一个表示物体的虚拟对象Mesh,需要通过.add()
方法,把网格模型mesh
添加到三维场景scene
中。
scene.add(mesh);
虚拟相机 Camera
透视投影相机PerspectiveCamera
Threejs提供了正投影相机OrthographicCamera (opens new window)和透视投影相机PerspectiveCamera (opens new window)。本节课先给大家比较常用的透视投影相机PerspectiveCamera
。
透视投影相机PerspectiveCamera
本质上就是在模拟人眼观察这个世界的规律。
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
相机位置.position
生活中用相机拍照,你相机位置不同,拍照结果也不同,threejs中虚拟相机同样如此。
比如有一间房子,你拿着相机站在房间里面,看到的是房间内部,站在房子外面看到的是房子外面效果。
相机对象Camera
具有位置属性.position
,通过位置属性.position
可以设置相机的位置。
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200);
相机观察目标.lookAt()
你用相机拍照你需要控制相机的拍照目标,具体说相机镜头对准哪个物体或说哪个坐标。对于threejs相机而言,就是设置.lookAt()
方法的参数,指定一个3D坐标。
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); //坐标原点
camera.lookAt(0, 10, 0); //y轴上位置10
camera.lookAt(mesh.position);//指向mesh对应的位置
判断相机相对三维场景中长方体位置
你可以把三维场景中长方体mesh
想象为一个房间,然后根据相机位置和长方体位置尺寸对比,判断两者相对位置。你可以发现设置相机坐标(200, 200, 200),位于长方体外面一处位置。
// 长方体尺寸100, 100, 100
const geometry = new THREE.BoxGeometry( 100, 100, 100 );
const mesh = new THREE.Mesh(geometry,material);
// 相机位置xyz坐标:0,10,0
mesh.position.set(0,10,0);
// 相机位置xyz坐标:200, 200, 200
camera.position.set(200, 200, 200);
定义相机渲染输出的画布尺寸
你生活中相机拍照的照片是有大小的,对于threejs而言一样,需要定义相机在网页上输出的Canvas画布(照片)尺寸,大小可以根据需要定义,这里先随机定义一个尺寸。
Canvas画布:课程中会把threejs虚拟相机渲染三维场景在浏览器网页上呈现的结果称为Canvas画布。
// 定义相机输出画布的尺寸(单位:像素px)
const width = 800; //宽度
const height = 500; //高度
透视投影相机PerspectiveCamera
:视锥体
透视投影相机的四个参数fov, aspect, near, far
构成一个四棱台3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在Canvas画布上。
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = 800; //宽度
const height = 500; //高度
// 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
PerspectiveCamera
参数介绍:
PerspectiveCamera( fov, aspect, near, far )
参数 | 含义 | 默认值 |
---|---|---|
fov | 相机视锥体竖直方向视野角度 | 50 |
aspect | 相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height | 1 |
near | 相机视锥体近裁截面相对相机距离 | 0.1 |
far | 相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向 | 2000 |
渲染器WebGL
WebGL渲染器WebGLRenderer
通过WebGL渲染器WebGLRenderer (opens new window)可以实例化一个WebGL渲染器对象。
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
设置Canvas画布尺寸.setSize()
// 定义threejs输出画布的尺寸(单位:像素px)
const width = 800; //宽度
const height = 500; //高度
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
渲染器渲染方法.render()
渲染器WebGLRenderer
执行渲染方法.render()
就可以生成一个Canvas画布(照片),并把三维场景Scene呈现在canvas画布上面,你可以把.render()
理解为相机的拍照动作“咔”。
renderer.render(scene, camera); //执行渲染操作
渲染器Canvas画布属性.domElement
渲染器WebGLRenderer
通过属性.domElement
可以获得渲染方法.render()
生成的Canvas画布,.domElement
本质上就是一个HTML元素:Canvas画布。
document.body.appendChild(renderer.domElement);
Canvas画布插入到任意HTML元素中
<div id="webgl" style="margin-top: 200px;margin-left: 100px;"></div>
document.getElementById('webgl').appendChild(renderer.domElement);
辅助观察坐标系axesHelper
辅助观察坐标系
THREE.AxesHelper()
的参数表示坐标系坐标轴线段尺寸大小,你可以根据需要改变尺寸。
// AxesHelper:辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
材质半透明设置
设置材质半透明,这样可以看到坐标系的坐标原点。
const material = new THREE.MeshBasicMaterial({
color: 0x0000ff, //设置材质颜色
transparent:true,//开启透明
opacity:0.5,//设置透明度
});
AxesHelper
的xyz轴
three.js坐标轴颜色红R、绿G、蓝B分别对应坐标系的x、y、z轴,对于three.js的3D坐标系默认y轴朝上。
设置模型在坐标系中的位置或尺寸
通过模型的位置、尺寸设置,加深3D坐标系的概念。
测试:设置长方体xyz不同方向尺寸
// 设置几何体长宽高,也就是x、y、z三个方向的尺寸
//对比三个参数分别对应xyz轴哪个方向
new THREE.BoxGeometry(100, 60, 20);
测试:改变位置
// 设置模型mesh的xyz坐标
mesh.position.set(100,0,0);
改变相机参数——预览新的渲染效果
你可以尝试源码中改变相机的参数,看看场景中的物体渲染效果怎么变化。
相机放在x轴负半轴,目标观察点是坐标原点,这样相当于相机的视线是沿着x轴正方向,只能看到长方体的一个矩形平面。
camera.position.set(-1000, 0, 0);
camera.lookAt(0, 0, 0);
// 相机视线沿着x轴负半轴,mesh位于相机后面,自然看不到
camera.position.set(-1000, 0, 0);
camera.lookAt(-2000, 0, 0);
相机far偏小,mesh位于far之外,物体不会显示在画布上。
// const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
// 你可以进行下面测试,改变相机参数,把mesh放在视锥体之外,看看是否显示
// 3000改为300,使mesh位于far之外,mesh不在视锥体内,被剪裁掉
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 300);
光源Light
光源对光源对物体表面影响
实际生活中物体表面的明暗效果是会受到光照的影响,比如晚上不开灯,你就看不到物体,灯光比较暗,物体也比较暗。在threejs中,咱们用网格模型Mesh
模拟生活中物体,所以threejs中模拟光照Light
对物体表面的影响,就是模拟光照Light
对网格模型Mesh
表面的影响。
你可以打开课件中案例源码,对比有光照和无光照两种情况,网格模型Mesh
表面的差异。
受光照影响材质
threejs提供的网格材质,有的受光照影响,有的不受光照影响。
点光源PointLight
const pointLight = new THREE.PointLight(0xffffff, 1.0);
除了通过THREE.PointLight
的参数2设置光照强度,你可以可以直接访问光照强度属性.intensity
设置。
pointLight.intensity = 1.0;//光照强度
光源衰减
实际生活中点光源,比如比如一个灯泡,随机距离的改变,光线会衰减,越来越弱,光源衰减属性.decay
默认值是2.0,如果你不希望衰减可以设置为0.0
。
pointLight.decay = 0.0;//设置光源不随距离衰减
如果使用默认衰减2.0
,不同版本可能有差异,对于部分threejs新版本,有时候你可能看不到光源效果,这时候可以把光照强度加强试试看,如果你的版本不影响,就不用加强光照强度(根据版本情况灵活对应)。
// 你可以对不同初始光照强度,传播同样距离明暗差异
pointLight.intensity = 10000.0;//光照强度
pointLight.intensity = 50000.0;//光照强度
光源位置
你把点光源想象为一个电灯泡,你在3D空间中,放的位置不同,模型的渲染效果就不一样。
注意光源位置尺寸大小:如果你希望光源照在模型的外表面,那你就需要把光源放在模型的外面。
//点光源位置
pointLight.position.set(400, 0, 0);//点光源放在x轴上
光源添加到场景
光源和网格模型Mesh对应一样是三维场景的一部分,自然需要添加到三维场景中才能起作用。
scene.add(pointLight); //点光源添加到场景中
点光源辅助观察PointLightHelper
通过点光源辅助观察对象[PointLightHelper (opens new window)]可视化点光源。
预览观察:可以借助相机控件OrbitControls
旋转缩放三维场景便于预览点光源位置
// 光源辅助观察
const pointLightHelper = new THREE.PointLightHelper(pointLight, 10);
scene.add(pointLightHelper);
环境光AmbientLight
环境光AmbientLight (opens new window)没有特定方向,只是整体改变场景的光照明暗。
//环境光:没有特定方向,整体改变场景的光照明暗
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
平行光DirectionalLight
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
// 设置光源的方向:通过光源position属性和目标指向对象的position属性计算
directionalLight.position.set(80, 100, 50);
// 方向光指向对象网格模型mesh,可以不设置,默认的位置是0,0,0
directionalLight.target = mesh;
scene.add(directionalLight);
平行光辅助观察DirectionalLightHelper
通过点光源辅助观察对象DirectionalLightHelper (opens new window)可视化点光源。
// DirectionalLightHelper:可视化平行光
const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5,0xff0000);
scene.add(dirLightHelper);
平行光与Mesh表面光线反射规律
平行光照射到网格模型Mesh表面,光线和模型表面构成一个入射角度,入射角度不同,对光照的反射能力不同。
光线照射到漫反射网格材质MeshLambertMaterial (opens new window)对应Mesh表面,Mesh表面对光线反射程度与入射角大小有关。
// 对比不同入射角,mesh表面对光照的反射效果
directionalLight.position.set(100, 0, 0);
directionalLight.position.set(0, 100, 0);
directionalLight.position.set(100, 100, 100);
directionalLight.position.set(100, 60, 50);
//directionalLight.target默认指向坐标原点
动画渲染循环
threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame
实现动画渲染。
请求动画帧window.requestAnimationFrame
// requestAnimationFrame实现周期性循环执行
// requestAnimationFrame默认每秒钟执行60次,但不一定能做到,要看代码的性能
let i = 0;
function render() {
i+=1;
console.log('执行次数'+i);
requestAnimationFrame(render);//请求再次执行函数render
}
render();
备注说明:对于部分高刷新率的电脑硬件设备,.requestAnimationFrame
每秒钟默认调用函数执行次数也是有可能超过60次的,比如你的电脑显卡、显示器等硬件能够支持144hz刷新频率,.requestAnimationFrame
的每秒执行上限,也可以接近144帧率。
threejs旋转动画
动画说白了就是一张张照片,连起来依次展示,这样就形成一个动画效果,只要帧率高,人的眼睛就感觉不到卡顿,是连续的视频效果。
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
// renderer.render(scene, camera); //执行渲染操作
document.body.appendChild(renderer.domElement);
// 渲染函数
function render() {
renderer.render(scene, camera); //执行渲染操作
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧
}
render();
计算两帧渲染时间间隔和帧率
// 渲染循环
const clock = new THREE.Clock();
function render() {
const spt = clock.getDelta()*1000;//毫秒
console.log('两帧渲染时间间隔(毫秒)',spt);
console.log('帧率FPS',1000/spt);
renderer.render(scene, camera); //执行渲染操作
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧
}
render();
相机控件OrbitControls
设置了渲染循环,相机控件OrbitControls就不用再通过事件change
执行renderer.render(scene, camera);
,毕竟渲染循环一直在执行renderer.render(scene, camera);
。
OrbitControls使用
你可以打开课件案例源码测试下效果。
- 旋转:拖动鼠标左键
- 缩放:滚动鼠标中键
- 平移:拖动鼠标右键
引入扩展库OrbitControls.js
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
注意:如果你在原生.html文件中,使用上面引入方式import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
,注意通过<script type="importmap">
配置。
<script type="importmap">
{
"imports": {
"three": "../../../three.js/build/three.module.js",
"three/addons/": "../../../three.js/examples/jsm/"
}
}
</script>
使用OrbitControls
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件
OrbitControls本质
OrbitControls本质上就是改变相机的参数,比如相机的位置属性,改变相机位置也可以改变相机拍照场景中模型的角度,实现模型的360度旋转预览效果,改变透视投影相机距离模型的距离,就可以改变相机能看到的视野范围。
controls.addEventListener('change', function () {
// 浏览器控制台查看相机位置变化
console.log('camera.position',camera.position);
});
宽度自适应
全屏渲染
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = window.innerWidth; //窗口文档显示区的宽度作为画布宽度
const height = window.innerHeight; //窗口文档显示区的高度作为画布高度
const renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
canvas画布宽高度动态变化
canvas画布宽高度动态变化,需要更新相机和渲染的参数,否则无法正常渲染。
// onresize 事件会在窗口被调整大小时发生
window.onresize = function () {
// 重置渲染器输出画布canvas尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
// 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
// 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
camera.updateProjectionMatrxingix();
};
性能监视器
引入Stats
//引入性能监视器stats.js
import Stats from 'three/addons/libs/stats.module.js';
Stats使用
//创建stats对象
const stats = new Stats();
//stats.domElement:web页面上输出计算结果,一个div元素,
document.body.appendChild(stats.domElement);
// 渲染函数
function render() {
//requestAnimationFrame循环调用的函数中调用方法update(),来刷新时间
stats.update();
renderer.render(scene, camera); //执行渲染操作
requestAnimationFrame(render); //请求再次执行渲染函数render,渲染下一帧
}
render();
stats方法setMode(mode)
可以通过setMode()
方法的参数mode的数值设置首次打开页面,测试结果的显示模式,鼠标单击可以更换不同的显示模式。
// stats.domElement显示:渲染帧率 刷新频率,一秒渲染次数
stats.setMode(0);//默认模式
//stats.domElement显示:渲染周期 渲染一帧多长时间(单位:毫秒ms)
stats.setMode(1);
性能测试
控制长方体模型数量,你可以逐渐增加或减少,看看帧率变化,电脑性能不同结果不同。
// 随机创建大量的模型,测试渲染性能
const num = 1000; //控制长方体模型数量
for (let i = 0; i < num; i++) {
const geometry = new THREE.BoxGeometry(5, 5, 5);
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff
});
const mesh = new THREE.Mesh(geometry, material);
// 随机生成长方体xyz坐标
const x = (Math.random() - 0.5) * 200
const y = (Math.random() - 0.5) * 200
const z = (Math.random() - 0.5) * 200
mesh.position.set(x, y, z)
scene.add(mesh); // 模型对象插入场景中
}
Threejs常见几何体简介
Three.js提供的几何体API很多,本节课先给大家介绍几个比较简单的案例,为后面的学习打下基础。
你可以结合threejs文档,把下面动手把下面几何体相关代码全部测试一遍,并预览3D效果。
//BoxGeometry:长方体
const geometry = new THREE.BoxGeometry(100, 100, 100);
// SphereGeometry:球体
const geometry = new THREE.SphereGeometry(50);
// CylinderGeometry:圆柱
const geometry = new THREE.CylinderGeometry(50,50,100);
// PlaneGeometry:矩形平面
const geometry = new THREE.PlaneGeometry(100,50);
// CircleGeometry:圆形平面
const geometry = new THREE.CircleGeometry(50);
双面可见
Three.js的材质默认正面可见,反面不可见,对于矩形平面PlaneGeometry
、圆形平面如果你想看到两面,可以设置side: THREE.DoubleSide
。
new THREE.MeshBasicMaterial({
side: THREE.FrontSide, //默认只有正面可见
});
new THREE.MeshBasicMaterial({
side: THREE.DoubleSide, //两面可见
});
高光网格材质Phong
高光网格材质MeshPhongMaterial
和基础网格材质MeshBasicMaterial
、漫反射网格材质MeshLambertMaterial
一样都是网格模型的Mesh
的材质。
高光网格材质MeshPhongMaterial和漫反射网格材质MeshLambertMaterial一样会受到光照的影响。
MeshPhongMaterial
对光照反射特点
MeshPhongMaterial
和MeshLambertMaterial
都会收到光照的影响区别在于,对光线反射方式有差异。
MeshPhongMaterial
可以实现MeshLambertMaterial
不能实现的高光反射效果。对于高光效果,你可以想象一下,你在太阳下面观察一辆车,你会发现在特定角度和位置,你可以看到车表面某个局部区域非常高亮。
镜面反射与漫反射
MeshPhongMaterial
可以提供一个镜面反射效果,可以类比你生活中拿一面镜子,放在太阳光下,调整角度,可以把太阳光反射到其它地方,如果反射光对着眼睛,也就是反射光线和视线平行的时候,会非常刺眼。
MeshLambertMaterial
对应的Mesh受到光线照射,没有镜面反射的效果,只是一个漫反射,也就是光线向四周反射。
高光亮度属性.shininess
通过MeshPhongMaterial
的高光亮度.shininess
属性,可以控制高光反射效果。
// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 20, //高光部分的亮度,默认30
});
高光颜色属性.specular
可以给颜色属性.specular
设置不同的值,比如0x444444
、0xfffffff
查看渲染效果变化。
// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 20, //高光部分的亮度,默认30
specular: 0x444444, //高光部分的颜色
});
WebGL渲染器设置(锯齿模糊)
渲染器锯齿属性.antialias
设置渲染器锯齿属性.antialias
的值可以直接在参数中设置,也可通过渲染器对象属性设置。
const renderer = new THREE.WebGLRenderer({
antialias:true,
});
或
renderer.antialias = true,
设备像素比window.devicePixelRatio
如果你有web前端基础,应该了解window
对象,设备像素比.devicePixelRatio
是window对象的一个属性,该属性的值和你的硬件设备屏幕相关,不同硬件设备的屏幕window.devicePixelRatio
的值可能不同,可能就是1、1.5、2.0等其它值。
// 不同硬件设备的屏幕的设备像素比window.devicePixelRatio值可能不同
console.log('查看当前屏幕设备像素比',window.devicePixelRatio);
设置设备像素比.setPixelRatio()
如果你遇到你的canvas画布输出模糊问题,注意设置renderer.setPixelRatio(window.devicePixelRatio)
。
注意:注意你的硬件设备设备像素比window.devicePixelRatio
刚好是1,那么是否执行.setPixelRatio()
不会有明显差异,不过为了适应不同的硬件设备屏幕,通常需要执行该方法。
// 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
设置背景颜色.setClearColor()
renderer.setClearColor(0x444444, 1); //设置背景颜色
gui.js库(可视化改变三维场景)
gui.js库(可视化改变三维场景)
dat.gui.js说白了就是一个前端js库,对HTML、CSS和JavaScript进行了封装,学习开发的时候,借助dat.gui.js可以快速创建控制三维场景的UI交互界面,你打开课件中案例源码体验一下就能感受到。
课程学习dat.gui.js也不仅仅是为了学习dat.gui.js,也是建立一种思想,就是threejs三维空间的很多参数,不是心算出来的,往往需要可视化的方式调试出来。
引入dat.gui.js
gihtub地址:https://github.com/dataarts/dat.gui
npm地址:https://www.npmjs.com/package/dat.gui
你可以通过npm或github方式获得dat.gui.js库,当然为了学习方便,threejs官方案例扩展库中也提供了gui.js,你可以直接使用。
// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
创建一个GUI对象
创建一个GUI对象,你可以看到浏览器右上角多了一个交互界面,GUI本质上就是一个前端js库。
// 实例化一个gui对象
const gui = new GUI();
.domElement
:改变GUI界面默认的style属性
通过.domElement
属性可以获取gui界面的HTML元素,那就意味着你可以改变默认的style样式,比如位置、宽度等。
//改变交互界面style属性
gui.domElement.style.right = '0px';
gui.domElement.style.width = '300px';
.add()
方法
执行gui的.add()
方法可以快速创建一个UI交互界面,比如一个拖动条,可以用来改变一个JavaScript对象属性的属性值。
格式:.add(控制对象,对象具体属性,其他参数)
其他参数,可以一个或多个,数据类型也可以不同,gui会自动根据参数形式,自动生成对应的交互界面。
参数3和参数4,分别是一个数字,交互界面是一个鼠标可以拖动的拖动条,可以在一个区间改变属性的值
执行gui.add(obj, 'x', 0, 100);
你可以发现右上角gui界面增加了新的内容,可以控制obj对象x属性的新交互界面。
//创建一个对象,对象属性的值可以被GUI库创建的交互界面改变
const obj = {
x: 30,
};
// gui增加交互界面,用来改变obj对应属性
gui.add(obj, 'x', 0, 100);
体验.add()
功能——改变对象属性值
为了方便观察.add()
是如何改变JavaScript对象属性的,可以浏览器控制台不停地打印obj的值,这样通过gui界面拖动改变obj对象属性的的时候,便于观察obj的变化。
const obj = {x: 30};
setInterval(function () {
console.log('x', obj.x);
}, 10)
gui改变js对象多个属性
const obj = {
x: 30,
y: 60,
z: 300,
};
// gui界面上增加交互界面,改变obj对应属性
gui.add(obj, 'x', 0, 100);
gui.add(obj, 'y', 0, 50);
gui.add(obj, 'z', 0, 60);
gui改变threejs光照强度测试
three.js在调试场景渲染效果的时候,比如光照的强度,人大脑的CPU是没有能力通过光照参数算出来模型渲染效果的,一般来说你先大概给一个经验值,然后通过gui在这个大概值的基础上下浮动可视化调试。
光源对象具有一个光照强度属性.intensity
,可以通过gui拖动条改变该属性。
// 光照强度属性.intensity
console.log('ambient.intensity',ambient.intensity);
// 通过GUI改变mesh.position对象的xyz属性
gui.add(ambient, 'intensity', 0, 2.0);
gui改变threejs模型位置测试
mesh.position
是JavaScript对象,具有x
、y
、z
属性,这三个属性分别表示模型的xyz坐标,这就是说,gui改变mesh.position
的x
、y
、z
属性,就可以可视化改变mesh的位置。
gui.add(mesh.position, 'x', 0, 180);
gui.add(mesh.position, 'y', 0, 180);
gui.add(mesh.position, 'z', 0, 180);
gui调试界面2-颜色命名等
.name()
方法
.add()
创建的交互界面,会默认显示所改变属性的名字,为了通过交互界面更好理解你改变的某个对象属性,你可以通过.name()
方法改变gui生成交互界面显示的内容。
const gui = new GUI();//创建GUI对象
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度');
gui.add(directionalLight, 'intensity', 0, 2.0).name('平行光强度');
步长.step()
方法
步长.step()
方法可以设置交互界面每次改变属性值间隔是多少。
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度').step(0.1);
.onChange()
方法
当gui界面某个值的时候,.onChange()
方法就会执行,这时候你可以根据需要通过.onChange()
执行某些代码。
const obj = {
x: 30,
};
// 当obj的x属性变化的时候,就把此时obj.x的值value赋值给mesh的x坐标
gui.add(obj, 'x', 0, 180).onChange(function(value){
mesh.position.x = value;
// 你可以写任何你想跟着obj.x同步变化的代码
// 比如mesh.position.y = value;
});
.addColor()
颜色值改变
.addColor()
生成颜色值改变的交互界面
const obj = {
color:0x00ffff,
};
// .addColor()生成颜色值改变的交互界面
gui.addColor(obj, 'color').onChange(function(value){
mesh.material.color.set(value);
});
gui调试3-下拉菜单、单选框
.add()
方法参数3和4数据类型:数字
格式:add(控制对象,对象具体属性,其他参数)
其他参数,可以一个或多个,数据类型也可以不同,gui会自动根据参数形式,自动生成对应的交互界面。
参数3和参数4,分别是一个数字,交互界面是一个鼠标可以拖动的拖动条,可以在一个区间改变属性的值
// 参数3、参数4数据类型:数字(拖动条)
gui.add(obj, 'x', 0, 180).onChange(function (value) {
mesh.position.x = value;
});
.add()
方法参数3数据类型:数组
参数3是一个数组,生成交互界面是下拉菜单
const obj = {
scale: 0,
};
// 参数3数据类型:数组(下拉菜单)
gui.add(obj, 'scale', [-100, 0, 100]).name('y坐标').onChange(function (value) {
mesh.position.y = value;
});
.add()
方法参数3数据类型:对象
参数3是一个对象,生成交互界面是下拉菜单
const obj = {
scale: 0,
};
// 参数3数据类型:对象(下拉菜单)
gui.add(obj, 'scale', {
left: -100,
center: 0,
right: 100
// 左: -100,//可以用中文
// 中: 0,
// 右: 100
}).name('位置选择').onChange(function (value) {
mesh.position.x = value;
});
.add()
方法对应属性的数据类型:布尔值
如果.add()
改变属性的对应的数据类型如果是布尔值,那么交互界面就是一个单选框。
const obj = {
bool: false,
};
// 改变的obj属性数据类型是布尔值,交互界面是单选框
gui.add(obj, 'bool').name('是否旋转');
gui.add(obj, 'bool').onChange(function (value) {
// 点击单选框,控制台打印obj.bool变化
console.log('obj.bool',value);
});
.add()
方法改变布尔值案例
控制一个对象是否旋转。
gui.add(obj, 'bool').name('旋转动画');
// 渲染循环
function render() {
// 当gui界面设置obj.bool为true,mesh执行旋转动画
if (obj.bool) mesh.rotateY(0.01);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
gui.js库(分组)
gui交互界面不分组
gui交互界面不分组,只有一个默认的总的菜单。
const gui = new GUI(); //创建GUI对象
//创建一个对象,对象属性的值可以被GUI库创建的交互界面改变
const obj = {
color: 0x00ffff,// 材质颜色
specular: 0x111111,// 材质高光颜色
};
// 材质颜色color
gui.addColor(obj, 'color').onChange(function(value){
material.color.set(value);
});
// 材质高光颜色specular
gui.addColor(obj, 'specular').onChange(function(value){
material.specular.set(value);
});
// 环境光强度
gui.add(ambient, 'intensity',0,2);
// 平行光强度
gui.add(directionalLight, 'intensity',0,2);
// 平行光位置
gui.add(directionalLight.position, 'x',-400,400);
gui.add(directionalLight.position, 'y',-400,400);
gui.add(directionalLight.position, 'z',-400,400);
.addFolder()
分组
new GUI()
实例化一个gui对象,默认创建一个总的菜单,通过gui对象的.addFolder()
方法可以创建一个子菜单,当你通过GUI控制的属性比较多的时候,可以使用.addFolder()
进行分组。
.addFolder()
返回的子文件夹对象,同样具有gui对象的.add()
、.onChange()
、.addColor()
等等属性。
const gui = new GUI(); //创建GUI对象
const obj = {
color: 0x00ffff,// 材质颜色
};
// 创建材质子菜单
const matFolder = gui.addFolder('材质');
matFolder.close();
// 材质颜色color
matFolder.addColor(obj, 'color').onChange(function(value){
material.color.set(value);
});
// 材质高光颜色specular
matFolder.addColor(obj, 'specular').onChange(function(value){
material.specular.set(value);
});
// 环境光子菜单
const ambientFolder = gui.addFolder('环境光');
// 环境光强度
ambientFolder.add(ambient, 'intensity',0,2);
// 平行光强度
dirFolder.add(directionalLight, 'intensity',0,2);
// 平行光位置
dirFolder.add(directionalLight.position, 'x',-400,400);
dirFolder.add(directionalLight.position, 'y',-400,400);
dirFolder.add(directionalLight.position, 'z',-400,400);
关闭.close()
和展开.open()
交互界面
gui对象创建的总菜单或gui.addFolder()
创建的子菜单都可以用代码控制交互界面关闭或开展状态。
const gui = new GUI(); //创建GUI对象
gui.close();//关闭菜单
// 创建材质子菜单
const matFolder = gui.addFolder('材质');
matFolder.close();//关闭菜单
.addFolder()
套娃——子菜单嵌套子菜单
.addFolder()
创建的对象,同样也具有.addFolder()
属性,可以继续嵌套子菜单。
// 平行光子菜单
const dirFolder = gui.addFolder('平行光');
dirFolder.close();//关闭菜单
// 平行光强度
dirFolder.add(directionalLight, 'intensity',0,2);
const dirFolder2 = dirFolder.addFolder('位置');//子菜单的子菜单
dirFolder2.close();//关闭菜单
// 平行光位置
dirFolder2.add(directionalLight.position, 'x',-400,400);
dirFolder2.add(directionalLight.position, 'y',-400,400);
dirFolder2.add(directionalLight.position, 'z',-400,400);
threejs语法总结
Three.js语法总结:类(构造函数)
Three.js提供了各种各样的类(构造函数),通过new
关键字可以实例化类(构造函数),获得一个对象,对象具有属性和方法。
// new实例化类THREE.MeshLambertMaterial,创建一个材质对象
const material = new THREE.MeshLambertMaterial();
// 可以看到材质对象的属性color、side、opacity、transparent...
// 通过属性可以看到threejs默认的属性值
console.log('查看材质对象',material);
// 查看材质默认属性值
console.log('material.color',material.color);
console.log('material.side',material.side);
console.log('material.transparent',material.transparent);
console.log('material.opacity',material.opacity);
类(构造函数)的参数设置属性
通过类(构造函数)的参数设置属性
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff,
side:THREE.DoubleSide,
transparent:true,
opacity:0.5,
});
查看选项参数设置的材质属性值,可以和原来默认属性值对比
console.log('material.color',material.color);
console.log('material.side',material.side);
console.log('material.transparent',material.transparent);
console.log('material.opacity',material.opacity);
访问对象属性改变属性的值
// 访问对象属性改变属性的值
material.transparent = false;
material.opacity = 1.0;
console.log('directionalLight',ambient.intensity);
directionalLight.intensity = 0.1;//改变光源强度
父类和子类
如果你学习过JavaScript面向对象,应该有父类和子类的概念,子类是通过父类派生出来的,会继承父类的属性或方法。
- 环境光、平行光源的父类Light
- mesh、light光源的父类Object3D
如果你想通过文档查询一个类的方法或属性,除了可以查询类本身,还可以查询类的父类。
通过对象的方法改变对象的属性
console.log('模型位置属性',mesh.position);
mesh.position.x = 50;//访问属性改变位置x坐标
mesh.translateX(50);//执行方法改变位置属性
[…] 开始之前我们要了解3d场景的三个要素、场景Scene、相机Camera、渲染器Renderer,所有的图像都是在这三个基础上进行展现的,不了解的可以查看我之前three.js笔记。 […]