Some test code with three.js
# node version should >= 18.0.0, current repo use 20.0.0
cd Three_test
npx viteimport * as THREE from 'three'
// 1. 创建场景
const scene = new THREE.Scene();
/**
* 2. 创建相机
* PerspectiveCamera(视野角度, 长宽比, 近截面, 远截面)
*/
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
// 3. 创建渲染器
const renderer = new THREE.WebGLRenderer();
// 4. 设置渲染尺寸并将渲染器加入到页面中
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio)
document.body.appendChild(renderer.domElement)
// 创建立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
// 创建材质
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
// 为立方体设置材质
const cube = new THREE.Mesh(geometry, material)
// 在场景中加入立方体;默认会加入到场景中的(0, 0, 0)坐标
scene.add(cube)
// 设置相机的位置
camera.position.z = 5;
// 创建循环渲染(动画循环)并执行
function animate(){
requestAnimationFrame(animate)
// 使立方体持续旋转
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera)
}
animate();import * as THREE from 'three'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader'
const scene = new THREE.Scene()
const importAirplane = () => {
const modelPath = '../static/airplane.fbx';
const loader = new FBXLoader()
loader.load(modelPath, (data) => {
scene.add(data)
}, (event) => {
console.log(event.loaded / event.total * 100 + "% loaded.");
}, (err) => {
console.log(err);
})
}import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
const scene = new THREE.Scene()
const importAirplane = () => {
const modelPath = '../static/airplane.glb';
const loader = new GLTFLoader()
loader.load(modelPath, (data) => {
// 和fbx不同的是,它的scene在返回数据的第二层
data = data.scene;
data.traverse((child) => {
// 为模型的每一个子元素都设置投射阴影
child.castShadow = true
// 解决颜色暗淡无光的问题(似乎仅适用于.glb的文件)
if (child.isMesh) {
child.material.emissive = child.material.color;
child.material.emissiveMap = child.material.map ;
// child.material.shadowSide = THREE.BackSide
}
})
scene.add(data)
}, (event) => {
console.log(event.loaded / event.total * 100 + "% loaded.");
}, (err) => {
console.log(err);
})
}// 创建场景、相机、渲染器并挂在至浏览器窗口
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
100, window.innerWidth / window.innerHeight, 0.1, 1000
)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
// 1.渲染器开启阴影贴图
renderer.shadowMap.enabled = true
const createBall = () => {
const sphereGeo = new THREE.SphereGeometry(1, 20, 20)
const material = new THREE.MeshStandardMaterial()
const sphere = new THREE.Mesh(sphereGeo, material)
// 2.投射阴影
sphere.castShadow = true
scene.add(sphere)
// 创建一个平面
const geometry = new THREE.PlaneGeometry(50, 50)
const planeMaterial = new THREE.MeshStandardMaterial()
const plane = new THREE.Mesh(geometry, planeMaterial)
plane.position.set(0, -1, 0)
plane.rotation.x = -Math.PI / 2
// 3.接受阴影
plane.receiveShadow = true
scene.add(plane)
// 环境光
const light = new THREE.AmbientLight(0xffffff, 1)
scene.add(light)
// 聚光灯
const spotLight = new THREE.SpotLight(0xffffff, 2)
spotLight.position.set(0, 11, 5)
// spotLight.position.set(-5, 5, 15)
spotLight.decay = 0; // 纵向沿着光照距离的缩减量
spotLight.angle = Math.PI / 8 // 散射角度
spotLight.penumbra = 0.1 // 聚光锥的半影缩减百分比
spotLight.distance = 30;
spotLight.shadow.radius = 10
spotLight.shadow.mapSize.set(256, 256) // 阴影映射的宽高
// spotLight.target.position.set(0, 0, 0)
spotLight.castShadow = true // 4.开启阴影
spotLight.shadow.mapSize.width = 4096; // 设置影子的像素大小,越小越模糊
spotLight.shadow.mapSize.height = 4096;
spotLight.shadow.camera.near = 0.5;
spotLight.shadow.camera.far = 100;
scene.add(spotLight)
// 打开聚光灯助手
// const spotLightHelper = new THREE.SpotLightHelper(spotLight, 'lightgreen')
// scene.add(spotLightHelper)
}// 加载机器人模型
const loadBot = () => {
const modelPath = '../static/Xbot.glb'
const loader = new GLTFLoader()
loader.load(modelPath, (modelData) => {
// 将模型加入到场景中
scene.add(modelData.scene)
// 打开骨架助手
const skeleton = new THREE.SkeletonHelper(modelData.scene)
skeleton.visible = false
scene.add(skeleton)
// 创建动画控制器
const mixer = new THREE.AnimationMixer(modelData.scene)
modelData.animations.forEach(clip => {
const action = mixer.clipAction(clip)
// 存储3d模型里的动画
animationHub[clip.name] = action;
})
createGUI()
const clock = new THREE.Clock();
function animate(){
requestAnimationFrame(animate)
const delta = clock.getDelta()
if(mixer) mixer.update(delta)
renderer.render(scene, camera)
}
animate();
}, (event) => {
console.log(event.loaded / event.total * 100 + '% loaded.')
}, (err) => {
console.log(err);
})
}
// 创建GUI
const createGUI = () => {
const gui = new GUI()
const animateFolder = gui.addFolder("动画")
// 收起
// animateFolder.close();
// 展开(默认)
// animateFolder.open();
animateFolder.add(
currentAnimate, 'action', Object.keys(animationHub)
).name('模型动画').onChange(key => {
console.log(1008600, { key });
executeAnimateWithKey(key)
}).setValue('idle')
bindKeyBoard()
}
// 绑定键盘
const bindKeyBoard = () => {
document.addEventListener('keyup', (event) => {
// run
if(event.key === 'W') {
executeAnimateWithKey('run')
return;
}
// walk
if(event.key === 'w') {
executeAnimateWithKey('walk')
return;
}
// stop
if(event.key === 's') {
executeAnimateWithKey('idle')
return;
}
})
}
// 根据键名执行动画
const executeAnimateWithKey = (keyName) => {
Object.values(animationHub).forEach(i => {
i.stop();
})
if(animationHub[keyName]) {
animationHub[keyName].play();
}
}