下载threejs
版本号更改信息:github.com/mrdoob/thre…
学习链接:www.webgl3d.cn/
版本问题如何对待
查文档的时候就以自己的项目为准
three源码
最常用到的就是docs文档和examples案例
three.js-文件包
└───build——three.js相关库,可以引入你的.html文件中。
│
└───docs——Three.js API文档文件
│───index.html——打开该文件,本地离线方式预览threejs文档
└───examples——大量的3D案例,是你平时开发参考学习的最佳资源
│───jsm——threejs各种功能扩展库
└───src——Three.js引擎的源码,有兴趣可以阅读。
│
└───editor——Three.js的可视化编辑器,可以编辑3D场景
│───index.html——打开应用程序
查看threejs实例
Npm install live-server -g
在根目录运行live-server即可打开
引入threejs
开发环境:使用vue,react引入
学习环境直接使用html引入
开发环境
Npm install three@0.148.0 --save
//引入
import * as THREE from "three"
//引入扩展库
import {OrbitControls} from "three/addons/controls/OribitControls.js"
// 引入扩展库
import {GLTFLoader} from "three/addons/loaders/GLTFLoader.js"
//旧版本引入 例如122 新版本addons替换了examples/jsm
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader.js"
学习环境
<script src="./build/three.js">
//es6引入方式
<script type="module">
import * as THREE from "./build/three.module.js"
</script>
type="importmap" 引入路径
<script type="importmap">
{
"imports":{
"three":"./build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from "three"
</script>
学习threejs的第一步
场景scene,相机camera,渲染器renderer很重要
scene
//创建scene
const scene=new THREE.Scene();
camera
- 透视投影相机:PerspectiveCamera本质上是四棱台
- 立体相机(StereoCamera)
- 正交相机(OrthographicCamera)
- 立方相机(CubeCamera)
还有一个相机辅助和相机数组
- CameraHelper:用于模拟相机视锥体的辅助对象.
- 摄像机阵列(ArrayCamera):多相机
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
//相机位置.position
//相机在Three.js三维坐标系中的位置// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200);
//相机观察目标.lookAt()
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); //坐标原点
camera.lookAt(mesh.position);//指向mesh对应的位置
透视投影相机PerspectiveCamera
:视锥体
透视投影相机的四个参数fov, aspect, near, far
构成一个四棱台3D空间,被称为视 锥体
- near:近裁截面
- far:远裁截面
aspect
:宽高比- fov:视场角度
参数 | 含义 | 默认值 |
---|---|---|
fov | 相机视锥体竖直方向视野角度 | 50 |
aspect | 相机视锥体水平方向和竖直方向长度比,一般设置为Canvas画布宽高比width / height | 1 |
near | 相机视锥体近裁截面相对相机距离 | 0.1 |
far | 相机视锥体远裁截面相对相机距离,far-near构成了视锥体高度方向 | 2000 |
renderer
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
//执行渲染操作
renderer.render(scene, camera);
//显示
document.body.appendChild(renderer.domElement);
抗锯齿
const renderer = new THREE.WebGLRenderer({antialias:true,});
//或
renderer.antialias = true,
设置设备像素比
获取你屏幕对应的设备像素比.devicePixelRatio告诉threejs,以免渲染模糊问题
renderer.setPixelRatio(window.devicePixelRatio);
设置背景颜色.setClearColor()
renderer.setClearColor(0x444444, 1); //设置背景颜色
最后的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="importmap">
{
"imports":{
"three":"../build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
//创建场景
const scene=new THREE.Scene();
//创建一个立方体,添加到场景中
const geometry=new THREE.BoxGeometry(1,1,1);
const material=new THREE.MeshBasicMaterial({color:0xff0000});
const cube=new THREE.Mesh(geometry,material);
scene.add(cube);
//设置相机,透视相机
const camera=new THREE.PerspectiveCamera();
//设置相机位置
camera.position.set(0,10,0);
//设置相机看的位置
camera.lookAt(cube.position);
//设置渲染器
const renderer=new THREE.WebGLRenderer();
//执行渲染器
renderer.render(scene,camera);
//设置大小
// renderer.setSize(300,300)
//显示到页面上
document.body.appendChild(renderer.domElement)
</script>
</body>
</html>
如果你只想写一个demo看到这就可以了
api学习
材质
-
不受光照影响
- MeshBaiscMaterial
-
受光照影响材质
-
漫反射
- MeshLambertMaterial
-
高光
- MeshPhongMaterial
-
物理
- MeshStandardMaterial
- MeshPysicalMaterial
-
- 所有材质:
MeshPhonematerial
// 模拟镜面反射,产生一个高光效果
const material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 20, //高光部分的亮度,默认30
specular: 0x444444, //高光部分的颜色
});
MeshPhonematerial和MeshLambertMaterial 区别
-
MeshPhonematerial可以提供一个镜面反射效果
-
MeshLambertMaterial没有镜面反射效果,向四周反射
光源
- 环境光AmbientLight
- 点光源PointLight
- 平行光:DirectionalLight
- 聚光灯光源 SpotLight
动画
threejs可以借助HTML5的API请求动画帧window.requestAnimationFrame
实现动画渲染,非常的简单,如下
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, opacity: 0.5 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
function render() {
cube.rotateY(0.01);//每次绕y轴旋转0.01弧度
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
计算两帧时间间隔
// 渲染循环
const clock = new THREE.Clock();
function render() {
cube.rotateY(0.01);//每次绕y轴旋转0.01弧度
const spt = clock.getDelta() * 1000;//毫秒
// console.log('两帧渲染时间间隔(毫秒)', spt);
// console.log('帧率FPS', 1000 / spt);
requestAnimationFrame(render);
renderer.render(scene, camera);//周期性执行相机的渲染功能,更新canvas画布的内容
}
物体
几何体
//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);
双面可见
new THREE.MeshBasicMaterial({side: THREE.FrontSide, //默认只有正面可见});
new THREE.MeshBasicMaterial({side: THREE.DoubleSide, //两面可见});
手势操作
相机空间轨道控制器OrbitControl;
- 旋转:拖动鼠标左键
- 缩放:滚动鼠标中键
- 平移:拖动鼠标右键
<script type="importmap">
{
"imports":{
"three":"../build/three.module.js",
"MyThreeCommon":"./js/three_common.js",
"three/addons/":"../jsm/"
}
}
</script>
<script type="module">
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 如果OrbitControls改变了相机参数,重新调用渲染器渲染三维场景
controls.addEventListener('change', function () {
renderer.render(scene, camera); //执行渲染操作
});//监听鼠标、键盘事件
}
工具
stats
性能监控
//引入性能监视器stats.jsimport Stats from 'three/addons/libs/stats.module.js';
//创建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.domElement显示:渲染帧率 刷新频率,一秒渲染次数
stats.setMode(0);//默认模式//stats.domElement显示:渲染周期 渲染一帧多长时间(单位:毫秒ms)
stats.setMode(1);
Gui
常用支持
- 颜色 .addColor
- 名称.name
- 步长.step
- 事件:onChange
- 下拉框,第三个参数为对象或者数组
- 单选框,参数的值为布尔
- 分组.addFolder
let obj = {
color: 0xff0000,
specular: 0x111111,// 材质高光颜色
scale: 0,
isRotate: false,
}
//gui设置
// 通过GUI改变mesh.position对象的xyz属性,.name为名称
//平行光强度
const directFolder=gui.addFolder('平行光');
directFolder.add(DirectionalLight, 'intensity', 0, 2.0).step(0.1).name('平行光强度');
//改变物体位置
const cubeFolder=gui.addFolder('正方体')
const positionFolder=cubeFolder.addFolder('位置');
positionFolder.add(cube.position, 'x', 0, 10).name('球x轴移动').step(0.01);
positionFolder.add(cube.position, 'y', 0, 10).name('球y轴移动').step(0.01);
positionFolder.add(cube.position, 'z', 0, 10).name('球z轴移动').step(0.01);
const materialFolder=cubeFolder.addFolder('材质');
//设置颜色
materialFolder.addColor(obj, 'color').name('颜色').onChange((value) => {
cube.material.color.set(value);
})
//下拉菜单设置
// gui.add(obj,'scale',[-4,0,4]).name('y坐标').onChange((value)=>{
// cube.position.y=value;
// })
cubeFolder.add(obj, 'scale', { '下': -4, '中': 0, '上': 4 }).name('y坐标').onChange((value) => {
cube.position.y = value;
})
//单选框
cubeFolder.add(obj, 'isRotate').name('是否旋转').onChange((value) => {
console.log(value)
})
遇到的问题
如何让threejs适应画布不伸缩
three默认返回的尺寸是300,150;我们设置的正方体的盒子,从下图看就是一个被压扁的盒子,为什么
//设置相机,透视相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
//设置渲染器的大小
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);
需要注意的是:camera的的aspect和renderer的setSize是有关系的,还有就是renderer.setSize要在render之前,不然是白屏
解决了一开始的适配问题,那么当窗口大小改变时,box也会拉伸压缩,使用如下方法:
按照网络上找到的方法设置
window.addEventListener('resize', onWindowResize);
function onWindowResize() {
//设置观察范围长宽比aspect为窗口宽高比
camera.aspect = window.innerWidth / window.innerHeight;
// 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
camera.updateProjectionMatrix();
// // 重置渲染器输出画布canvas尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
}
设置后依然不管用,百思不得其解
最后思考应该没用重新 renderer.render(scene,camera);
看了下官网的方法
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
每一帧都会去渲染scene,camera
其他相机的设置
正交相机适配
// onresize 事件会在窗口被调整大小时发生
window.onresize=function(){
// 重置渲染器输出画布canvas尺寸
renderer.setSize(window.innerWidth,window.innerHeight);
// 重置相机投影的相关参数
k = window.innerWidth/window.innerHeight;//窗口宽高比
camera.left = -s*k;
camera.right = s*k;
camera.top = s;
camera.bottom = -s;
// 渲染器执行render方法的时候会读取相机对象的投影矩阵属性projectionMatrix
// 但是不会每渲染一帧,就通过相机的属性计算投影矩阵(节约计算资源)
// 如果相机的一些属性发生了变化,需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
camera.updateProjectionMatrix ();
}
改变相机lookAt位置没有成功
注意相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数
// const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);const camera = new THREE.PerspectiveCamera(30, width / height, 1, 8000);
camera.position.set(2000, 2000, 2000);// camera.lookAt(0, 0, 0);// 改变相机观察目标点
camera.lookAt(1000, 0, 1000);
// 设置相机控件轨道控制器OrbitControlsconst controls = new OrbitControls(camera, renderer.domElement);// 相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0// console.log('controls.target', controls.target);
controls.target.set(1000, 0, 1000);
controls.update();//update()函数内会执行camera.lookAt(controls.targe)
思考
代码一定要按自己的思路写,不要直接copy官方实例,因为不是你自己写的,有些问题你根本发现不了。因为官网已经给你写好了