常用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 = 10-1//金属度 0表述金属度低,1表述金属度高,(值越高越像金属)

material.roughness = 0.50-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库提供了onStartonUpdateonComplete等用于控制动画执行的回调函数。

  • 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);
            }

 

posted @   雨落风  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示