常用three.js
x 左右
y 上下
z 前后
const model = new THREE.Group();
// 创建边界框可视化辅助对象,颜色为黄色
const box = new THREE.Box3().setFromObject(模型);
const helper = new THREE.Box3Helper(box, 0xffff00)
const box3 = new THREE.Box3().setFromObject(模型);
const box3Helper = new THREE.Box3Helper(box3, 0xff0000);
scene.add(box3Helper);
背景色
const renderer = new THREE.WebGLRenderer({
antialias: true, //设置抗锯齿
alpha:false, //透明开启与关闭
logarithmicDepthBuffer:true, //能解决部分模型冲突闪烁的问题
});
renderer.setSize(Width, Height);
renderer.setPixelRatio(window.devicePixelRatio) //告诉three.js浏览器的像素比,防止模糊
document.getElementById("dineas").appendChild(renderer.domElement);
//设置背景色
renderer.setClearColor(0x444444) //设置背景色
renderer.setClearColor(0x444444,0) //也可以加透明
//透明度
renderer.setClearAlpha(0)
轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// 引入轨道控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement );
//创建轨道控制器(相机,渲染器)
//创建动画
animate() {
requestAnimationFrame(this.animate);
this.renderer.render(this.scene, this.camera);
},
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 修改相机指向的位置
controls.target.set(0, 0, 0)
controls.update()
controls.addEventListener('change',function(e){
console.log('camera.position',camera.position);
})
轨道控制器的属性
controls.enablePan = false
// 禁止右键平移(模型true)
controls.enableZoom = false
// 禁止缩放
controls.enableRotate = false
//禁止右键旋转
controls.minDistance = 200
//缩放的最小
controls.maxDistance = 500
//缩放的最大
controls.addEventListener("change",function(){
// 相机位置与目标观察点的距离
const dis = controls.getDistance()
console.log(dis)
})
controls.minPolarAngle = 0
//上下旋转的范围
controls.maxPolarAngle = Math.PI/2
//上下旋转的范围
controls.minAzimuthAngle = 0
//左右旋转的范围
controls.maxAzimuthAngle = Math.PI/2
//左右旋转的范围
添加背景图
var urls = ['1.png', '1.png','1.png', '1.png','1.png', '1.png',];
scene.background = new THREE.CubeTextureLoader().setPath('/').load(urls)
添加雾
scene.fog = new THREE.Fog( 0xcccccc, 10, 15 );
网格坐标辅助对象.
this.gridHelper = new THREE.GridHelper();
this.scene.add( this.gridHelper );
3维坐标轴
const axesHelper = new THREE.AxesHelper( 5 );
this.scene.add( axesHelper );
获取网格模型的世界坐标
const posit = new THREE.Vector3()
mesh.getWorldPosition(posit)
console.log(posit,"模型的世界坐标")
移动物体或相机
this.物体.position.set(0,3,0)
this.物体.position.x = 1
this.物体.position.z = 1
this.物体.position.y = 1
缩放物体
this.物体.scale.set(1,2,3)
this.物体.scale.x = 1
this.物体.scale.z = 1
this.物体.scale.y = 1
旋转物体
this.物体.rotation.set(Math.PI/4,0,0)
//Math.PI圆周率
this.物体.rotation.set(Math.PI/4,0,0,"xzy")
//可添加旋转顺序
this.物体.rotation.x = 1
this.物体.rotation.z = 1
this.物体.rotation.y = 1
this.物体.rotateY(0.01)
gsap 通过gsap插件设置动画
npm i gsap //安装
import gsap from 'gsap'; //引入
let animation = gsap.to(this.cube.position, {
z: 5, //方向
duration: 5, //时间
ease: "power1.inOut", //动画运动方式
repeat: -1, //动画次数 -1无限次
yoyo:true, //往返运行
delay:2, //延时时间
onComplete: () => {
console.log("动画结束")
},
onStart: () => {
console.log("动画开始")
}
})
// gsap.to(this.cube.rotation, { y: 2 * Math.PI, duration: 5 })
//监听双击事件
this.renderer.domElement.addEventListener("dblclick",()=>{
if (animation.isActive()) { //判断动画是在运动中还是停止中 true运动中 false停止中
animation.pause() // 设置暂定
}else{
animation.resume() //设置开启动画
}
})
监听页面变化
// 监听页面变化
window.addEventListener("resize",()=>{
console.log("画面变化了")
// 更新摄像头
this.camera.aspect = window.innerWidth / window.innerHeight
// 更新摄像机的投影矩阵
this.camera.updateProjectionMatrix()
// 更新渲染器
this.renderer.setSize(window.innerWidth,window.innerHeight)
// 设置渲染器的像素比
this.renderer.setPixelRatio(window.devicePixelRatio)
})
纹理
//纹理
this.textureLoader = new THREE.TextureLoader() //创建纹理
const doorcolor = this.textureLoader.load('./customerY.png') //加载纹理图
const doorcolor1 = this.textureLoader.load('./customerY.png') //加载纹理图
console.log(doorcolor,"doorcolor")
// doorcolor.offset.x = 0.5 //设置纹理偏移量
doorcolor.center.set(0.5,0.5) //旋转中心点
doorcolor.rotation = Math.PI / 4 //旋转纹理
// 创建物体
this.geometry = new THREE.BoxGeometry(1, 1, 1);
this.material = new THREE.MeshBasicMaterial({
color: 0x00ff00, //物体颜色
map:doorcolor, //物体添加纹理
alphaMap:doorcolor1, //添加透明纹理
transparent:true, //是否透明
side:THREE.DoubleSide //渲染两面
});
this.cube = new THREE.Mesh(this.geometry, this.material);
this.scene.add(this.cube);
性能检测
import Stats from 'three/examples/jsm/libs/stats.module.js'; //性能检测
// 性能检测
this.Stats = new Stats()
this.$refs.container.appendChild(this.Stats.domElement)
animate() {
this.Stats.update()
requestAnimationFrame(this.animate);
},
创建随机数
(Math.random()-0.1)*20
//在20以内随机
渲染器
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true, //设置抗锯齿
});
renderer.setSize(window.innerWidth, window.innerHeight);
//渲染的宽高
renderer.setPixelRatio(window.devicePixelRatio)
//告诉three.js浏览器的像素比,防止模糊
renderer.setClearColor(0x444444)
// 设置渲染器的背景色
document.getElementById("app").appendChild(renderer.domElement);
//把渲染器插入到那个标签下
动画与画布窗口变化
// 渲染循环
function animate() {
controls.update()
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
// 画布跟随窗口变化
window.onresize = function () {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
gui插件
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
//导入插件
const gui = new GUI();
// 实例化一个gui对象
//改变值
gui.add(mesh.position, 'x', 0, 180);
//gui.add(要改变的对象, 要改变的对象名, 改变的最小值, 改变的最大值);
gui.add(obj,'x',0,180).name("测试")
//可以改变显示的名字
gui.add(obj,'x',0,180).step(0.1)
//改变的步数
//监听改变的值
gui.add(obj,'x',0,180).name("测试").onChange((value)=>{
console.log(value)
mesh.position.x = value
})
改变颜色
//改变颜色 addColor
gui.addColor(obj,'color').name("测试1").onChange((value)=>{
mesh.material.color.set(value)
})
设置选择值
//可选择-100,0,100
gui.add(obj,'scale',[-100,0,100]).name("测试").onChange((value)=>{
mesh.position.x = value
})
//可设置名称
gui.add(obj,'scale',{left:-100,centter:0,right:100}).name("测试").onChange((value)=>{
mesh.position.x = value
})
//可中文设置
gui.add(obj,'scale',{左:-100,中:0,右:100}).name("测试").onChange((value)=>{
mesh.position.x = value
})
设置单选
gui.add(obj,'danxua').name('是否').onChange((value)=>{
console.log(value)
})
设置分组
const mat = gui.addFolder("材质")
// 设置分组
mat.close()
// 设置默认关闭
mat.add(mesh.position,'y',0,1000)
// 在mat分组上设置
光源与受光源物体
受光照影响材质
光源
点光源
// 创建点光源
const pointLight = new THREE.PointLight(0xffffff,1,100 )
pointLight.position.set( 80,80, 80 );//偏移光源位置,观察渲染效果变化
pointLight.intensity = 38100;//光照强度
pointLight.distance = 300;// 光的衰减
pointLight.decay = 2;//光照强度
scene.add( pointLight );
环境光
// 环境光
const light = new THREE.AmbientLight( 0x404040 ,4); // 柔和的白光
scene.add( light );
平行光
// 平行光
const directionalLight = new THREE.DirectionalLight( 0xffffff, 1 );
// 创建平行光
directionalLight.position.set(100,100,100)
// 创建平行光位置
directionalLight.target = mesh
// 设置平行光执行的物体
scene.add( directionalLight );
聚光源
// // // 聚光源
const spotLight = new THREE.SpotLight(0xffffff,40)
// //创建聚光源,参数一:颜色,参数二:光照强度
spotLight.castShadow = true;
spotLight.angle = Math.PI/15
// //设置聚光源发散角度
spotLight.distance = 300
spotLight.decay = 0
scene.add( spotLight );
spotLight.position.set(0,180,0)
//设置聚光源的位置
spotLight.target.position.set(0,0,0)
// 设置聚光源照的位置
scene.add(spotLight.target)
// 设置聚光源照添加到场景中
几何体
// 生成一个缓冲几何体(可以自定义形状)
const geometry = new THREE.BufferGeometry()
// 创建类型化数组(三个顶点等于一个坐标)
const vertices = new Float32Array([
0,0,0,
50,0,0,
0,100,0,
0,0,10,
0,0,100,
50,0,10
])
const attribute = new THREE.BufferAttribute(vertices,3)
// 属性缓冲对象表示
console.log(attribute)
geometry.attributes.position = attribute
// 设置几何体的顶点位置属性
const material = new THREE.PointsMaterial({color:0xffff00,size:10})
// 创建点材质点材质
const points = new THREE.Points(geometry,material)
// 创建点模型
const line1 = new THREE.Line(geometry,material)
// 创建线模型(会顺着链接起来,但是最后点和开始点不会链接)
const line2 = new THREE.LineLoop(geometry,material)
// 创建线模型(会顺着链接起来,会闭合线条)
const line3 = new THREE.LineSegments(geometry,material)
// 创建线模型(非连续的线条)
scene.add(line3)
点模型
const points = new THREE.Points(geometry,material)
线模型(会顺着链接起来,但是最后点和开始点不会链接)
const line1 = new THREE.Line(geometry,material)
线模型(会顺着链接起来,会闭合线条)
const line2 = new THREE.LineLoop(geometry,material)
线模型(非连续的线条)
const line3 = new THREE.LineSegments(geometry,material)
通过网格模型渲染
// 生成一个缓冲几何体(可以自定义形状)
const geometry = new THREE.BufferGeometry()
// 创建类型化数组(三个顶点等于一个坐标)
const vertices = new Float32Array([
0,0,0,
50,0,0,
0,100,0,
0,0,10,
0,0,100,
50,0,10
])
const attribute = new THREE.BufferAttribute(vertices,3)
// 属性缓冲对象表示
console.log(attribute)
geometry.attributes.position = attribute
// 设置几何体的顶点位置属性
const material = new THREE.MeshBasicMaterial({
color:0xffff00,
// side:THREE.FrontSide //默认只有正面可见
side:THREE.DoubleSide //两面可见
// side:THREE.FrontSide //背面可见
})
// 创建网格材质
const mesh = new THREE.Mesh(geometry, material);
// 通过网格模型渲染
通过类型化创建
const vertices = new Float32Array([
0,0,0,
50,0,0,
0,100,0,
50,100,0,
])
const attribute = new THREE.BufferAttribute(vertices,3)
const indexes = new Uint16Array([0,1,2,1,3,2])
// 0表述第一组,1表述第二组,2表示第三组,3表述第四组
//0 = 0,0,0 || 1=50,0,0 || 2=0,100,0 || 3=50,100,0
geometry.index = new THREE.BufferAttribute(indexes,1)
const mesh = new THREE.Mesh(geometry, material);
旋转、缩放、平移几何体
居中
geometry.translate(50, 0, 0);//偏移
// 居中:已经偏移的几何体居中,执行.center(),你可以看到几何体重新与坐标原点重合
geometry.center();
角度
cube.rotation.y = Math.PI/4
//旋转角度
设置颜色
material.color.set(0x005001) //十六进制设置颜色
material.color.set('#005001') //css设置颜色
克隆
const cube2 = cube.clone() //克隆
复制
cube2.rotation.copy(cube.rotation) //复制
层级
创建组对象
const geometry = new THREE.BoxGeometry( 100, 100, 100 );
const material = new THREE.MeshBasicMaterial( {color:0xff000f});
const cube1 = new THREE.Mesh( geometry, material );
const cube2 = new THREE.Mesh( geometry, material );
cube2.translateX(150)
// 创建组对象
const group = new THREE.Group()
group.add(cube1) //把物体加入组里
group.add(cube2)
group.add(cube1,cube2) //可以同时添加多个
group.translateX(150) //可以操作组对象,操作组对象,子对象也会同时操作
scene.add(group) //把组放到场景中
递归查找所有的子对象
// 递归循环所有模型节点
group3.traverse((obj)=>{
// 判断是否是网格模型
if (obj.type == "Mesh") {
// 是网格模型则修改颜色
obj.material.color.set(0xffffff)
}
console.log(obj,"obj")
})
查找指定模型名称
const obj = group3.getObjectByName("4号楼")
const obj = gltf.scene.getObjectByName("外壳01")
示例
const group = new THREE.Group()
const group2 = new THREE.Group()
const group3 = new THREE.Group()
group.name = '高层'
for (let i = 0; i < 5; i++) {
const geometry = new THREE.BoxGeometry( 20, 60, 10 );
const material = new THREE.MeshBasicMaterial( {color:0xff000f});
const cube = new THREE.Mesh( geometry, material );
cube.name = (i+1)+"号楼"
cube.translateX(i*40)
cube.translateY(70)
group.add(cube)
}
group2.name = '洋房'
for (let i = 0; i < 5; i++) {
const geometry = new THREE.BoxGeometry( 20, 30, 10 );
const material = new THREE.MeshBasicMaterial( {color:0xff000f});
const cube = new THREE.Mesh( geometry, material );
cube.name = (i+6)+"号楼"
cube.translateX(i*40)
cube.translateZ(40)
group2.add(cube)
}
group3.name = '小区'
group3.add(group,group2)
//可以同时添加多个
scene.add(group3) //把组放到场景中
console.log('group', JSON.parse(JSON.stringify(group)))
// 递归循环所有模型节点
group3.traverse((obj)=>{
// 判断是否是网格模型
if (obj.type == "Mesh") {
// 是网格模型则修改颜色
obj.material.color.set(0xffffff)
}
console.log(obj,"obj")
})
// 查找指定模型名称
const obj = group3.getObjectByName("4号楼")
obj.material.color.set(0xfff00f)
添加局部坐标
const Helper = new THREE.AxesHelper(50);
cube.add(Helper);
改变物体原点
const geometry = new THREE.BoxGeometry( 20, 20, 20 );
const material = new THREE.MeshLambertMaterial( {color:0xff000f});
const cube = new THREE.Mesh( geometry, material );
geometry.translate(20/2,0,0)
// 改变物体原点
删除子类
const geometry = new THREE.BoxGeometry( 20, 20, 20 );
const material = new THREE.MeshLambertMaterial( {color:0xff000f});
const cube = new THREE.Mesh( geometry, material );
const cube1 = new THREE.Mesh( geometry, material );
cube1.position.x = 50
const group = new THREE.Group()
group.add(cube,cube1)
group.remove(cube)
// 删除
显示隐藏
cube1.visible = false
// 隐藏,可以隐藏材质
纹理
创建纹理贴图
const geometry = new THREE.SphereGeometry(50);
// 创建一个纹理加载器
const loadtex = new THREE.TextureLoader()
// 加载图片
const texture = loadtex.load('/earth.jpg')
const material = new THREE.MeshLambertMaterial({
// color: 0xff000f,
// map:texture,//设置材质的颜色贴图
});
material.map = texture; //可以直接访问材质属性设置
const cube = new THREE.Mesh(geometry, material);
自定义uv坐标
// 创建一个纹理加载器
const loadtex = new THREE.TextureLoader()
// 加载图片
const texture = loadtex.load('/earth.jpg')
// 生成一个缓冲几何体(可以自定义形状)
const geometry = new THREE.BufferGeometry()
// 创建类型化数组(三个顶点等于一个坐标)
const vertices = new Float32Array([
0,0,0,
160,0,0,
160,80,0,
0,80,0,
])
const attribute = new THREE.BufferAttribute(vertices,3)
// 属性缓冲对象表示
console.log(attribute)
geometry.attributes.position = attribute
// 设置几何体的顶点位置属性
// Uint16Array类型数组创建顶点索引数据
const indexes = new Uint16Array([0,1,2,0,2,3])
geometry.index = new THREE.BufferAttribute(indexes,1)
const material = new THREE.MeshBasicMaterial({
map:texture,
})
// 创建网格材质
const mesh = new THREE.Mesh(geometry, material);
// 创建uv坐标
const uvs = new Float32Array([
0,0,
1,0,
1,1,
0,1
])
// 通过几何体的顶点位置设置uv坐标
geometry.attributes.uv = new THREE.BufferAttribute(uvs,2)
scene.add(mesh)
顶点UV坐标可以在0~1.0之间任意取值,纹理贴图左下角对应的UV坐标是(0,0)
,右上角对应的坐标(1,1)
。
纹理对象阵列(地转)
const scene = new THREE.Scene();
const gui = new GUI()
const geometry = new THREE.PlaneGeometry(800,800);
// const geometry = new THREE.CircleGeometry( 5, 32 );
// const geometry = new THREE.SphereGeometry(50);
geometry.rotateX(Math.PI/2)
// 创建一个纹理加载器
const loadtex = new THREE.TextureLoader()
// // 加载图片
const texture = loadtex.load('/瓷砖.jpg')
//阵列
texture.wrapS = THREE.RepeatWrapping //上下开启阵列
texture.wrapT = THREE.RepeatWrapping //左右开启阵列
texture.repeat.set(30,30) //上下左右的阵列次数
const material = new THREE.MeshLambertMaterial({
side:THREE.DoubleSide,
map:texture,//设置材质的颜色贴图
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)
开启透明
// 创建一个纹理加载器
const loadtex = new THREE.TextureLoader()
// 加载图片
const texture = loadtex.load('/转弯.png')
const material = new THREE.MeshLambertMaterial({
map:texture,//设置材质的颜色贴图
transparent:true, //开启透明
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)
uv偏移量
const geometry = new THREE.PlaneGeometry(50,50);
// 创建一个纹理加载器
const loadtex = new THREE.TextureLoader()
// 加载图片
const texture = loadtex.load('/转弯.png')
const material = new THREE.MeshLambertMaterial({
map:texture,//设置材质的颜色贴图
transparent:true, //开启透明
});
// uv偏移量
texture.offset.x += 0.5
texture.offset.y += 0.5
texture.wrapS = THREE.RepeatWrapping //被裁掉的自动追加到后面
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)
加载模型
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; //引入GLTF加载器
const scene = new THREE.Scene();
//创建一个GLTF加载器
const loader = new GLTFLoader()
//声明一个组对象,用来添加加载成功的三维场景
const model = new THREE.Group()
//gltf加载成功后返回一个对象
loader.load('/工厂.gltf',(gltf)=>{
console.log(gltf,"gltf")
//三维场景添加到model组对象中
model.add(gltf.scene)
})
scene.add(model)
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 修改相机指向的位置
controls.target.set(-14,-24,10)
controls.update()
单独给贴纸
//gltf加载成功后返回一个对象
loader.load('/工厂.gltf',(gltf)=>{
console.log(gltf.scene,"gltf")
gltf.scene.traverse((moxin)=>{
if (moxin.isMesh) {
if (moxin.material.map) {
console.log("判断是否存在贴图",moxin.material.map.encoding)
}
}
})
//三维场景添加到model组对象中
model.add(gltf.scene)
})
scene.add(model)
// // 创建一个纹理加载器
const loadtex = new THREE.TextureLoader()
// // // 加载图片
const texture = loadtex.load('/瓷砖.jpg')
texture.encoding = THREE.sRGBEncoding
// 反转贴图
texture.flipY = false
renderer.outputColorSpace = THREE.SRGBColorSpace;
PBR材质
MeshStandardMaterial 材质
material.metalness = 1 (0-1)
//金属度 0表述金属度低,1表述金属度高,(值越高越像金属)
material.roughness = 0.5 (0-1)
//粗糙度 0表述表面光滑,1表示粗糙 (值越高越粗糙)
// CubeTextureLoader环境贴图,setPath加载的文件夹,load加载的文件,
// 加载环境贴图
// 加载周围环境6个方向贴图
// 上下左右前后6张贴图构成一个立方体空间
// 'px.jpg', 'nx.jpg':x轴正方向、负方向贴图 p:正positive n:负negative
// 'py.jpg', 'ny.jpg':y轴贴图
// 'pz.jpg', 'nz.jpg':z轴贴图
const texturecube = new THREE.CubeTextureLoader().setPath('/环境贴图/环境贴图0/')
.load(['px.jpg','nx.jpg','py.jpg','ny.jpg','pz.jpg','nz.jpg',])
moxin.material.envMap = texturecube
//环境贴图(给某个物体添加环境贴图)
moxin.material.envMapIntensity = 1
//环境贴图的影响程度
给全部物体添加贴图
// 环境贴图纹理对象textureCube作为.environment属性值,影响所有模型
scene.environment = textureCube;
环境贴图色彩空间编码.encoding
//如果renderer.outputEncoding=THREE.sRGBEncoding;环境贴图需要保持一致
textureCube.encoding = THREE.sRGBEncoding;
MeshPhysicalMaterial清漆层
const material = new THREE.MeshPhysicalMaterial( {
clearcoat: 1.0,//物体表面清漆层或者说透明涂层的厚度
clearcoatRoughness: 0.1,//透明涂层表面的粗糙度
color: obj.material.color, //默认颜色
metalness: 0.9,//金属度
roughness: 0.5,//粗糙度
envMap: textureCube, //环境贴图
envMapIntensity: 2.5, //环境贴图对Mesh表面影响程度
} );
//示例
//创建一个GLTF加载器
const loader = new GLTFLoader()
//声明一个组对象,用来添加加载成功的三维场景
const model = new THREE.Group()
//gltf加载成功后返回一个对象
loader.load('/轿车.glb',(gltf)=>{
console.log(gltf,"gltf")
const obj = gltf.scene.getObjectByName("外壳01")
obj.material = new THREE.MeshPhysicalMaterial({
color: obj.material.color, //默认颜色
metalness: 0.9,//车外壳金属度
roughness: 0.5,//车外壳粗糙度
envMap: texturecube, //环境贴图
envMapIntensity: 2.5, //环境贴图对Mesh表面影响程度
clearcoat:1, //物体表面清漆层或者说透明涂层的厚度
clearcoatRoughness:0, //透明涂层表面的粗糙度
})
model.add(gltf.scene)
},function ( xhr ) {
//加载进度
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},)
scene.add(model)
玻璃
const obj1 = gltf.scene.getObjectByName("玻璃01")
obj1.material = new THREE.MeshPhysicalMaterial({
color: obj1.material.color, //默认颜色
metalness: 0,//车外壳金属度
roughness: 0,//车外壳粗糙度
envMapIntensity: 0, //环境贴图对Mesh表面影响程度
transmission: 1.0, //玻璃材质透光率
ior:1.5, ////折射率 非金属材料的折射率从1.0到2.333。默认值为1.5。
})
相机
正投影相机
let Width = window.innerWidth
let Height = window.innerHeight
const k = Width/Height
const s = 500
const camera = new THREE.OrthographicCamera(-s*k,s*k,s,-s,1,8000)
camera.position.set(0, 2000, 0);
camera.lookAt(2000, 100, 2000); //相机观察目标指向Three.js坐标系原点
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 修改相机指向的位置
controls.target.set(200, 0, 200)
controls.update()
相机属性
// //相机
const camera = new THREE.PerspectiveCamera(30, Width / Height, 0.1, 3000);
camera.position.set(800, 800, 800); //相机的位置
camera.lookAt(0, 0, 0); //相机看的方向
// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0) //这里要和 camera.lookAt(0, 0, 0)保持同步
controls.update()
包围盒
const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// 包围盒
const box3 = new THREE.Box3()
box3.expandByObject(mesh) //计算的模型
const size = new THREE.Vector3()
box3.getSize(size) //获取模型计算的尺寸
console.log(size,"size")
const center = new THREE.Vector3()
box3.getCenter(center) //获取模型计算的位置
console.log(center,"center")
后期处理
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js"; //效果合成器
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass"; //引入渲染通道
const composer = new EffectComposer(renderer)
//创建效果合成器,renderer渲染器作为参数
const renderpass =new RenderPass(scene,camera)
//创建渲染通道,参数为 scene场景 camera相机
composer.addPass(renderpass)
//效果合成器添加渲染通道
// 渲染循环
function animate() {
renderer.render(scene, camera);
composer.render();
requestAnimationFrame(animate);
}
描边效果
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass"; //引入描边效果
// OutlinePass 创建描边效果 参数一:2维向量 就是画布的尺寸, scene场景 camera相机
const v2 = new THREE.Vector2(Width,Height)
const outlinePass = new OutlinePass(v2,scene,camera)
// 那个模型需要需要描边,可以有多个
outlinePass.selectedObjects = [mesh1]
outlinePass.visibleEdgeColor.set(0xffff00)
// 设置描边的颜色
outlinePass.edgeThickness = 5
// 设置描边的厚度
outlinePass.edgeStrength = 6
// 设置描边的亮度
outlinePass.pulsePeriod = 2
// 设置描边闪烁,0就是不闪烁
// 把描边添加到效果合成器里
composer.addPass(outlinePass)
发光效果
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass";
// unrealBloomPass 创建发光效果
const v2 = new THREE.Vector2(Width,Height)
const unrealBloomPass = new UnrealBloomPass(v2)
unrealBloomPass.strength = 1
composer.addPass(unrealBloomPass)
解决颜色偏差
import { GammaCorrectionShader } from "three/examples/jsm/shaders/GammaCorrectionShader.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass";
const gammaPass = new ShaderPass(GammaCorrectionShader)
composer.addPass(gammaPass)
选择模型
选择单个模型
renderer.domElement.addEventListener('click', function (event) {
// .offsetY、.offsetX以canvas画布左上角为坐标原点,单位px
const px = event.offsetX;
const py = event.offsetY;
//屏幕坐标px、py转WebGL标准设备坐标x、y
//width、height表示canvas画布宽高度
const x = (px / window.innerWidth) * 2 - 1;
const y = -(py / window.innerHeight) * 2 + 1;
//创建一个射线投射器`Raycaster`
const raycaster = new THREE.Raycaster();
//.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
// 形象点说就是在点击位置创建一条射线,射线穿过的模型代表选中
raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
//.intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算
// 未选中对象返回空数组[],选中一个对象,数组1个元素,选中两个对象,数组两个元素
const intersects = raycaster.intersectObjects([mesh1, mesh2, mesh]);
console.log("射线器返回的对象", intersects);
// intersects.length大于0说明,说明选中了模型
if (intersects.length > 0) {
// 选中模型的第一个模型,设置为红色
intersects[0].object.material.color.set(0xff0000);
}
})
选择组模型
const model = new THREE.Group()
const loader = new GLTFLoader()
loader.load('/工厂.gltf', (gltf) => {
console.log(gltf, "gltf")
//三维场景添加到model组对象中
model.add(gltf.scene)
})
scene.add(model)
renderer.domElement.addEventListener('click', function (event) {
const px = event.offsetX;
const py = event.offsetY;
const x = (px / window.innerWidth) * 2 - 1;
const y = -(py / window.innerHeight) * 2 + 1;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
// 固定设置
//首先找到要设置的组模型
const cunshu = model.getObjectByName("存储罐")
// 循环设置自定属性,全部指向父类
for (let i = 0; i < cunshu.children.length; i++) {
const element = cunshu.children[i];
element.traverse(function(obj){
if (obj.isMesh) {
obj.ancestors = element
}
})
}
const intersects = raycaster.intersectObjects(cunshu.children);
composer.addPass(outlinePass)
if (intersects.length > 0) {
//设置发光效果
outlinePass.selectedObjects = [intersects[0].object.aaaaa]
}
})
页面物体标注html内容
添加基础标签
const group = new THREE.Group()
// 创建一个组
let div = document.getElementById('label')
// 选择在页面中创建的div
let label = new CSS2DObject(div)
// 创建css,导入标签
label.position.set(200,200,200)
// 设置标签的位置
group.add(label)
// 添加到组中
scene.add(group);
// 将组添加到场景中
const css2Renderer = new CSS2DRenderer()
// 标签渲染
css2Renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("sanwei").appendChild(css2Renderer.domElement);
css2Renderer.domElement.style.position = 'absolute';
css2Renderer.domElement.style.top = '0px';
css2Renderer.domElement.style.pointerEvents = 'none';
// 添加穿透
// 渲染循环
function animate() {
css2Renderer.render(scene, camera);
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
可以直接标注到模型
let div = document.getElementById('label')
// 选择在页面中创建的div
let label = new CSS2DObject(div)
// 设置标签的位置
mesh.add(label)
// 可以直接标注到某个模型下
标注到模型的某个点上
let div = document.getElementById('label')
// 选择在页面中创建的div
let label = new CSS2DObject(div)
// 设置标签的位置
mesh.add(label)
// 可以直接标注到某个模型下
const pos = mesh.geometry.attributes.position;
// 获取几何体顶点1的xyz坐标,设置标签局部坐标.position属性
label.position.set(pos.getX(0),pos.getY(0),pos.getZ(0));
模型动画
基础动画
import * as THREE from 'three';
const geometry = new THREE.BoxGeometry(50, 50, 50);
const material = new THREE.MeshLambertMaterial({ color: 0x00ffff });
const model = new THREE.Mesh(geometry, material);
model.name = 'box'
// 定义模型名字
const times = [0,3,6] //定义时间线0秒,3秒,5秒
const values = [0,0,0 ,100,0,0 ,0,0,100] //定义每秒的位置
// 设置关键帧数据THREE.KeyframeTrack(参数一:要加入的关键帧,参数二:定义的时间线,参数三:定义的参数位置或改变的状态)
const posKF = new THREE.KeyframeTrack('box.position',times,values)
const colorKF = new THREE.KeyframeTrack('box.material.color',[2, 5],[1, 0, 0, 0, 0, 1])
// 1.3 AnimationClip表示一个关键帧动画,可以基于关键帧数据产生动画效果
// 创建一个clip关键帧动画对象,命名"test",动画持续时间6s
// AnimationClip包含的所有关键帧数据都放到参数3数组中即可
const clip = new THREE.AnimationClip("test",6,[posKF,colorKF ]);
// 包含关键的模型
const mixer = new THREE.AnimationMixer(model)
const clipAction = mixer.clipAction(clip);
clipAction.play()
// 通过Clock对象辅助获取每次loop()执行的时间间隔。
const clock = new THREE.Clock();
// 执行mixer.update()更新播放器AnimationMixer时间数据
function loop() {
requestAnimationFrame(loop);
const frameT = clock.getDelta();
// 更新播放器相关的时间
mixer.update(frameT);
}
loop();
export default model;
循环方式
clipAction.loop = THREE.LoopOnce //只执行一次
clipAction.loop = THREE.LoopRepeat //重复次数为repetitions的值, 且每次循环结束时候将回到起始动作开始下一次循环。
clipAction.loop = THREE.LoopPingPong //重复次数为repetitions的值, 且像乒乓球一样在起始点与结束点之间来回循环。
物体状态停留在动画结束的时候
clipAction.clampWhenFinished = true;
// 物体状态停留在动画结束的时候
停止、播放、暂停
clipAction.play();
//播放动画
clipAction.stop();
//动画停止结束,回到开始状态
clipAction.paused = false //播放
clipAction.paused = true //暂停
//暂停状态
倍速
clipAction.timeScale = 1;//默认
clipAction.timeScale = 2;//2倍速
播放模型动画
const loader = new GLTFLoader()
//声明一个组对象,用来添加加载成功的三维场景
const model = new THREE.Group()
//gltf加载成功后返回一个对象
loader.load('/工厂1.glb', (gltf) => {
console.log(gltf, "gltf")
const clock = new THREE.Clock();
// // 包含关键的模型
const mixer = new THREE.AnimationMixer(gltf.scene)
const clipAction = mixer.clipAction(gltf.animations[0]);
clipAction.loop = THREE.LoopOnce //只执行一次
clipAction.play()
// 监听动画是否播放完成
mixer.addEventListener('finished',function(){
clipAction.reset() //重制播放
})
function loop() {
requestAnimationFrame(loop);
const frameT = clock.getDelta();
// 更新播放器相关的时间
mixer.update(frameT);
}
loop();
//三维场景添加到model组对象中
model.add(gltf.scene)
})
scene.add(model)
动画库tween.js
npm安装
npm i @tweenjs/tween.js@^18
import TWEEN from '@tweenjs/tween.js';
tweenjs基本语法
let pos = { x: 10, z: 10, y: 10 } //基础信息
const tween = new TWEEN.Tween(pos) //创建动画
tween.to({ x: 100, z: 100, y: 100 },2000) // 参数一:{这里面是要改变后的值},参数二:执行的时间
tween.start(); //开始执行
function loop() {
TWEEN.update();//tween更新
requestAnimationFrame(loop);
}
loop();
//第二种写法
const tween = new TWEEN.Tween(pos).to({ x: 100, z: 100, y: 100 },2000).start();
tweenjs改变threejs模型对象位置
//创建一段mesh平移的动画
const tween = new TWEEN.Tween(mesh.position);
//经过2000毫秒,pos对象的x和y属性分别从零变化为100、50
tween.to({x: 100,y: 50}, 2000);
//缓动方式
tween.easing(TWEEN.Easing.Sinusoidal.InOut)
//tween动画开始执行
tween.start();
//tween动画停止
tween.stop()
//tween动画暂停
tween.pause()
//tween动画继续执行
tween.pause()
换个语法形式书写也可以,更简洁
const tween = new TWEEN.Tween(mesh.position).to({x: 100,y: 50}, 2000).start();
const tween = new TWEEN.Tween(mesh.position)
.to({x: 100,y: 50}, 2000)
.easing(TWEEN.Easing.Sinusoidal.InOut)
.start();
Tweenjs回调函数
twwenjs库提供了onStart
、onUpdate
、onComplete
等用于控制动画执行的回调函数。
onStart
:动画开始执行触发onUpdate
:动画执行过程中,一直被调用执行onComplete
:动画正常执行完触发
.onUpdate(function(obj){})
结构中,obj对应的是new TWEEN.Tween(pos)
的参数对象pos。
const tween = new TWEEN.Tween(pos).to({x: 0}, 4000)
// 开始执行:动画片段tween开始执行的时候触发onStart
.onStart(function(obj){
...
})
const theEntire = scene.getObjectByName("区域")
let enlarModel = graychoose(event, theEntire.children, camera, '-档案柜')
console.log(enlarModel, "enlarModel")
console.log(JSON.stringify(enlarModel), "enlarModel")
const options = {
trs: false,
onlyVisible: true,
binary: true,
maxTextureSize: 4096
};
var gltfExporter = new GLTFExporter();
gltfExporter.parse(
enlarModel,
function (result) {
if (result instanceof ArrayBuffer) {
saveArrayBuffer(result, 'scene.glb');
} else {
const output = JSON.stringify(result, null, 2);
console.log(output);
saveString(output, 'scene.gltf');
}
},
function (error) {
console.log('An error happened during parsing', error);
},
options
);
const link = document.createElement('a');
link.style.display = 'none';
document.body.appendChild(link); // Firefox workaround, see #6594
function save(blob, filename) {
link.href = URL.createObjectURL(blob);
link.download = filename;
link.click();
}
function saveArrayBuffer(buffer, filename) {
save(new Blob([buffer], { type: 'application/octet-stream' }), filename);
}
function saveString(text, filename) {
save(new Blob([text], { type: 'text/plain' }), filename);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?