一、本地搭建Threejs官方文档网站
1.官网地址
Github:github.com/mrdoob/thre...
我们可以直接下载压缩包并解压或使用 git clone 若 github 过慢,则使用 gitee 对应镜像仓库:three.js: mrdoob/three.js 同步...
2.目录解析
部署步骤
1.为项目创建一个目录,并在项目根目录下执行
sql
npm create vite@latest
2、APP.vue
javascript
import * as THREE from "three";
// console.log(THREE);
// 目标:了解three.js最基本的内容
// 1、创建场景
const scene = new THREE.Scene();
// 2、创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// 设置相机位置
camera.position.set(0, 0, 10);
scene.add(camera);
// 添加物体
// 创建几何体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });
// 根据几何体和材质创建物体
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 将几何体添加到场景中
scene.add(cube);
// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// console.log(renderer);
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);
// 使用渲染器,通过相机将场景渲染进来
renderer.render(scene, camera);
第一个3D案例---创建3D场景
入门Three.js的第一步,就是认识场景Scene 、相机Camera 、渲染器Renderer三个基本概念
三维场景Scene
你可以把三维场景Scene (opens new window)对象理解为虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界。
arduino
// 创建3D场景对象Scene
const scene = new THREE.Scene();
物体形状:几何体Geometry
Three.js提供了各种各样的几何体API,用来表示三维物体的几何形状
文档搜索关键词geometry
你可以看到threejs提供各种几何体相关API,具体使用方法,也可以参考文档。
arduino
//创建一个长方体几何对象Geometry
const geometry = new THREE.BoxGeometry(100, 100, 100);
物体外观:材质Material
如果你想定义物体的外观效果,比如颜色,就需要通过材质Material
相关的API实现。
threejs不同材质渲染效果不同,下面就以threejs最简单的网格基础材质 MeshBasicMaterial (opens new window)为例给大家实现一个红色材质效果。
arduino
//创建一个材质对象Material
const material = new THREE.MeshBasicMaterial({
color: 0xff0000,//0xff0000设置材质颜色为红色
});
物体:网格模型Mesh
实际生活中有各种各样的物体,在threejs中可以通过网格模型 Mesh (opens new window)表示一个虚拟的物体,比如一个箱子、一个鼠标。
arduino
// 两个参数分别为几何体geometry、材质material
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
模型位置.position
实际生活中,一个物体往往是有位置的,对于threejs而言也是一样的,你可以通过位置属性.position
定义网格模型Mesh
在三维场景Scene
中的位置。
arduino
const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//设置网格模型在三维空间中的位置坐标,默认是坐标原点
mesh.position.set(0,10,0);
.add()
方法
在threejs中你创建了一个表示物体的虚拟对象Mesh,需要通过.add()
方法,把网格模型mesh
添加到三维场景scene
中。
csharp
scene.add(mesh);
后续内容
写到这里,我知道你已经迫不及待想执行代码看看渲染效果了,那么你需要看看后面两节课关于相机Camera 和渲染器Renderer的介绍
第一个3D案例---透视投影相机
Threejs如果想把三维场景Scene
渲染到web网页上,还需要定义一个虚拟相机 Camera
,就像你生活中想获得一张照片,需要一台用来拍照的相机。
透视投影相机PerspectiveCamera
Threejs提供了正投影相机OrthographicCamera (opens new window)和透视投影相机PerspectiveCamera (opens new window)。常用的透视投影相机PerspectiveCamera
。
透视投影相机PerspectiveCamera
本质上就是在模拟人眼观察这个世界的规律。
arduino
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
相机位置.posiiotn
生活中用相机拍照,你相机位置不同,拍照结果也不同,threejs中虚拟相机同样如此。
比如有一间房子,你拿着相机站在房间里面,看到的是房间内部,站在房子外面看到的是房子外面效果。
相机对象Camera
具有位置属性.posiiotn
,通过位置属性.posiiotn
可以设置相机的位置。
arduino
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200);
相机观察目标.lookAt()
你用相机拍照你需要控制相机的拍照目标 ,具体说相机镜头对准哪个物体或说哪个坐标。对于threejs相机而言,就是设置.lookAt()
方法的参数,指定一个3D坐标。
scss
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); //坐标原点
scss
camera.lookAt(0, 10, 0); //y轴上位置10
arduino
camera.lookAt(mesh.position);//指向mesh对应的位置
'
判断相机相对三维场景中长方体位置
你可以把三维场景中长方体mesh
想象为一个房间,然后根据相机位置和长方体位置尺寸对比,判断两者相对位置。你可以发现设置相机坐标(200, 200, 200),位于长方体外面一处位置。
arduino
// 长方体尺寸100, 100, 100
const geometry = new THREE.BoxGeometry( 100, 100, 100 );
const mesh = new THREE.Mesh(geometry,material);
// mess位置xyz坐标:0,10,0
mesh.position.set(0,10,0);
// 相机位置xyz坐标:200, 200, 200
camera.position.set(200, 200, 200);
定义相机渲染输出的画布尺寸
你生活中相机拍照的照片是有大小的,对于threejs而言一样,需要定义相机在网页上输出的Canvas画布(照片)尺寸,大小可以根据需要定义,这里先随机定义一个尺寸。
Canvas画布
arduino
// 定义相机输出画布的尺寸(单位:像素px)
const width = 800; //宽度
const height = 500; //高度
透视投影相机PerspectiveCamera
:视锥体
透视投影相机的四个参数fov, aspect, near, far
构成一个四棱台 3D空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在Canvas画布上。
arduino
// 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
参数介绍:
scss
PerspectiveCamera( fov, aspect, near, far )
参数 | 含义 | 默认值 |
---|---|---|
fov | 相机视锥体竖直方向视野角度 | 50 |
aspect | 相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height | 1 |
near | 相机视锥体近裁截面相对相机距离 | 0.1 |
far | 相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向 | 2000 |
第一个3D案例---渲染器
生活中如果有了景物 和相机 ,那么如果想获得一张照片,就需要你拿着相机,按一下,咔,完成拍照。对于threejs而言,如果完成"咔"这个拍照动作,就需要一个新的对象,也就是WebGL渲染器WebGLRenderer (opens new window)。
WebGL渲染器WebGLRenderer
通过WebGL渲染器WebGLRenderer (opens new window)可以实例化一个WebGL渲染器对象。
arduino
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
#设置Canvas画布尺寸.setSize()
arduino
// 定义threejs输出画布的尺寸(单位:像素px)
const width = 800; //宽度
const height = 500; //高度
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
染器渲染方法.render()
渲染器WebGLRenderer
执行渲染方法.render()
就可以生成一个Canvas画布(照片),并把三维场景Scene呈现在canvas画布上面,你可以把.render()
理解为相机的拍照动作"咔"。
scss
renderer.render(scene, camera); //执行渲染操作
渲染器Canvas画布属性.domElement
渲染器WebGLRenderer
通过属性.domElement
可以获得渲染方法.render()
生成的Canvas画布,.domElement
本质上就是一个HTML元素:Canvas画布。
ini
document.body.appendChild(renderer.domElement);
Canvas画布插入到任意HTML元素中
css
<div id="webgl" style="margin-top: 200px;margin-left: 100px;"></div>
javascript
document.getElementById('webgl').appendChild(renderer.domElement);
三维坐标系-加强三维空间认识
threejs三维空间的认识。
辅助观察坐标系
THREE.AxesHelper()
的参数表示坐标系坐标轴线段尺寸大小,你可以根据需要改变尺寸。
csharp
// AxesHelper:辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(150);
scene.add(axesHelper);
材质半透明设置
设置材质半透明,这样可以看到坐标系的坐标原点。
arduino
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不同方向尺寸
arduino
// 设置几何体长宽高,也就是x、y、z三个方向的尺寸
//对比三个参数分别对应xyz轴哪个方向
new THREE.BoxGeometry(100, 60, 20);
测试:改变位置
arduino
// 设置模型mesh的xyz坐标
mesh.position.set(100,0,0);
改变相机参数------预览新的渲染效果
你可以尝试源码中改变相机的参数,看看场景中的物体渲染效果怎么变化。
相机放在x轴负半轴,目标观察点是坐标原点,这样相当于相机的视线是沿着x轴正方向,只能看到长方体的一个矩形平面。
ini
camera.position.set(-1000, 0, 0);
camera.lookAt(0, 0, 0);
scss
// 相机视线沿着x轴负半轴,mesh位于相机后面,自然看不到
camera.position.set(-1000, 0, 0);
camera.lookAt(-2000, 0, 0);
相机far偏小,mesh位于far之外,物体不会显示在画布上。
arduino
// 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);
光源对物体表面影响
实际生活中物体表面的明暗 效果是会受到光照 的影响,比如晚上不开灯,你就看不到物体,灯光比较暗,物体也比较暗。在threejs中,咱们用网格模型Mesh
模拟生活中物体,所以threejs中模拟光照Light
对物体表面的影响,就是模拟光照Light
对网格模型Mesh
表面的影响。
你可以打开课件中案例源码,对比有光照和无光照两种情况,网格模型Mesh
表面的差异。
受光照影响材质
threejs提供的网格材质,有的受光照影响,有的不受光照影响。
基础网格材质 MeshBasicMaterial (opens new window)不会受到光照影响。
arduino
//MeshBasicMaterial不受光照影响
const material = new THREE.MeshBasicMaterial();
漫反射网格材质 MeshLambertMaterial (opens new window)会受到光照影响,该材质也可以称为Lambert网格材质,音译为兰伯特网格材质。
一个立方体长方体使用MeshLambertMaterial材质,不同面和光线夹角不同,立方体不同面就会呈现出来不同的明暗效果。
arduino
//MeshLambertMaterial受光照影响
const material = new THREE.MeshLambertMaterial();
光源简介
Three.js提供了多种模拟生活中光源的API,文档搜索关键词light就可以看到。
点光源
点光源PointLight (opens new window)可以类比为一个发光点,就像生活中一个灯泡以灯泡为中心向四周发射光线。
arduino
//点光源:两个参数分别表示光源颜色和光照强度
// 参数1:0xffffff是纯白光,表示光源颜色
// 参数2:1.0,表示光照强度,可以根据需要调整
const pointLight = new THREE.PointLight(0xffffff, 1.0);
除了通过THREE.PointLight
的参数2设置光照强度,你可以可以直接访问光照强度属性.intensity
设置。
ini
pointLight.intensity = 1.0;//光照强度
光源衰减
实际生活中点光源,比如比如一个灯泡,随机距离的改变,光线会衰减,越来越弱,光源衰减属性.decay
默认值是2.0,如果你不希望衰减可以设置为0.0
。
ini
pointLight.decay = 0.0;//设置光源不随距离衰减
【扩展提醒】:如果使用默认衰减2.0
,不同版本可能有差异,对于部分threejs新版本,有时候你可能看不到光源效果,这时候可以把光照强度加强试试看,如果你的版本不影响,就不用加强光照强度(根据版本情况灵活对应)。
ini
// 你可以对比不同光照强度明暗差异(传播同样距离)
pointLight.intensity = 10000.0;//光照强度
pointLight.intensity = 50000.0;//光照强度
光源位置
你把点光源想象为一个电灯泡,你在3D空间中,放的位置不同,模型的渲染效果就不一样。
注意光源位置尺寸大小:如果你希望光源照在模型的外表面,那你就需要把光源放在模型的外面。
arduino
//点光源位置
pointLight.position.set(400, 0, 0);//点光源放在x轴上
光源添加到场景
光源和网格模型Mesh对应一样是三维场景的一部分,自然需要添加到三维场景中才能起作用。
csharp
scene.add(pointLight); //点光源添加到场景中
设置好上面所有代码,你现在可以执行代码,用浏览器查看渲染效果。
改变光源位置,观察网格模型表面的明暗变化。
arduino
pointLight.position.set(400, 200, 300);
相机控件OrbitControls
平时开发调试代码,或者展示模型的时候,可以通过相机控件OrbitControls实现旋转缩放预览效果。
OrbitControls使用
你可以打开课件案例源码测试下效果。
- 旋转:拖动鼠标左键
- 缩放:滚动鼠标中键
- 平移:拖动鼠标右键
引入扩展库OrbitControls.js
javascript
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
注意:如果你在原生.html文件中,使用上面引入方式import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
,注意通过<script type="importmap">
配置。
xml
<script type="importmap">
{
"imports": {
"three": "../../../three.js/build/three.module.js",
"three/addons/": "../../../three.js/examples/jsm/"
}
}
</script>
使用OrbitControls
javascript
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件
必须传入2个参数:
相机,让哪一个相机围绕目标运动。默认目标是原点。立方体在原点处。
渲染的画布dom对象,用于监听鼠标事件控制相机的围绕运动。
OrbitControls本质
OrbitControls本质上就是改变相机的参数,比如相机的位置属性,改变相机位置也可以改变相机拍照场景中模型的角度,实现模型的360度旋转预览效果,改变透视投影相机距离模型的距离,就可以改变相机能看到的视野范围。
javascript
controls.addEventListener('change', function () {
// 浏览器控制台查看相机位置变化
console.log('camera.position',camera.position);
});
平行光与环境光
通过平行光DirectionalLight (opens new window)和环境光AmbientLight (opens new window)进一步了解光照对应模型Mesh表面的影响。
点光源辅助观察PointLightHelper
通过点光源辅助观察对象PointLightHelper (opens new window)可视化点光源。
预览观察:可以借助相机控件OrbitControls
旋转缩放三维场景便于预览点光源位置
csharp
// 光源辅助观察
const pointLightHelper = new THREE.PointLightHelper(pointLight, 10);
scene.add(pointLightHelper);
改变点光源位置,观察光照效果变化。
arduino
pointLight.position.set(100, 60, 50);
// 改变点光源位置,使用OrbitControls辅助观察
pointLight.position.set(-400, -200, -300);
环境光设置
环境光AmbientLight (opens new window)没有特定方向,只是整体改变场景的光照明暗。
csharp
//环境光:没有特定方向,整体改变场景的光照明暗
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
平行光
平行光DirectionalLight (opens new window)就是沿着特定方向发射。
ini
// 平行光
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)可视化点光源。
csharp
// DirectionalLightHelper:可视化平行光
const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5,0xff0000);
scene.add(dirLightHelper);
平行光与Mesh表面光线反射规律
平行光照射到网格模型Mesh表面,光线和模型表面构成一个入射角度,入射角度不同,对光照的反射能力不同。
光线照射到漫反射网格材质 MeshLambertMaterial (opens new window)对应Mesh表面,Mesh表面对光线反射程度与入射角大小有关。
arduino
// 对比不同入射角,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
scss
// requestAnimationFrame实现周期性循环执行
// requestAnimationFrame默认每秒钟执行60次,但不一定能做到,要看代码的性能
let i = 0;
function render() {
i+=1;
console.log('执行次数'+i);
requestAnimationFrame(render);//请求再次执行函数render
}
render();
备注说明 :对于部分高刷新率的电脑硬件设备,.requestAnimationFrame
每秒钟默认调用函数执行次数也是有可能超过60次的,比如你的电脑显卡、显示器等硬件能够支持144hz刷新频率,.requestAnimationFrame
的每秒执行上限,也可以接近144帧率。
threejs旋转动画
动画说白了就是一张张照片,连起来依次展示,这样就形成一个动画效果,只要帧率高,人的眼睛就感觉不到卡顿,是连续的视频效果。
scss
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();
计算两帧渲染时间间隔和帧率
scss
// 渲染循环
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);
。
Canvas画布布局和全屏
threejs渲染输出的结果就是一个Cavnas画布,canvas画布也是HTML的元素之一,这意味着three.js渲染结果的布局和普通web前端习惯是一样的。
通过renderer.domElement
属性可以访问threejs的渲染结果,也就是HTML的元素canvas
画布。
非全屏局部布局
你可以把threejs的渲染结果renderer.domElement
,插入到web页面上任何一个元素中,只要符合你项目的布局规则即可。
arduino
<div id="webgl" style="margin-top: 100px;margin-left: 200px;"></div>
<script type="module">
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = 800; //宽度
const height = 500; //高度
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
/**
* 创建渲染器对象
*/
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
renderer.render(scene, camera); //执行渲染操作
//three.js执行渲染命令会输出一个canvas画布,也就是一个HTML元素,你可以插入到web页面中
// document.body.appendChild(renderer.domElement);
document.getElementById('wegbl').appendChild(renderer.domElement);
全屏渲染
ini
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = window.innerWidth; //窗口文档显示区的宽度作为画布宽度
const height = window.innerHeight; //窗口文档显示区的高度作为画布高度
const renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
全屏布局注意CSS的设置。
xml
<style>
body{
overflow: hidden;
margin: 0px;
}
</style>
canvas画布宽高度动态变化
canvas画布宽高度动态变化,需要更新相机和渲染的参数,否则无法正常渲染。
javascript
// onresize 事件会在窗口被调整大小时发生
window.onresize = function () {
// 重置渲染器输出画布canvas尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 全屏情况下:设置观察范围长宽比aspect为窗口宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
// 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
// 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
camera.updateProjectionMatrix();
};
ini
// 导入threejs
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
1000 // 远平面
);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建几何体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const parentMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
// 创建网格
let parentCube = new THREE.Mesh(geometry, parentMaterial);
const cube = new THREE.Mesh(geometry, material);
parentCube.add(cube);
parentCube.position.set(-3, 0, 0);
parentCube.rotation.x = Math.PI / 4;
// parentCube.scale.set(2, 2, 2);
// cube.position.x = 2;
cube.position.set(3, 0, 0);
// 设置立方体的放大
// cube.scale.set(2, 2, 2);
// 绕着x轴旋转
cube.rotation.x = Math.PI / 4;
// 将网格添加到场景中
scene.add(parentCube);
// 设置相机位置
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
// 添加世界坐标辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 添加轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置带阻尼的惯性
controls.enableDamping = true;
// 设置阻尼系数
controls.dampingFactor = 0.05;
// 设置旋转速度
// controls.autoRotate = true;
// 渲染函数
function animate() {
controls.update();
requestAnimationFrame(animate);
// 旋转
// cube.rotation.x += 0.01;
// cube.rotation.y += 0.01;
// 渲染
renderer.render(scene, camera);
}
animate();
// 监听窗口变化
window.addEventListener("resize", () => {
// 重置渲染器宽高比
renderer.setSize(window.innerWidth, window.innerHeight);
// 重置相机宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 更新相机投影矩阵
camera.updateProjectionMatrix();
});
var btn = document.createElement("button");
btn.innerHTML = "点击全屏";
btn.style.position = "absolute";
btn.style.top = "10px";
btn.style.left = "10px";
btn.style.zIndex = "999";
btn.onclick = function () {
// 全屏
document.body.requestFullscreen();
console.log("全屏");
};
document.body.appendChild(btn);
// 退出全屏的按钮
var exitBtn = document.createElement("button");
exitBtn.innerHTML = "退出全屏";
exitBtn.style.position = "absolute";
exitBtn.style.top = "10px";
exitBtn.style.left = "100px";
exitBtn.style.zIndex = "999";
exitBtn.onclick = function () {
// 退出全屏
document.exitFullscreen();
console.log("退出全屏");
};
document.body.appendChild(exitBtn);
WebGL渲染器设置(锯齿模糊)
一般实际开发,threejs的WebGL渲染器需要进行一些通用的基础配置,给大家简单介绍下,比如渲染模糊或锯齿问题。
渲染器锯齿属性.antialias
设置渲染器锯齿属性.antialias
的值可以直接在参数中设置,也可通过渲染器对象属性设置。
php
const renderer = new THREE.WebGLRenderer({
antialias:true,
});
或
ini
renderer.antialias = true,
设备像素比window.devicePixelRatio
如果你有web前端基础,应该了解window
对象,设备像素比 .devicePixelRatio
是window对象的一个属性,该属性的值和你的硬件设备屏幕 相关,不同硬件设备的屏幕window.devicePixelRatio
的值可能不同,可能就是1、1.5、2.0等其它值。
javascript
// 不同硬件设备的屏幕的设备像素比window.devicePixelRatio值可能不同
console.log('查看当前屏幕设备像素比',window.devicePixelRatio);
设置设备像素比.setPixelRatio()
如果你遇到你的canvas画布输出模糊问题,注意设置renderer.setPixelRatio(window.devicePixelRatio)
。
注意:注意你的硬件设备设备像素比window.devicePixelRatio
刚好是1,那么是否执行.setPixelRatio()
不会有明显差异,不过为了适应不同的硬件设备屏幕,通常需要执行该方法。
javascript
// 获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
设置背景颜色.setClearColor()
scss
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地址:github.com/dataarts/da...
npm地址:www.npmjs.com/package/dat...
你可以通过npm或github方式获得dat.gui.js库,当然为了学习方便,threejs官方案例扩展库中也提供了gui.js,你可以直接使用。
javascript
// 引入dat.gui.js的一个类GUI
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
#创建一个GUI对象
创建一个GUI对象,你可以看到浏览器右上角多了一个交互界面,GUI本质上就是一个前端js库。
arduino
// 实例化一个gui对象
const gui = new GUI();
.domElement
:改变GUI界面默认的style属性
通过.domElement
属性可以获取gui界面的HTML元素,那就意味着你可以改变默认的style样式,比如位置、宽度等。
ini
//改变交互界面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属性的新交互界面。
csharp
//创建一个对象,对象属性的值可以被GUI库创建的交互界面改变
const obj = {
x: 30,
};
// gui增加交互界面,用来改变obj对应属性
gui.add(obj, 'x', 0, 100);
体验.add()
功能------改变对象属性值
为了方便观察.add()
是如何改变JavaScript对象属性的,可以浏览器控制台不停地打印obj的值,这样通过gui界面拖动改变obj对象属性的的时候,便于观察obj的变化。
javascript
const obj = {x: 30};
setInterval(function () {
console.log('x', obj.x);
}, 10)
gui改变js对象多个属性
csharp
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拖动条改变该属性。
arduino
// 光照强度属性.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的位置。
arduino
gui.add(mesh.position, 'x', 0, 180);
gui.add(mesh.position, 'y', 0, 180);
gui.add(mesh.position, 'z', 0, 180);
gui调试界面2-颜色命名等
给大家介绍gui.js库更多的方法。
.name()
方法
.add()
创建的交互界面,会默认显示所改变属性的名字,为了通过交互界面更好理解你改变的某个对象属性,你可以通过.name()
方法改变gui生成交互界面显示的内容。
csharp
const gui = new GUI();//创建GUI对象
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度');
gui.add(directionalLight, 'intensity', 0, 2.0).name('平行光强度');
步长.step()
方法
步长.step()
方法可以设置交互界面每次改变属性值间隔是多少。
csharp
gui.add(ambient, 'intensity', 0, 2.0).name('环境光强度').step(0.1);
.onChange()
方法
当gui界面某个值的时候,.onChange()
方法就会执行,这时候你可以根据需要通过.onChange()
执行某些代码。
ini
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()
生成颜色值改变的交互界面
ini
const obj = {
color:0x00ffff,
};
// .addColor()生成颜色值改变的交互界面
gui.addColor(obj, 'color').onChange(function(value){
mesh.material.color.set(value);
});
gui调试3-下拉菜单、单选框
前面大家学过通过.add()
方法可以添加一个拖动条用来改变对象的某个属性,本节课给大家介绍.add()
方法创建新的UI交互界面,比如下拉菜单、单选框。
.add()
方法参数3和4数据类型:数字
格式:add(控制对象,对象具体属性,其他参数)
其他参数,可以一个或多个,数据类型也可以不同,gui会自动根据参数形式,自动生成对应的交互界面。
参数3和参数4,分别是一个数字 ,交互界面是一个鼠标可以拖动的拖动条,可以在一个区间改变属性的值
ini
// 参数3、参数4数据类型:数字(拖动条)
gui.add(obj, 'x', 0, 180).onChange(function (value) {
mesh.position.x = value;
});
.add()
方法参数3数据类型:数组
参数3是一个数组,生成交互界面是下拉菜单
ini
const obj = {
scale: 0,
};
// 参数3数据类型:数组(下拉菜单)
gui.add(obj, 'scale', [-100, 0, 100]).name('y坐标').onChange(function (value) {
mesh.position.y = value;
});
.add()
方法参数3数据类型:对象
参数3是一个对象,生成交互界面是下拉菜单
javascript
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()
改变属性的对应的数据类型如果是布尔值,那么交互界面就是一个单选框。
csharp
const obj = {
bool: false,
};
// 改变的obj属性数据类型是布尔值,交互界面是单选框
gui.add(obj, 'bool').name('是否旋转');
javascript
gui.add(obj, 'bool').onChange(function (value) {
// 点击单选框,控制台打印obj.bool变化
console.log('obj.bool',value);
});
.add()
方法改变布尔值案例
控制一个对象是否旋转。
scss
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交互界面不分组
gui交互界面不分组,只有一个默认的总的菜单。
csharp
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()
等等属性。
javascript
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);
});
csharp
// 环境光子菜单
const ambientFolder = gui.addFolder('环境光');
// 环境光强度
ambientFolder.add(ambient, 'intensity',0,2);
csharp
// 平行光强度
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()
创建的子菜单都可以用代码控制交互界面关闭或开展状态。
go
const gui = new GUI(); //创建GUI对象
gui.close();//关闭菜单
go
// 创建材质子菜单
const matFolder = gui.addFolder('材质');
matFolder.close();//关闭菜单
.addFolder()
套娃------子菜单嵌套子菜单
.addFolder()
创建的对象,同样也具有.addFolder()
属性,可以继续嵌套子菜单。
csharp
// 平行光子菜单
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);
scss
// 创建GUI
const gui = new GUI();
// 添加按钮
gui.add(eventObj, "Fullscreen").name("全屏");
gui.add(eventObj, "ExitFullscreen").name("退出全屏");
// 控制立方体的位置
// gui.add(cube.position, "x", -5, 5).name("立方体x轴位置");
let folder = gui.addFolder("立方体位置");
folder
.add(cube.position, "x")
.min(-10)
.max(10)
.step(1)
.name("立方体x轴位置")
.onChange((val) => {
console.log("立方体x轴位置", val);
});
folder
.add(cube.position, "y")
.min(-10)
.max(10)
.step(1)
.name("立方体y轴位置")
.onFinishChange((val) => {
console.log("立方体y轴位置", val);
});
folder.add(cube.position, "z").min(-10).max(10).step(1).name("立方体z轴位置");
gui.add(parentMaterial, "wireframe").name("父元素线框模式");
let colorParams = {
cubeColor: "#00ff00",
};
gui
.addColor(colorParams, "cubeColor")
.name("立方体颜色")
.onChange((val) => {
cube.material.color.set(val);
});
参考资料:Threejs中文网