首页 > 其他分享 >Threejs教程,2024全新系统threejs入门教程

Threejs教程,2024全新系统threejs入门教程

时间:2024-12-14 15:44:22浏览次数:9  
标签:threejs const 入门教程 Threejs THREE camera renderer new scene

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.webgl3d.cn/

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&&currentAction!==runAction){
      currentAction.fadeOut(0.5)
      runAction.reset().fadeIn(0.5).play()
      currentAction=runAction
    }
  }else{
    if(idleAction&&currentAction!==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° = П/180
90

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

https://haohuo.jinritemai.com/ecommerce/trade/detail/index.html?id=3705764964951261249&origin_type=604

标签:threejs,const,入门教程,Threejs,THREE,camera,renderer,new,scene
From: https://www.cnblogs.com/KooTeam/p/18606823

相关文章

  • React服务端渲染框架Next.js入门教程
    React服务端渲染框架Next.js入门教程https://www.bilibili.com/video/BV13441117KK*101节_Next.js简介和手动创建项目02节_creat-next-app快速创建项目03节_Next.js的Page和Conponent使用04节_路由的标签跳转和编程跳转05节_路由跳使用query传递参数和接受参数06节_路由......
  • 《安富莱嵌入式周报》第347期:分立元件自制14bit分辨率DAC,开源电池测试仪,大量位操作技
    周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 视频版https://www.bilibili.com/video/BV1SFq9YAE4j/目录:1、分立元件自制14bit分辨率DAC2、开源电池测试仪3、微软为VSCode制作的AIToolkit插件4、Zephyr相关(1)好消......
  • web安全攻防入门教程
    Web安全攻防入门教程Web安全攻防是指在Web应用程序的开发、部署和运行过程中,保护Web应用免受攻击和恶意行为的技术与策略。这个领域不仅涉及防御措施的实现,还包括通过渗透测试、漏洞挖掘和模拟攻击来识别潜在的安全问题。本教程将带你入门Web安全攻防的基础概念、常见攻击类......
  • 第一部分:基础知识 9 . 视图 --[MySQL轻松入门教程]
    在MySQL中,视图(View)是一个命名的SQL查询,它被存储在数据库目录中。视图可以包含来自一个或多个表的数据,并且可以像真实表一样被查询。下面是对MySQL视图的详细讲解:创建视图使用CREATEVIEW语句来创建视图。语法如下:CREATE[ORREPLACE][ALGORITHM={UNDEFINED|MERGE......
  • ThreeJs-06详解灯光与阴影
    一.gsap动画库1.1基本使用和原理首先直接npm安装然后导入比如让一个物体,x轴时间为5s旋转同理动画的速度曲线,可以在官网的文档找到1.2控制动画属性与方法当然这里面也有一些方法,动画完成,动画开始等一些属性也可实现停止动画随时,给到一个变量双击暂停以及恢复......
  • 渗透测试人员的 Nmap:漏洞扫描零基础入门教程,网络安全看这一篇就够了!
    此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。本文所提供的工具仅用于学习,禁止用......
  • 【人工智能】Moss-AI编程利器:CodeMoss & ChatGPT中文版超详细入门教程!(VScode/IDER/WE
    文章目录摘要一、环境介绍VSvode安装步骤IDER(Pycharm)安装步骤Web使用步骤二、Moss9大功能讲解1、AI问答对话2、文件上传功能3、自定义AI助手4、AI联网助手5、AI图片识别6、思维链思维链的简单介绍使用CodeMoss思维链7、AI图片生成图片生成效果8、图片生成代码9、......
  • 2024年最新最全网络安全护网行动指南【附零基础入门教程】_网络安全靶场整个业务指南
    前言随着《网络安全法》和《等级保护制度条例2.0》的颁布,国内企业的网络安全建设需与时俱进,要更加注重业务场景的安全性并合理部署网络安全硬件产品,严防死守“网络安全”底线。“HW行动”大幕开启,国联易安誓为政府、企事业单位网络安全护航!,网络安全形势变得尤为复杂严峻。......
  • ThreeJS入门(185):THREE.OrbitControls 知识详解,示例代码
    作者:还是大剑师兰特,曾为美国某知名大学计算机专业研究生,现为国内GIS领域高级前端工程师,CSDN知名博主,深耕openlayers、leaflet、mapbox、cesium,webgl,ThreeJS,canvas,echarts等技术开发,欢迎加微信(gis-dajianshi),一起交流。查看本专栏目录-本文是第185篇入门文章......
  • ThreeJS入门(182):THREE.FirstPersonControls 知识详解,示例代码
    作者:还是大剑师兰特,曾为美国某知名大学计算机专业研究生,现为国内GIS领域高级前端工程师,CSDN知名博主,深耕openlayers、leaflet、mapbox、cesium,webgl,ThreeJS,canvas,echarts等技术开发,欢迎加微信(gis-dajianshi),一起交流。查看本专栏目录-本文是第182篇入门文章......