安装 three
npm install three --save-dev
安装 控制动画的插件
npm install gsap
gsap文档
安装GUI调节属性
npm install --save dat.gui
纹理素材下载地址
例子1 自转和公转 时间动画 缓冲几何图形
<template>
<div id="container"></div>
</template>
<script>
//引入three包 最新版
import * as THREE from 'three'
//鼠标控制器 需要threejs高版本
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
//时间动画
import gsap from "gsap";
//引入dat.gui
import * as dat from 'dat.gui'
export default {
data() {
return {
scene:null,//场景
camera:null,//相机
renderer:null,//渲染器
container:null,
clock:new THREE.Clock(),//设置时钟
mesh:null, //材质
animate1:null,
samll:null,
};
},
mounted() {
this.init() //初始的方法都放在init
// window.addEventListener('dblclick',()=>{ //监听鼠标双击事件
// if(this.animate1.isActive()){ //判断是否运动
// this.animate1.pause() //暂停
// }else{
// this.animate1.resume() //恢复
// }
// })
//双击屏幕控制画布全屏 退出全屏
window.addEventListener('dblclick',()=>{
const fullScreenElement=document.fullscreenElement;
if(!fullScreenElement){
this.renderer.domElement.requestFullscreen() //画布对象全屏
}else{
document.exitFullscreen() //退出全屏
}
})
},
methods: {
setScene(){
this.container = document.getElementById('container');//获取dom元素
//创建渲染器 //消除锯齿{antialias: true} 透明背景alpha: true
this.renderer = new THREE.WebGLRenderer({antialias: true});
this.renderer.setClearColor(0x000000,1);//设置背景颜色
//this.renderer.setSize( window.innerWidth, window.innerHeight ); //全屏区域
this.renderer.setSize(this.container.clientWidth,this.container.clientHeight);//设置渲染区域尺寸
//开启场景中的阴影贴图
this.renderer.shadowMap.enabled=true;
// document.body.appendChild( this.renderer.domElement );
this.container.appendChild(this.renderer.domElement);//body元素中插入canvas对象
//创建场景
this.scene = new THREE.Scene();
},
setCamera(){
//创建相机
//this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000 );
this.camera=new THREE.PerspectiveCamera(45,this.container.clientWidth/this.container.clientHeight,0.1,10000);
this.camera.position.set( 100, 20, 150 );
this.camera.lookAt( 0, 6, 0 );
// this.camera.lookAt(this.scene.position);//设置相机方向(指向的场景对象)
},
setControls(){
//创建controls对象 控制器 摄像机 渲染到那个元素上
let controls=new OrbitControls(this.camera,this.renderer.domElement)
//监听控制器的鼠标事件,执行渲染内容
controls.addEventListener('change',()=>{
this.renderer.render(this.scene,this.camera);
})
//监听窗口的变化
window.addEventListener('resize',()=>this.onWindowResize());
},
//监听窗口的大小变化 还有相机的比例
onWindowResize(){
// this.camera.aspect=window.innerWidth/window.innerHeight;
this.camera.aspect=this.container.clientWidth/this.container.clientHeight;
this.camera.updateProjectionMatrix();
// this.renderer.setSize(window.innerWidth,window.innerHeight)
this.renderer.setSize(this.container.clientWidth,this.container.clientHeight)
},
//设置材质
setFromPoints(){
this.mercury = new THREE.Group()//建立一个组 一个自转
this.mercuryParent =new THREE.Group()//建立一个组 一个公转
this.scene.add(this.mercuryParent)
const geometry = new THREE.BoxGeometry( 5, 5, 5 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
this.mesh = new THREE.Mesh( geometry, material );
// console.log(mesh) //通过打印可以调取里面的方法来控制物体的变化
//缩放
this.mesh.scale.set(3,2,1)
//旋转 Math.PI等于180度
this.mesh.rotation.set(Math.PI /45,0,0)
this.mercury.position.x -= 30 //初始位置
this.mercury.add(this.mesh)//添加到组里
this.mercuryParent.add(this.mercury)
},
//设置缓冲几何体
setbuffmater(){
// const geometry=new THREE.BufferGeometry();
// //设置顶点数组 每三个值作为一个顶点 一维数组
// const vertices=new Float32Array([
// -1.0,-1.0,1.0,
// 1.0,-1.0,1.0,
// 1.0,1.0,1.0,
// 1.0,1.0,1.0,
// -1.0,1.0,1.0,
// -1.0,-1.0,1.0,
// ]);
// //把点传到属性上,设置位置点, 需要告诉它每三个组成一个顶点坐标
// geometry.setAttribute('position',new THREE.BufferAttribute(vertices,3))
// const material = new THREE.MeshBasicMaterial( {color: 0xff0000} );
// const mesh = new THREE.Mesh( geometry, material );
// this.scene.add(mesh)
//创建几何体
for(let i=0;i<50;i++){
//每一个三角形,需要3个顶点,每个顶点需要3个值
const geometry=new THREE.BufferGeometry();
const positionArray=new Float32Array(9); //浮点类型数组 需要告诉它有9个值
for(let j=0;j<9;j++){ //随机生成一个三角形顶点坐标 -5到5
positionArray[j]=Math.random()*10-5
}
geometry.setAttribute('position',new THREE.BufferAttribute(positionArray,3))
//设置颜色随机
let color=new THREE.Color(Math.random(),Math.random(),Math.random())
//设置透明度transparent:true,opacity:0.5
const material = new THREE.MeshBasicMaterial( {color:color,transparent:true,opacity:0.5} );
const mesh = new THREE.Mesh( geometry, material );
this.scene.add(mesh)
}
},
//金属材质 MeshBasicMaterial 不受光照的影响
basicmater(){
//导入纹理 导入图片
const textureLoader=new THREE.TextureLoader()
//加载纹理
const doorColorTexture= textureLoader.load(require('../assets/img/5.png'))
//加载黑白图片 黑表示隐藏 白色表示显示
const doorAplhaTexture=textureLoader.load(require('../assets/img/4.png'))
//环境这档贴图 黑色表示加深 暗光 白色表示白光
const doorAoTexture=textureLoader.load(require('../assets/img/6.png'))
//设置纹理属性
// doorColorTexture.offset.x= 0.5 //偏移 值的范围是0~1
// doorColorTexture.offset.y= 0.5
// doorColorTexture.offset.set(0.5,0.5)
//设置旋转原点
// doorColorTexture.center.set(0.5,0.5) //(0.5,0.5) 对应纹理的正中心 默认是(0,0) 左下角
// 纹理旋转
//doorColorTexture.rotation=Math.PI/4 //旋转45度
//设置纹理的重复
// doorColorTexture.repeat.set(2,3) //水平重复2次 垂直重复3次
//设置纹理重复的模式
// doorColorTexture.wrapS=THREE.RepeatWrapping; //x轴 RepeatWrapping平铺重复
//doorColorTexture.wrapT=THREE.MirroredRepeatWrapping; //y轴 镜像重复MirroredRepeatWrapping
//texture纹理显示设置 很小像素的贴图比如16像素
// const doorColorTexture= textureLoader.load(require('../assets/img/3.png'))
// doorColorTexture.minFilter=THREE.NearestFilter;
// doorColorTexture.magFilter=THREE.NearestFilter;
const cubeGeometry=new THREE.BoxBufferGeometry(10,10,10);
const basicMaterial=new THREE.MeshBasicMaterial({
//color:'#ffff00', //颜色
map:doorColorTexture , //纹理
alphaMap:doorAplhaTexture, //加载透明纹理 就是黑白色图片 和opacity只设置一个就可以
transparent:true, //添加这个才生效透明纹理
aoMap:doorAoTexture, //环境遮挡贴图
//aoMapIntensity:0.5, //暗的强度 取值0~1
//opacity:0.3, //透明度
// side:THREE.DoubleSide, //两面
})
//也可以单独设置
//basicMaterial.side=THREE.DoubleSide
const cube=new THREE.Mesh(cubeGeometry,basicMaterial)
//给cube添加第二组uv 可以加第二层环境遮挡贴图 cubeGeometry.attributes.uv之前本身的UV贴图拿过来继续用
cubeGeometry.setAttribute('uv2',new THREE.BufferAttribute(cubeGeometry.attributes.uv.array,2))
cube.position.x = 20
this.scene.add(cube)
},
//灯光材质 标准的物理材质 平时基本用这种 MeshStandardMaterial 没有灯光就是黑色的
standmater(){
let Event={}
//单张纹理图的加载
Event.onLoad=()=>{
console.log('图片加载完成')
}
Event.onProgress=(url,num,total)=>{
console.log('图片加载完成:',url)
console.log('图片加载进度:',num)
console.log('图片总数:',total)
console.log('加载进度的百分比:',(num/total*100).toFixed(2)+'%')
}
Event.onError=(e)=>{
console.log('图片加载出现错误')
console.log(e)
}
//设置加载管理器
const loadingManager=new THREE.LoadingManager(Event.onLoad, Event.onProgress,Event.onError)
//导入纹理 导入图片
const textureLoader=new THREE.TextureLoader(loadingManager)
//加载纹理
const doorColorTexture= textureLoader.load(require('../assets/img/5.png'),
// Event.onLoad, Event.onProgress,Event.onError
)
//加载黑白图片 黑表示隐藏 白色表示显示
const doorAplhaTexture=textureLoader.load(require('../assets/img/4.png'))
//环境这档贴图 黑色表示加深 暗光 白色表示白光
const doorAoTexture=textureLoader.load(require('../assets/img/6.png'))
//导入置换贴图 黑色的隐藏 白色的凸出来显示
const doorHeightTexture=textureLoader.load(require('../assets/img/9.png'))
//导入粗糙贴图 黑色粗糙 白色光滑
const roughnessTexture=textureLoader.load(require('../assets/img/11.png'))
//导入金属贴图 黑色完全不是金属 白色是金属
const metalnessTexture=textureLoader.load(require('../assets/img/7.png'))
//导入法线贴图
const normalTexture=textureLoader.load(require('../assets/img/12.png'))
//需要配置顶点
const cubeGeometry=new THREE.BoxBufferGeometry(10,10,10,100,100,100);
const basicMaterial=new THREE.MeshStandardMaterial({
//color:'#ffff00', //颜色
map:doorColorTexture , //纹理
alphaMap:doorAplhaTexture, //加载透明纹理 就是黑白色图片 和opacity只设置一个就可以
transparent:true, //添加这个才生效透明纹理
aoMap:doorAoTexture, //环境遮挡贴图
displacementMap:doorHeightTexture,
displacementScale:1, //需要设置这个凸出 BoxBufferGeometry后面三位数值需要加顶点才生效
roughness:1,//设置粗糙度 取值0~1 0是光滑 1是非常粗糙
roughessMap:roughnessTexture, //粗糙贴图 roughness是乘关系
metalness:1, //金属度 取值0~1 1完全是金属
metalnessMap:metalnessTexture, //金属贴图
normalMap:normalTexture //法线贴图
})
const cube=new THREE.Mesh(cubeGeometry,basicMaterial)
cube.position.x = 40
this.scene.add(cube)
},
//设置cube纹理加载器 环境贴图加载器
cubetrue(){
const cubeTextureLoader=new THREE.CubeTextureLoader()
//前、后、上、下、左、右的顺序放图片
const envMapTexture= cubeTextureLoader.load([
require('../assets/img/heather_ft.jpg'),
require('../assets/img/heather_bk.jpg'),
require('../assets/img/heather_up.jpg'),
require('../assets/img/heather_dn.jpg'),
require('../assets/img/heather_rt.jpg'),
require('../assets/img/heather_lf.jpg'),
])
const sphereGeometry=new THREE.SphereBufferGeometry(4,20,20);
const material =new THREE.MeshStandardMaterial({
metalness:0.7, //金属度 取值0~1 1完全是金属
roughness:0.1, //设置粗糙度 取值0~1 0是光滑 1是非常粗糙
envMap:envMapTexture //环境贴图
})
const sphere=new THREE.Mesh(sphereGeometry,material)
sphere.position.y =20
//给场景添加背景图
this.scene.background=envMapTexture
// this.scene.environment=envMapTexture //给场景所有的物体添加默认的环境贴图
this.scene.add(sphere)
},
//灯光
light(){
//环境光 没有方向
const light=new THREE.AmbientLight(0xffffff,0.5); //环境光 灯光强度0~1
this.scene.add(light)
//直线光 比如太阳光 DirectionalLight 不知道为啥投射阴影失效 是不是版本的问题
//SpotLight 聚光灯
// const directionalLight=new THREE.SpotLight(0xffffff,0.5);
// directionalLight.position.set(20,40,20) //设置位置
// //开启光照投射阴影
// directionalLight.castShadow=true
// //设置阴影贴图模糊度
// directionalLight.shadow.radius=10;
// //设置阴影贴图的分辨率
// directionalLight.shadow.mapSize.set(2048,2048);
// //聚光灯打在这个球上面
// directionalLight.target=this.sphere
// //更改聚光灯的角度 最大角度Math.PI/2
// directionalLight.angle=Math.PI/6
// //光源的衰减值 0是不衰减
// directionalLight.distance=0;
// //半影衰减
// directionalLight.penumbra=0; //取值是0~1
// this.scene.add(directionalLight)
// const gui=new dat.GUI()
// //有动画的时候不生效 修改位置
// gui.add(directionalLight,'penumbra')
// .min(0) //最小值
// .max(1) //最大值
// .step(0.1) // 每次移动的数量
//点光源
// const pointLight=new THREE.PointLight(0xff0000,1)
// pointLight.position.set(40,10,10)
// //开启光照投射阴影
// pointLight.castShadow=true
// //设置阴影贴图模糊度
// pointLight.shadow.radius=10;
// //光照的距离到哪里为0
// pointLight.distance=100;
// //设置阴影贴图的分辨率
// pointLight.shadow.mapSize.set(2048,2048);
// this.scene.add(pointLight)
},
//设置光源在小球上面重叠
smallBall(){
this.samll=new THREE.Mesh(
new THREE.SphereBufferGeometry(1,20,20),
new THREE.MeshBasicMaterial({color:0xff0000})
)
this.samll.position.set(20,20,20)
//点光源
const pointLight=new THREE.PointLight(0xff0000,1,100)
//开启光照投射阴影
pointLight.castShadow=true
this.samll.add(pointLight)
this.scene.add(this.samll)
},
//光照阴影 灯光与阴影
spherGemet(){
//1,材质要满足能够对关照有反应
// 最常用-标准网格材质(MeshStandardMaterial)
// 更消耗性能-物理网格材质(MeshPhysicalMaterial)-MeshStandardMaterial的扩展,
// 提供了更高级的基于物理的渲染属性:
//2,设置渲染器开启阴影的计算 renderer.shadowMap.enabled=true
//3,设置光照投射阴影 directionalLight.castShadow=true
//4,设置物体投射阴影 sphere.castShadow=true
//5,设置物体接收阴影 plane.receiveShadow=true
const sphereGeometry=new THREE.SphereBufferGeometry(4,20,20)
const material=new THREE.MeshStandardMaterial()
this.sphere=new THREE.Mesh(sphereGeometry,material)
this.sphere.position.z=20
//设置物体投射阴影
this.sphere.castShadow=true
this.scene.add(this.sphere)
//创建平面
const planeGeomentry=new THREE.PlaneBufferGeometry(1000,1000)
const plane=new THREE.Mesh(planeGeomentry,material)
plane.position.set(0,-4,0)
plane.rotation.x = -Math.PI/2
//设置物体接收阴影
plane.receiveShadow=true
this.scene.add(plane)
},
//设置公转函数
revolution () {
this.mercuryParent.rotation.y += 0.0016
},
//设置自转 旋转
selfRotation(){
this.mercury.rotation.y += 0.08
},
//时间动画
gsaptime(){
this.animate1= gsap.to(this.mesh.position,{
x:500,
duration:5, //持续时间
repeat:-1, //重复几次 无限循环-1
ease:'power1.inOet', // ease更改速率
yoyo:true, //往返运动
delay:2, //延迟2秒
onComplete:()=>{ //onComplete回调函数
console.log('动画完成')
},onStart:()=>{
console.log('动画开始')
}
})
//旋转
gsap.to(this.mesh.rotation,{x:2*Math.PI,duration:5})
},
//GUI
gui(){
const gui=new dat.GUI()
//有动画的时候不生效 修改位置
gui.add(this.mesh.position,'x')
.min(0) //最小值
.max(1000) //最大值
.step(0.1) // 每次移动的数量
.name('移动x轴') //命名
.onChange((value)=>{
console.log('值被修改:',value)
})
.onFinishChange((value)=>{
console.log('完全停下来:',value)
})
//修改物体颜色
const params={
color:'#ffff00',
fn:()=>{
//让立方体运动起来
gsap.to(this.mesh.position,{x:500,duration:2,yoyo:true,repeat:-1})
}
}
gui.addColor(params,'color').onChange((value)=>{
console.log('值被修改',value)
//设置材质的颜色
this.mesh.material.color.set(value)
})
gui.add(this.mesh,'visible').name('是否显示')
//点击触发某个事件
gui.add(params,'fn').name('立方体运动')
//文件夹
const folder= gui.addFolder('设置立方体')
//设置材质线框
folder.add(this.mesh.material,'wireframe')
},
//初始化
init() {
this.setScene() //设置场景
this.setCamera() //设置相机
this.setControls() //设置可旋转控制
this.setFromPoints() //材质
this.setbuffmater() //材质
this.basicmater() //金属材质
this.standmater() //灯光材质
this.cubetrue() //纹理加载器
this.spherGemet() //光照阴影
this.light() //灯光
this.smallBall() //发光的小球
//this.gsaptime() //动画
this.gui() //gui
this.animate(); // 循环动画
},
// 循环场景 、相机、 位置更新
animate() {
//获取时钟运行的总时长
let time=this.clock.getElapsedTime();
//实现圆周运动 Math.cos(x) x 的余弦值。返回的是 -1.0 到 1.0 之间的数
this.samll.position.x=Math.sin(time)*30
this.samll.position.z=Math.cos(time)*30
//设置小球上下运动
this.samll.position.y=20+Math.sin(time*10)
// 获取间隔时长
// let deltaTime=this.clock.getDelta();
// console.log(deltaTime)
requestAnimationFrame( this.animate);
// this.revolution()
// this.selfRotation()
this.renderer.render( this.scene, this.camera);
},
}
}
</script>
<style>
#container {
height: 1000px;
}
body{
margin: 0;
padding: 0;
}
</style>