Threejs教程,2024全新系统threejs入门教程
https://www.bilibili.com/video/BV1Zm421g7oi/?spm_id_from=333.999.0.0
2 4
01-theejs三要素
WebGL
顶点数据
顶点索引
矩阵
三要素
场景 Scene 容器
相机 Camera 观察
渲染器Renderer 组合
透视相机(PerspectiveCamera)
http://www.yanhuangxueyuan.com/threejs/docs/index.html#manual/zh/introduction/Creating-a-scene
TextureLoader 加载本地图片
import * as THREE from "three";
//创建场景
const scene = new THREE.Scene();
// scene.background=new THREE.Color('rgb(222,222,222)')
let loader=new THREE.TextureLoader()
loader.load('./img/mm.jpg',function(texture){
scene.background=texture
//renderer.render(scene,camera) //重新render
})
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 3;
camera.position.x = 2;
camera.lookAt(0, 0, 0);
//创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
const cube=new THREE.Mesh(
new THREE.BoxGeometry(1,1,1),
new THREE.MeshBasicMaterial()
)
scene.add(cube)
function animate(){
requestAnimationFrame(animate)
cube.rotation.x+=0.01
cube.rotation.y+=0.01
renderer.render(scene,camera)
}
animate()
02-threejss三要素之相机
三要素之相机
正交相机
透视相机
立方相机
立体相机
http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/zh/cameras/PerspectiveCamera
PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
fov — 摄像机视锥体垂直视野角度
aspect — 摄像机视锥体长宽比
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面
import * as THREE from "three";
//创建场景
const scene = new THREE.Scene();
// scene.background=new THREE.Color('rgb(222,222,222)')
let loader=new THREE.TextureLoader()
loader.load('./img/mm.jpg',function(texture){
scene.background=texture
//renderer.render(scene,camera) //重新render
})
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// camera.position.z = 3; //在视野的坐标
// camera.position.x = 2;
camera.position.set(0.4,10)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
const cube=new THREE.Mesh(
new THREE.BoxGeometry(1,1,1),
new THREE.MeshBasicMaterial()
)
const cube2=new THREE.Mesh(
new THREE.BoxGeometry(1,1,1),
new THREE.MeshBasicMaterial({
color:0xff0000
})
)
cube2.position.x=2
scene.add(cube,cube2)
let angle=0
function animate(){
requestAnimationFrame(animate)
cube.rotation.x+=0.01
cube.rotation.y+=0.01
const radius=3
angle+=0.01
camera.position.x=radius*Math.cos(angle)
camera.position.z=radius*Math.sin(angle)
camera.lookAt(0, 0, 0);
renderer.render(scene,camera)
}
animate()
03-threejs三要素之渲染器
WebGLRenderer
import * as THREE from "three";
//创建场景
const scene = new THREE.Scene();
// scene.background=new THREE.Color('rgb(222,222,222)')
let loader=new THREE.TextureLoader()
loader.load('./img/mm.jpg',function(texture){
scene.background=texture
//renderer.render(scene,camera) //重新render
})
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// camera.position.z = 3; //在视野的坐标
// camera.position.x = 2;
camera.position.set(0.4,10)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
canvas:document.querySelector('.c1')
});
// renderer.setSize(window.innerWidth,window.innerHeight)
// document.body.appendChild(renderer.domElement)
const cube=new THREE.Mesh(
new THREE.BoxGeometry(1,1,1),
new THREE.MeshBasicMaterial()
)
const cube2=new THREE.Mesh(
new THREE.BoxGeometry(1,1,1),
new THREE.MeshBasicMaterial({
color:0xff0000
})
)
cube2.position.x=2
scene.add(cube,cube2)
let angle=0
function animate(){
requestAnimationFrame(animate)
cube.rotation.x+=0.01
cube.rotation.y+=0.01
const radius=3
angle+=0.01
camera.position.x=radius*Math.cos(angle)
camera.position.z=radius*Math.sin(angle)
camera.lookAt(0, 0, 0);
renderer.render(scene,camera)
}
animate()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
html,body{
width: 100%;
height: 100%;
overflow: hidden;
}
.wrap{
display: flex;
}
.c1,.c2{
margin: 0 10px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div class="wrap">
<canvas class="c1" width="500" height="500"></canvas>
<canvas class="c2" width="500" height="500"></canvas>
</div>
<script type="module" src="./main.js">
</script>
</body>
</html>
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
canvas:document.querySelector('.c1')
});
04-轨道控制器
轨道控制器
(OrbitControls)
GridHelper 网格
import * as THREE from "three";
//创建场景
const scene = new THREE.Scene();
let loader=new THREE.TextureLoader()
loader.load('./img/mm.jpg',function(texture){
scene.background=texture
})
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,200,20)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
const cube2=new THREE.Mesh(
new THREE.BoxGeometry(10,10,10),
new THREE.MeshBasicMaterial({
color:0xff0000
})
)
let grid=new THREE.GridHelper(100,10,0xffffff)
scene.add(grid,cube2)
let isDown=false
let startx=0
let currentX=0
renderer.domElement.addEventListener('mousedown',(e)=>{
isDown=true
startx=e.clientX
})
renderer.domElement.addEventListener('mousemove',(e)=>{
if(isDown){
const distanceX=e.clientX-startx
currentX+=distanceX*0.01
startx=e.clientX
let radius=40
camera.position.x = radius * Math.cos(currentX);
camera.position.z = radius * Math.sin(currentX);
camera.lookAt(0, 0, 0);
}
})
renderer.domElement.addEventListener('mouseup',(e)=>{
isDown=false
})
function animate(){
requestAnimationFrame(animate)
renderer.render(scene,camera)
}
animate()
模拟轨道
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
let loader=new THREE.TextureLoader()
loader.load('./img/mm.jpg',function(texture){
scene.background=texture
renderer.render(scene,camera)
})
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,200,20)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
const cube2=new THREE.Mesh(
new THREE.BoxGeometry(10,10,10),
new THREE.MeshBasicMaterial({
color:0xff0000
})
)
let grid=new THREE.GridHelper(100,10,0xffffff)
scene.add(grid,cube2)
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
function animate(){
requestAnimationFrame(animate)
controls.update()
// renderer.render(scene,camera)
}
animate()
05-绘制物体的三要素之几何体
绘制:几何体 盒子模型 材质
创建结构 几何体
创建材质 外观
结构和材质组合,生成一个物体
BufferGeometry 接近底层webgl
Float32Array 类型化数组
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
let loader=new THREE.TextureLoader()
loader.load('./img/mm.jpg',function(texture){
scene.background=texture
renderer.render(scene,camera)
})
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,2,20)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
// 创建结构 几何体
// const geometry=new THREE.PlaneGeometry(5,5,10,10)
// const geometry=new THREE.PlaneGeometry(5,5)
const geometry=new THREE.BufferGeometry()
//Float32Array 类型化数组
//一个面有6个点 4 2个三角形
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
])
const colors=new Float32Array([
1.0,0.0,0.0,
0.0,1.0,0.0,
0.0,0.0,1.0,
1.0,0.0,0.0,
0.0,1.0,0.0,
0.0,0.0,1.0,
])
geometry.setAttribute('position',new THREE.BufferAttribute(
vertices,
3
))
geometry.setAttribute('color',new THREE.BufferAttribute(
colors,
3
))
// 创建材质 外观
const material=new THREE.MeshBasicMaterial({
vertexColors:true,
wireframe:true
})
// 结构和材质组合,生成一个物体
const mesh=new THREE.Mesh(
geometry,
material
)
let grid=new THREE.GridHelper(100,10,0xffffff)
scene.add(grid,mesh)
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
function animate(){
requestAnimationFrame(animate)
controls.update()
// renderer.render(scene,camera)
}
animate()
06-绘制物体的三要素之材质
物体三要素之材质
Material
Material
皮肤设置:颜色 纹理 光照响应
材质种类:
MeshBasicMaterial 不受光影响
MeshStandardMaterial受光影响
漫反射
镜面反射
高光
哑光
灯光
.roughness:Float材质的粗糙程度,0.0表示平滑的镜面反射,1.0表示完全漫反射。默认值为
1.0,如果还提供roughnessMap,则两个值相乘。
threejs.org/docs/index.html
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
let loader=new THREE.TextureLoader()
let texture=loader.load('./img/mm.jpg')
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,2,20)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
//灯光
const ambientLight=new THREE.AmbientLight(0x404040)
const directionLight=new THREE.DirectionalLight(0xffffff,0.5) //单独背部 不照到
directionLight.position.set(3,3,3)
scene.add(ambientLight,directionLight)
// 创建结构 几何体
const geometry=new THREE.SphereGeometry(2,32,32)
// 创建材质 外观
// const material=new THREE.MeshBasicMaterial({
// // color:0xff0000,
// map:texture
// })
const material=new THREE.MeshStandardMaterial({
// color:0xff0000,
// emissive:0xff0000,//没光 物体自带颜色
map:texture,
roughness:0.5,
metalness:0.5
})
// 结构和材质组合,生成一个物体
const mesh=new THREE.Mesh(
geometry,
material
)
scene.add(mesh)
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
function animate(){
requestAnimationFrame(animate)
controls.update()
renderer.render(scene,camera)
}
animate()
07-贴图详解
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
// let loader=new THREE.TextureLoader()
// let texture=loader.load('./img/mm.jpg')
let image=new Image()
image.src='./img/mm.jpg'
let texture=new THREE.Texture(image)
image.onload=()=>{
texture.needsUpdate=true
}
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,2,20)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
//灯光
const ambientLight=new THREE.AmbientLight(0x404040)
const directionLight=new THREE.DirectionalLight(0xffffff,0.5) //单独背部 不照到
directionLight.position.set(3,3,3)
scene.add(ambientLight,directionLight)
// 创建结构 几何体
const geometry=new THREE.SphereGeometry(2,32,32)
// 创建材质 外观
// const material=new THREE.MeshBasicMaterial({
// // color:0xff0000,
// map:texture
// })
const material=new THREE.MeshStandardMaterial({
// color:0xff0000,
// emissive:0xff0000,//没光 物体自带颜色
map:texture,
roughness:0.5,
metalness:0.5,
//对应纹理 更逼真 需要对应的图片
aoMap:texture,
roughnessMap:texture,
displacementMap:texture,
normalMap:texture
})
// 结构和材质组合,生成一个物体
const mesh=new THREE.Mesh(
geometry,
material
)
scene.add(mesh)
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
function animate(){
requestAnimationFrame(animate)
controls.update()
renderer.render(scene,camera)
}
animate()
08-光照详解
AmbientLight 环境光 全面 没有shadow
平行光(DirectionalLight)太阳光 有shadow
半球光(HemisphereLight)上下 俩面不同
点光源(PointLight) 从一个点向各个方向发射的光源。一个常见的例子是模拟一个灯泡发出的光。
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
// let loader=new THREE.TextureLoader()
// let texture=loader.load('./img/mm.jpg')
let image=new Image()
image.src='./img/mm.jpg'
let texture=new THREE.Texture(image)
image.onload=()=>{
texture.needsUpdate=true
}
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,2,20)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
//灯光
const ambientLight=new THREE.AmbientLight(0x404040)
const directionLight=new THREE.DirectionalLight(0xffffff,0.5) //单独背部 不照到
directionLight.position.set(3,3,3)
const directionLightHelper=new THREE.DirectionalLightHelper(directionLight)
// scene.add(ambientLight,directionLight)
// scene.add(directionLight,directionLightHelper)
// const hemisphereLight=new THREE.HemisphereLight(0xff0000,0x00ff00,1)
// scene.add(hemisphereLight)
const pointLight=new THREE.PointLight(0xff0000,1,100)
const pointLightHelper=new THREE.PointLightHelper(pointLight,1)
pointLight.position.set(0,5,0)
scene.add(pointLight,pointLightHelper)
// 创建结构 几何体
const geometry=new THREE.SphereGeometry(2,32,32)
// 创建材质 外观
// const material=new THREE.MeshBasicMaterial({
// // color:0xff0000,
// map:texture
// })
const material=new THREE.MeshStandardMaterial({
// color:0xff0000,
// emissive:0xff0000,//没光 物体自带颜色
map:texture,
roughness:0.5,
metalness:0.5,
//对应纹理 更逼真 需要对应的图片
// aoMap:texture,
// roughnessMap:texture,
// displacementMap:texture,
// normalMap:texture
})
// 结构和材质组合,生成一个物体
const mesh=new THREE.Mesh(
geometry,
material
)
scene.add(mesh)
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
function animate(){
requestAnimationFrame(animate)
controls.update()
renderer.render(scene,camera)
}
animate()
09-阴影投射详解
阴影投射:光源 物体 渲染器
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
// let loader=new THREE.TextureLoader()
// let texture=loader.load('./img/mm.jpg')
let image=new Image()
image.src='./img/mm.jpg'
let texture=new THREE.Texture(image)
image.onload=()=>{
texture.needsUpdate=true
}
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,50,50)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.shadowMap.enabled=true
document.body.appendChild(renderer.domElement)
//灯光
const ambientLight=new THREE.AmbientLight(0x404040)
scene.add(ambientLight)
const directionLight=new THREE.DirectionalLight(0xffffff,1) //单独背部 不照到
directionLight.castShadow=true
directionLight.shadow.mapSize.width=2048 //阴影清晰
directionLight.shadow.mapSize.height=2048
directionLight.position.set(120,20,0)
const directionLightHelper=new THREE.DirectionalLightHelper(directionLight,5)
scene.add(directionLight,directionLightHelper)
const pointLight=new THREE.PointLight(0xff0000,200,100)
const pointLightHelper=new THREE.PointLightHelper(pointLight,1)
pointLight.castShadow=true
pointLight.position.set(10,10,0)
scene.add(pointLight,pointLightHelper)
// 创建结构 几何体
const geometry=new THREE.PlaneGeometry(100,200)
// 创建材质 外观
const material=new THREE.MeshStandardMaterial({
color:0xffffff,
})
// 结构和材质组合,生成一个物体
const mesh=new THREE.Mesh(
geometry,
material
)
mesh.rotation.x=-Math.PI/2;
mesh.receiveShadow=true
const mesh2=new THREE.Mesh(
new THREE.BoxGeometry(10,10,10),
new THREE.MeshStandardMaterial({color:0xff0000})
)
mesh.position.set(0,-5,0)
mesh2.castShadow=true
scene.add(mesh,mesh2)
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
let angle=0
function animate(){
angle+=0.01
pointLight.position.x=10* Math.cos(angle)
pointLight.position.z=10* Math.sin(angle)
requestAnimationFrame(animate)
controls.update()
renderer.render(scene,camera)
}
animate()
10-raycaster射线详解
光线投射Raycaster
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0,30,30)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
const ambientLight=new THREE.AmbientLight(0x404040)
scene.add(ambientLight)
const mesh=new THREE.Mesh(
new THREE.SphereGeometry(1,30,30),
new THREE.MeshStandardMaterial({color:0xff0000})
)
const mesh2=new THREE.Mesh(
new THREE.SphereGeometry(1,30,30),
new THREE.MeshStandardMaterial({color:0xff0000})
)
const mesh3=new THREE.Mesh(
new THREE.SphereGeometry(1,30,30),
new THREE.MeshStandardMaterial({color:0xff0000})
)
mesh.position.set(3,0,0)
mesh3.position.set(-3,0,0)
scene.add(mesh,mesh2,mesh3)
// 光线投射Raycaster
const raycaster=new THREE.Raycaster()
const rayOrigin=new THREE.Vector3(-6,0,0)
const rayDirection=new THREE.Vector3(1,0,0)
raycaster.set(rayOrigin,rayDirection)
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
let meshs=[mesh,mesh2,mesh3]
const clock=new THREE.Clock()
function animate(){
const elapseTime=clock.getElapsedTime()
const intersects=raycaster.intersectObjects(meshs)
mesh.position.y=4*Math.sin(elapseTime*0.5)
mesh2.position.y=4*Math.sin(elapseTime*0.8)
mesh3.position.y=4*Math.sin(elapseTime*1.5)
for(const mesh of meshs){
mesh.material.color.set(new THREE.Color(0xff0000))
}
for(const intersectObj of intersects){
intersectObj.object.material.color.set(new THREE.Color(0x00eef3))
}
requestAnimationFrame(animate)
controls.update()
renderer.render(scene,camera)
}
animate()
11-raycaster实现选中物体变色
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
//创建场景
const scene = new THREE.Scene();
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0,30,30)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
document.body.appendChild(renderer.domElement)
const ambientLight=new THREE.AmbientLight(0x404040)
scene.add(ambientLight)
const mesh=new THREE.Mesh(
new THREE.SphereGeometry(1,30,30),
new THREE.MeshStandardMaterial({color:0xff0000})
)
const mesh2=new THREE.Mesh(
new THREE.SphereGeometry(1,30,30),
new THREE.MeshStandardMaterial({color:0xff0000})
)
const mesh3=new THREE.Mesh(
new THREE.SphereGeometry(1,30,30),
new THREE.MeshStandardMaterial({color:0xff0000})
)
mesh.position.set(3,0,0)
mesh3.position.set(-3,0,0)
scene.add(mesh,mesh2,mesh3)
let meshs=[mesh,mesh2,mesh3]
// 光线投射Raycaster
const raycaster=new THREE.Raycaster()
const mouse=new THREE.Vector2()
window.addEventListener('mousedown',(event)=>{
//浏览器坐标 转 标准坐标
mouse.x=(event.clientX/window.innerWidth)*2-1
mouse.y=-(event.clientY/window.innerHeight)*2+1
raycaster.setFromCamera(mouse,camera)
const intersects=raycaster.intersectObjects(meshs)
for(const mesh of meshs){
mesh.material.color.set(new THREE.Color(0xff0000))
}
for(const intersectObj of intersects){
intersectObj.object.material.color.set(new THREE.Color(0x00eef3))
}
})
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
function animate(){
requestAnimationFrame(animate)
controls.update()
renderer.render(scene,camera)
}
animate()
12-模拟物理运动1
cannon物理引擎库
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import * as CANNOU from 'cannon'
//创建场景
const scene = new THREE.Scene();
scene.background=new THREE.Color(0xbfd1e5)
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,3,10)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.shadowMap.enabled=true
document.body.appendChild(renderer.domElement)
//灯光
const directionLight=new THREE.DirectionalLight(0xffffff,1) //单独背部 不照到
directionLight.position.set(5,10,7.5)
scene.add(directionLight)
// 结构和材质组合,生成一个物体
//创建地面
const plane=new THREE.Mesh(
new THREE.PlaneGeometry(20,20),
new THREE.MeshStandardMaterial({
color:0x808080,
})
)
plane.rotation.x=-Math.PI/2;
//创建小球
const redius=1
const sphere=new THREE.Mesh(
new THREE.SphereGeometry(redius,32,32),
new THREE.MeshStandardMaterial({color:0xff0000})
)
sphere.position.set(0,10,0)
scene.add(plane,sphere)
//创建物理世界
let world=new CANNOU.World()
world.gravity.set(0,-9.8,0) //世界重力多少
//创建物理材料
const groundMaterial=new CANNOU.Material('groundMaterial')
const sphereMaterial=new CANNOU.Material('sphereMaterial')
const contactMeterial=new CANNOU.ContactMaterial(
groundMaterial,
sphereMaterial,
{
restitution:0.8,//0不弹 1持续
}
)
world.addContactMaterial(contactMeterial)
//创建物理地面
const groundBody=new CANNOU.Body({
mass:0,//是否有引力 地面默认没
shape:new CANNOU.Plane(),
material:groundMaterial
})
groundBody.quaternion.setFromEuler(-Math.PI/2,0,0) //position
world.addBody(groundBody)
//创建物理小球
const sphereBody=new CANNOU.Body({
mass:1,
// position:new CANNOU.Vec3(0,3,0),
position:sphere.position,
material:sphereMaterial
})
const sphereShape=new CANNOU.Sphere(redius)
sphereBody.addShape(sphereShape)
world.addBody(sphereBody)
const updatePhysic=()=>{
world.step(1/60)
sphere.position.copy(sphereBody.position)
}
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
//渲染循环
const clock=new THREE.Clock()
function animate(){
const elapsedTime=clock.getElapsedTime()
updatePhysic()
controls.update()
renderer.render(scene,camera)
requestAnimationFrame(animate)
}
animate()
13-物理运动-小球发射
import * as THREE from "three";
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import * as CANNOU from 'cannon'
//创建场景
const scene = new THREE.Scene();
scene.background=new THREE.Color(0xbfd1e5)
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2,3,10)
camera.lookAt(0, 0, 0);//改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias:true,//像素变小 锯齿小
});
renderer.setSize(window.innerWidth,window.innerHeight)
renderer.shadowMap.enabled=true
document.body.appendChild(renderer.domElement)
//灯光
const directionLight=new THREE.DirectionalLight(0xffffff,1) //单独背部 不照到
directionLight.position.set(5,10,7.5)
scene.add(directionLight)
// 结构和材质组合,生成一个物体
//创建地面
const plane=new THREE.Mesh(
new THREE.PlaneGeometry(20,20),
new THREE.MeshStandardMaterial({
color:0x808080,
})
)
plane.rotation.x=-Math.PI/2;
//创建小球
const redius=1
const sphere=new THREE.Mesh(
new THREE.SphereGeometry(redius,32,32),
new THREE.MeshStandardMaterial({color:0xff0000})
)
sphere.position.set(-5,10,0)
scene.add(plane,sphere)
//创建物理世界
let world=new CANNOU.World()
world.gravity.set(0,-9.8,0) //世界重力多少
//创建物理材料
const groundMaterial=new CANNOU.Material('groundMaterial')
const sphereMaterial=new CANNOU.Material('sphereMaterial')
const contactMeterial=new CANNOU.ContactMaterial(
groundMaterial,
sphereMaterial,
{
restitution:0.8,//0不弹 1持续
}
)
world.addContactMaterial(contactMeterial)
//创建物理地面
const groundBody=new CANNOU.Body({
mass:0,//是否有引力 地面默认没
shape:new CANNOU.Plane(),
material:groundMaterial
})
groundBody.quaternion.setFromEuler(-Math.PI/2,0,0) //position
world.addBody(groundBody)
//创建物理小球
const sphereBody=new CANNOU.Body({
mass:1,
// position:new CANNOU.Vec3(0,3,0),
position:sphere.position,
material:sphereMaterial,
linearDamping:0.5,//空气阻力
})
const sphereShape=new CANNOU.Sphere(redius)
sphereBody.addShape(sphereShape)
sphereBody.applyLocalForce(
new CANNOU.Vec3(100,0,0),//用多少力 和球的质量有关mass
new CANNOU.Vec3(0,-10,0) //击中球的上中下位置 0中 10上一点 -10下前后回跑 和台球一样
)
world.addBody(sphereBody)
const updatePhysic=()=>{
world.step(1/60)
sphere.position.copy(sphereBody.position)
sphere.quaternion.copy(sphereBody.quaternion)
}
let controls=new OrbitControls(camera,renderer.domElement)
controls.enableDamping=true //惯性
controls.addEventListener('change',()=>{
renderer.render(scene,camera)
})
//渲染循环
const clock=new THREE.Clock()
function animate(){
const elapsedTime=clock.getElapsedTime()
updatePhysic()
controls.update()
renderer.render(scene,camera)
requestAnimationFrame(animate)
}
animate()
小球法射
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as CANNOU from "cannon";
//创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xbfd1e5);
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2, 3, 10);
camera.lookAt(0, 0, 0); //改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, //像素变小 锯齿小
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
//灯光
const directionLight = new THREE.DirectionalLight(0xffffff, 1); //单独背部 不照到
directionLight.position.set(5, 10, 7.5);
scene.add(directionLight);
// 结构和材质组合,生成一个物体
//创建地面
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({
color: 0x808080,
})
);
plane.rotation.x = -Math.PI / 2;
scene.add(plane);
//创建物理世界
let world = new CANNOU.World();
world.gravity.set(0, -9.8, 0); //世界重力多少
//创建物理材料
const groundMaterial = new CANNOU.Material("groundMaterial");
const sphereMaterial = new CANNOU.Material("sphereMaterial");
const contactMeterial = new CANNOU.ContactMaterial(
groundMaterial,
sphereMaterial,
{
restitution: 0.8, //0不弹 1持续
}
);
world.addContactMaterial(contactMeterial);
//创建物理地面
const groundBody = new CANNOU.Body({
mass: 0, //是否有引力 地面默认没
shape: new CANNOU.Plane(),
material: groundMaterial,
});
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); //position
world.addBody(groundBody);
let spheres=[]
const createSphere = (position, direction) => {
//创建可视化小球
const redius = 1;
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(redius, 32, 32),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
sphere.position.copy(position);
scene.add(sphere);
//创建物理小球
const sphereBody = new CANNOU.Body({
mass: 1,
material: sphereMaterial,
linearDamping: 0.5, //空气阻力
});
const sphereShape = new CANNOU.Sphere(redius);
sphereBody.position.copy(position)
sphereBody.addShape(sphereShape);
sphereBody.applyLocalForce(
direction.scale(600), //用多少力 和球的质量有关mass
new CANNOU.Vec3(0, 0, 0) //击中球的上中下位置 0中 10上一点 -10下前后回跑 和台球一样
);
world.addBody(sphereBody);
spheres.push({
sphere,
sphereBody
})
};
renderer.domElement.addEventListener("mouseup", (event) => {
let mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
//射线
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const pos = new THREE.Vector3();
pos.copy(raycaster.ray.direction);
pos.add(raycaster.ray.origin);
const direction = new CANNOU.Vec3(
raycaster.ray.direction.x,
raycaster.ray.direction.y,
raycaster.ray.direction.z
);
createSphere({x:pos.x,y:pos.y,z:pos.z}, direction);
});
const updatePhysic = () => {
world.step(1 / 60);
spheres.forEach(({sphere,sphereBody})=>{
sphere.position.copy(sphereBody.position);
sphere.quaternion.copy(sphereBody.quaternion);
})
};
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; //惯性
controls.addEventListener("change", () => {
renderer.render(scene, camera);
});
//渲染循环
const clock = new THREE.Clock();
function animate() {
const elapsedTime = clock.getElapsedTime();
updatePhysic();
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
14-加载3D模型
8.1 支持的模型类型
Three.js 支持加载多种模型文件格式,常见的包括:
alrE Lal albaTE CL Teancmleelon Cormat Khe
gitf obj fbx clollada ply stl vrml 3ds json
Three.js 提供了多种加载器(Loaders)来支持上述模型格式的加载,例如:
GLTFLoader 用于加载 gITF 文件。
OBJLoader 用于加载 OBJ 文件。
FBXLoader 用于加载 FBX 文件。
ColladaLoader 用于加载 Collada 文件.
TDSLoader用于加载 3DS 文件。
JSONLoader 用于加载 Three.js 的 JSON 格式文件。
https://www.cgmodel.com/model/752945.html
https://juejin.cn/post/6911217131254185991
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as CANNOU from "cannon";
import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
//创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xbfd1e5);
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0, 500, 300);
camera.lookAt(0, 0, 0); //改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, //像素变小 锯齿小
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
//灯光
const ambientLight=new THREE.AmbientLight(0x404040)
const directionLight = new THREE.DirectionalLight(0xffffff, 1); //单独背部 不照到
directionLight.position.set(5, 10, 7.5);
scene.add(ambientLight,directionLight);
let fbxLoader=new FBXLoader()
let process=document.querySelector('.process')
fbxLoader.load('./mod/Aerith(Kamura Dress).fbx',(fbx)=>{
console.log(fbx,8888)
//fbx 看属性加 fbx.sence fbx
fbx.children.forEach(item=>{
let mesh=item.clone()
let color=mesh.material.color
mesh.material=new THREE.MeshPhongMaterial({
color
})
scene.add(mesh)
})
},(xhr)=>{
const percent=(xhr.loaded/xhr.total)*100
process.textContent= `${Math.round(percent,2)}%`
},(err)=>{
console.log(err.message)
})
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; //惯性
controls.addEventListener("change", () => {
renderer.render(scene, camera);
});
//渲染循环
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
15-控制模型动画
AnimationClip 动画
https://blog.csdn.net/qq_35459724/article/details/134259599
默认太小了
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as CANNOU from "cannon";
import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
import _ from 'lodash'
let btn=document.getElementById('btn')
btn.onclick=()=>{
location.reload();
}
//创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xbfd1e5);
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(30, 269, 516);
camera.lookAt(0, 0, 0); //改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, //像素变小 锯齿小
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
//灯光
const ambientLight=new THREE.AmbientLight(0xffffff,1)
const directionLight = new THREE.DirectionalLight(0xffffff, 1); //单独背部 不照到
directionLight.position.set(0, 5, 0);
scene.add(ambientLight,directionLight);
let modloader=new GLTFLoader()
let process=document.querySelector('.process')
let mixer,runAction,idleAction,currentAction
modloader.load('./mod/girl.glb',(mod)=>{
console.log(mod,8888)
const model=mod.scene
model.scale.set(100, 100, 100);
const animations=mod.animations
console.log(animations)
mixer=new THREE.AnimationMixer(model)
const runClip=animations.find(clip=>clip.name==='run')
const idleClip=animations.find(clip=>clip.name==='idle')
if(runClip&&idleClip){
runAction=mixer.clipAction(runClip)
idleAction=mixer.clipAction(idleClip)
idleAction.play()
currentAction=idleAction
}
//随机
// mixer.clipAction(randomAction(animations)).play()
scene.add(model)
},(xhr)=>{
const percent=(xhr.loaded/xhr.total)*100
process.textContent= `${Math.round(percent,2)}%`
},(err)=>{
console.log(err.message)
})
const randomAction=(animations)=>{
return _.shuffle(animations)[0]
}
let flag=false
window.addEventListener('click',(event)=>{
flag=!flag
if(flag){
if(runAction&¤tAction!==runAction){
currentAction.fadeOut(0.5)
runAction.reset().fadeIn(0.5).play()
currentAction=runAction
}
}else{
if(idleAction&¤tAction!==idleAction){
currentAction.fadeOut(0.5)
idleAction.reset().fadeIn(0.5).play()
currentAction=idleAction
}
}
})
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; //惯性
controls.addEventListener("change", () => {
renderer.render(scene, camera);
// console.log(camera.position)
});
//渲染循环
const clock=new THREE.Clock()
function animate() {
const elapsedTime=clock.getDelta()
if(mixer)mixer.update(elapsedTime);
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
16-3D看房案例1
全景图-球体 / 立方体4个面
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as CANNOU from "cannon";
import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
import _ from 'lodash'
let btn=document.getElementById('btn')
btn.onclick=()=>{
location.reload();
}
//创建场景
const scene = new THREE.Scene();
//创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0,0,30);
camera.lookAt(0, 0, 0); //改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, //像素变小 锯齿小
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
let textureload=new THREE.TextureLoader()
//换全景图6个就行了
let imgs=[
'./img/mm.jpg',
'./img/mm.jpg',
'./img/mm.jpg',
'./img/mm.jpg',
'./img/mm.jpg',
'./img/mm.jpg',
]
let materials=[]
imgs.forEach(item=>{
const texture=textureload.load(item)
const material=new THREE.MeshBasicMaterial({map:texture})
materials.push(material)
})
const cube=new THREE.Mesh(
new THREE.BoxGeometry(40,40,40),
materials
)
cube.geometry.scale(12,12,-12);//在里面看
scene.add(cube)
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; //惯性
controls.addEventListener("change", () => {
renderer.render(scene, camera);
// console.log(camera.position)
});
//渲染循环
const clock=new THREE.Clock()
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as CANNOU from "cannon";
import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
import _ from 'lodash'
let btn=document.getElementById('btn')
btn.onclick=()=>{
location.reload();
}
//创建场景
const scene = new THREE.Scene();
//创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0,0,30);
camera.lookAt(0, 0, 0); //改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, //像素变小 锯齿小
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
let textureload=new THREE.TextureLoader()
const texture=textureload.load('./img/vr.jpg')
const sphere=new THREE.Mesh(
new THREE.SphereGeometry(16,32,32),
new THREE.MeshBasicMaterial({map:texture})
)
sphere.geometry.scale(12,12,-12);//在里面看
scene.add(sphere)
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; //惯性
controls.addEventListener("change", () => {
renderer.render(scene, camera);
// console.log(camera.position)
});
//渲染循环
const clock=new THREE.Clock()
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
17-3D看房案例2
18-3D看房3
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as CANNOU from "cannon";
import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
import _ from 'lodash'
let btn=document.getElementById('btn')
btn.onclick=()=>{
location.reload();
}
//创建场景
const scene = new THREE.Scene();
const axesHelper = new THREE.AxesHelper( 100 );
scene.add( axesHelper );
//创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0,0,30);
camera.lookAt(0, 0, 0); //改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, //像素变小 锯齿小
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
let textureload=new THREE.TextureLoader()
const texture=textureload.load('./img/vr.jpg')
const sphere=new THREE.Mesh(
new THREE.SphereGeometry(16,32,32),
new THREE.MeshBasicMaterial({map:texture})
)
sphere.geometry.scale(12,12,-12);//在里面看
scene.add(sphere)
//点精灵
const spriteTexture=textureload.load('./img/open.png')
const sprite=new THREE.Sprite(new THREE.SpriteMaterial({map:spriteTexture}))
sprite.scale.set(10,5,1)
sprite.position.set(-70,25,40)
scene.add(sprite)
const oTips=document.querySelector('.tip')
const raycaster=new THREE.Raycaster()
const mouse=new THREE.Vector2()
let roomType='in'
renderer.domElement.addEventListener('mousemove',(event)=>{
mouse.x=(event.clientX/window.innerWidth)*2-1
mouse.y=-(event.clientY/window.innerHeight)*2+1
raycaster.setFromCamera(mouse,camera)
//物体相交
const intersects=raycaster.intersectObject(sprite)
console.log(intersects,66666)
if(intersects.length){
const worldVector=new THREE.Vector3(
intersects[0].object.position.x,
intersects[0].object.position.y,
intersects[0].object.position.z,
)
const dncPostion=worldVector.project(camera)
const halfW=window.innerWidth/2
const halfH=window.innerHeight/2
const left=halfW*dncPostion.x+halfW
const top=halfH*dncPostion.y+halfH-oTips.clientHeight/2-30
oTips.style.left=left+'px'
oTips.style.top=top+'px'
}else{
oTips.style.left=0
oTips.style.top=0
}
})
renderer.domElement.addEventListener('mousedown',(event)=>{
mouse.x=(event.clientX/window.innerWidth)*2-1
mouse.y=-(event.clientY/window.innerHeight)*2+1
raycaster.setFromCamera(mouse,camera)
//物体相交
const intersects=raycaster.intersectObject(sprite)
if(intersects.length){
if(roomType==='in'){
const changeTexture=textureload.load('./img/vr.jpg')
const changeMaterial=new THREE.MeshBasicMaterial({map:changeTexture})
sphere.material=changeMaterial
sprite.position.set(-70,25,40)
roomType='out'
}else if(roomType==='out'){
const changeTexture=textureload.load('./img/vr2.jpg')
const changeMaterial=new THREE.MeshBasicMaterial({map:changeTexture})
sphere.material=changeMaterial
sprite.position.set(-100,10,80)
roomType='in'
}
}
})
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; //惯性
controls.addEventListener("change", () => {
renderer.render(scene, camera);
// console.log(camera.position)
});
//渲染循环
const clock=new THREE.Clock()
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
19-计算相机方向向量
数学+
jdg 以后认真学 计算
16-计算相机正视的方向向量html
20-向量方法实现物体运动
17-物体使用向量方法运动html
cannon系统学习/10-人物跑动-指定方向添加速度
21-欧拉角解析物体旋转
18-物体选旋转的方法html
欧拉角(Euler)
绕着x轴旋转90°
360°=2Π
180°=П
1°=П/180
90° = П/18090
rotation
22-四元数解析物体旋转
19-四元数物体旋转.html
四元数(Quaternion)
有公式
const angle=(Math.PI/180)*30;
// const halfAngle=angle/2;
// const sinAngle=Math.sin(halfAngle)
// const cosAngle=Math.cos(halfAngle)
// const quaternion=new THREE.Quaternion(
// sinAngle*1,
// sinAngle*0,
// sinAngle*0,
// cosAngle
// )
// method 2
const quaternion=new THREE.Quaternion()
const axis=new THREE.Vector3(1,0,0)
quaternion.setFromAxisAngle(axis,angle)
mesh.quaternion.copy(quaternion)
setFromEuler ( efler : Euler ): 欧拉角 to 四元数
23-漫游案例-项目演示搭建
/人物漫游-镜头锁死版/index
person-roam
24-漫游案例-加载3D人物模型
25-漫游案例-添加建筑物
26-漫游案例-编写类执行模型动作
27-漫游案例-键盘控制动作切换
28-控制人物正确朝向
29-控制人物模型运动
30-【物理引擎】认识cannonjs物理引…..
yarn add cannon-es
31-【物理引擎】物理引擎调试工具
yarn add cannon-es-debugger
debug地盘无限大 也可以设置
32-【物理引擎】物理引擎刚体形状全….
33-【物理引擎】不规则形状的应用
34-【物理引擎】物理刚体详解
http://www.webgl3d.cn/pages/e48d3c/
35-【物理引擎】物理刚体碰撞效果
36-【物理引擎】物体碰撞分组问题
37-【物理引擎】施加力的作用
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as CANNOU from "cannon-es";
import CannonDebugger from 'cannon-es-debugger'
//创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xbfd1e5);
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(2, 3, 10);
camera.lookAt(0, 0, 0); //改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, //像素变小 锯齿小
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
//灯光
const directionLight = new THREE.DirectionalLight(0xffffff, 1); //单独背部 不照到
directionLight.position.set(5, 10, 7.5);
scene.add(directionLight);
// 结构和材质组合,生成一个物体
//创建地面
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({
color: 0x808080,
})
);
plane.rotation.x = -Math.PI / 2;
scene.add(plane);
//创建物理世界
let world = new CANNOU.World();
world.gravity.set(0, -9.8, 0); //世界重力多少
//创建物理材料
const groundMaterial = new CANNOU.Material("groundMaterial");
const sphereMaterial = new CANNOU.Material("sphereMaterial");
const contactMeterial = new CANNOU.ContactMaterial(
groundMaterial,
sphereMaterial,
{
restitution: 0.8, //0不弹 1持续
}
);
world.addContactMaterial(contactMeterial);
//创建物理地面
const groundBody = new CANNOU.Body({
mass: 0, //是否有引力 地面默认没
shape: new CANNOU.Plane(),
material: groundMaterial,
});
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); //position
world.addBody(groundBody);
let spheres=[]
const createSphere = (position, direction) => {
//创建可视化小球
const redius = 1;
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(redius, 32, 32),
new THREE.MeshStandardMaterial({ color: 0xff0000 })
);
sphere.position.copy(position);
scene.add(sphere);
//创建物理小球
const sphereBody = new CANNOU.Body({
mass: 1,
material: sphereMaterial,
linearDamping: 0.5, //空气阻力
});
const sphereShape = new CANNOU.Sphere(redius);
sphereBody.position.copy(position)
sphereBody.addShape(sphereShape);
sphereBody.applyLocalForce(
direction.scale(600), //用多少力 和球的质量有关mass
new CANNOU.Vec3(0, 0, 0) //击中球的上中下位置 0中 10上一点 -10下前后回跑 和台球一样
);
world.addBody(sphereBody);
spheres.push({
sphere,
sphereBody
})
};
renderer.domElement.addEventListener("mouseup", (event) => {
let mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
//射线
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const pos = new THREE.Vector3();
pos.copy(raycaster.ray.direction);
pos.add(raycaster.ray.origin);
const direction = new CANNOU.Vec3(
raycaster.ray.direction.x,
raycaster.ray.direction.y,
raycaster.ray.direction.z
);
createSphere({x:pos.x,y:pos.y,z:pos.z}, direction);
});
const updatePhysic = () => {
world.step(1 / 60);
spheres.forEach(({sphere,sphereBody})=>{
sphere.position.copy(sphereBody.position);
sphere.quaternion.copy(sphereBody.quaternion);
})
};
//测试工具
const cannonDebugger=new CannonDebugger(scene,world,{
onInit(body,mesh){
if(body.shapes[0] instanceof CANNOU.Plane){
mesh.scale.set(10,10,1)
}
},
color:0x0000ff
})
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; //惯性
controls.addEventListener("change", () => {
renderer.render(scene, camera);
});
//渲染循环
const clock = new THREE.Clock();
function animate() {
cannonDebugger.update()
const elapsedTime = clock.getElapsedTime();
updatePhysic();
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
项目
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import * as CANNOU from "cannon";
import {FBXLoader} from 'three/examples/jsm/loaders/FBXLoader.js'
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js'
import _, { chain } from 'lodash'
import * as CANNON from 'cannon-es'
let btn=document.getElementById('btn')
btn.onclick=()=>{
location.reload();
}
//创建场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceff);
//创建相机
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(0,3,8);
camera.lookAt(0, 0, 0); //改变视野方向
//创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true, //像素变小 锯齿小
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
//灯光
const ambientLight=new THREE.AmbientLight(0xffffff,1)
const directionLight = new THREE.DirectionalLight(0xffffff, 1); //单独背部 不照到
directionLight.castShadow=true
directionLight.position.set(20,20,20);
scene.add(ambientLight,directionLight);
const ground=new THREE.Mesh(
new THREE.PlaneGeometry(500,500),
new THREE.MeshLambertMaterial({
color:0xffffff,
side:THREE.DoubleSide
})
)
ground.receiveShadow=true
ground.rotateX(-Math.PI/2)
scene.add(ground)
const getRandomInt=(min,max)=>{
return Math.floor(Math.random()*(max-min+1)+min)
}
//创建立方体盒子
const blocks=[]
const createBox=()=>{
for(let i=0;i<500;i++){
const width=getRandomInt(1,5)
const height=getRandomInt(2,6)
const depth=getRandomInt(2,4)
const x=getRandomInt(-200,200)
const z=getRandomInt(-200,200)
const y=height/2
const mesh=new THREE.Mesh(
new THREE.BoxGeometry(width,height,depth),
new THREE.MeshLambertMaterial({
color:0xff0000
})
)
mesh.position.set(x,y,z)
blocks.push(mesh)
}
}
createBox()
scene.add(...blocks)
let modloader=new GLTFLoader()
let process=document.querySelector('.process')
let modelAnimation,player
modloader.load('./mod/girl.glb',(mod)=>{
const model=mod.scene
model.traverse(child=>{
if(child.isMesh){
child.castShadow=true
}
})
model.scale.set(1,1,1);
scene.add(model)
player=model
modelAnimation=new ModelAnimation(model,mod.animations)
modelAnimation.start('idle')
// const cameraDirectin=new THREE.Vector3()
// const playerDirectin=new THREE.Vector3()
// camera.getWorldDirection(cameraDirectin)
// player.getWorldDirection(playerDirectin)
},(xhr)=>{
const percent=(xhr.loaded/xhr.total)*100
process.textContent= `${Math.round(percent,2)}%`
},(err)=>{
console.log(err.message)
})
// 键盘事件
const keyPressed={}
let isJumping=false
window.addEventListener('keydown',(event)=>{
const keycode=event.code.toLocaleLowerCase()
if(keycode==='space'&& !isJumping){
isJumping=true
modelAnimation.updateAction('jump_falling')
}
keyPressed[keycode]=true
})
window.addEventListener('keyup',(event)=>{
const keycode=event.code.toLocaleLowerCase()
if(keycode==='space'&& isJumping){
setTimeout(() => {
isJumping=false
}, modelAnimation.getAnimationDuration('jump_falling')*800);
}
keyPressed[keycode]=false
})
/*更新人物朝向*/
const updateLookAt=()=>{
const cameraDirectin=new THREE.Vector3()
camera.getWorldDirection(cameraDirectin)
const lookAtPostion=new THREE.Vector3()
//声明一个速度向量
const velocity=new THREE.Vector3(0,0,0.06)
const originalLength=velocity.length()
if(keyPressed['keyw']){
lookAtPostion.set(
player.position.x+cameraDirectin.x,
player.position.y,
player.position.z+cameraDirectin.z
)
player.lookAt(lookAtPostion)
//向前运动代码
cameraDirectin.y=0
cameraDirectin.normalize()
cameraDirectin.setLength(originalLength)
player.position.add(cameraDirectin)
}
else if(keyPressed['keys']){
lookAtPostion.set(
player.position.x-cameraDirectin.x,
player.position.y,
player.position.z-cameraDirectin.z
)
player.lookAt(lookAtPostion)
cameraDirectin.y=0
cameraDirectin.normalize()
cameraDirectin.setLength(originalLength)
player.position.sub(cameraDirectin)
}
else if(keyPressed['keya']){
const leftDirection=new THREE.Vector3(
-cameraDirectin.z,
0,
cameraDirectin.x
)
lookAtPostion.set(
player.position.x-leftDirection.x,
player.position.y,
player.position.z-leftDirection.z
)
player.lookAt(lookAtPostion)
leftDirection.setLength(originalLength)
player.position.add(leftDirection.negate())
}
else if(keyPressed['keyd']){
const rightDirection=new THREE.Vector3(
cameraDirectin.z,
0,
-cameraDirectin.x
)
lookAtPostion.set(
player.position.x-rightDirection.x,
player.position.y,
player.position.z-rightDirection.z
)
player.lookAt(lookAtPostion)
rightDirection.setLength(originalLength)
player.position.add(rightDirection.negate())
}
}
//更新人物动作
const updateAniamtion=()=>{
if(isJumping)return
if(
(keyPressed['shiftleft']&&keyPressed['keyw'])||
(keyPressed['shiftleft']&&keyPressed['keya'])||
(keyPressed['shiftleft']&&keyPressed['keys'])||
(keyPressed['shiftleft']&&keyPressed['keyd'])
){
//切换成奔跑动作
modelAnimation.updateAction('run')
}
else if(
keyPressed['keyw']||
keyPressed['keya']||
keyPressed['keys']||
keyPressed['keyd']
){
//切换成走路的动作
modelAnimation.updateAction('walk')
}
else{
//切换成站立状态
modelAnimation.updateAction('idle')
}
}
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; //惯性
controls.addEventListener("change", () => {
renderer.render(scene, camera);
// console.log(camera.position)
});
//渲染循环
const clock=new THREE.Clock()
function animate() {
const elapsedTime=clock.getDelta()
if(modelAnimation){
updateAniamtion()
modelAnimation.update(elapsedTime)
updateLookAt()
};
controls.update();
renderer.render(scene, camera);
window.requestAnimationFrame(animate);
}
window.requestAnimationFrame(animate);
class ModelAnimation{
constructor(model,animations){
this.mixer=new THREE.AnimationMixer(model)
// this.animations=model.animations
this.animations=animations
this.actionObj={}
this.currentAct=null
this.previousAct=null
}
start(name){
this.actionInit(name)
}
actionInit(name){
this.animations.forEach(clip=>{
const action=this.mixer.clipAction(clip)
this.actionObj[clip.name]=action
})
this.currentAct=this.actionObj[name]
this.currentAct.play()
}
updateAction(name,duration=0.2){
this.previousAct=this.currentAct
this.currentAct=this.actionObj[name]
if(this.previousAct!=this.currentAct){
this.previousAct.fadeOut(duration)
this.currentAct.reset().fadeIn(duration).play()
}
}
update(dt){
this.mixer.update(dt)
}
getAnimationDuration(name){
const action=this.actionObj[name]
const clip=action.getClip()
if(action&&clip){
return clip.duration
}
return 0
}
}
gitee 19+ relearn
person-roam
https://gitee.com/wannianqing02/threejs-examples
https://bx2eq6ulg18.feishu.cn/docx/EtsodJe5loGKy2xa0iVcq49WnRb
标签:threejs,const,入门教程,Threejs,THREE,camera,renderer,new,scene From: https://www.cnblogs.com/KooTeam/p/18606823