新的一年,从 threejs 开始吧~
装置 threejs
npm i three
引入 threejs
// 援用 Threejs
import * as THREE from 'three';
// 引入 GLTF 加载器 用于载入 glTF 资源
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader';
threejs 根本元素
// 根本元素 1、定义场景
const scene = new THREE.Scene();
// 根本元素 2、相机 PerspectiveCamera- 透视相机近大远小 OrthographicCamera- 正交相机远近同样大小
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 50);
camera.position.set(5, 10, 25);
// 根本元素 3、灯光 ambientLight- 环境光 directionalLight- 方向光 pointLight- 点光源 spotLight- 聚光灯 hemisphereLight- 半球光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
scene.add(ambientLight);
// 根本元素 4、几何体
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
// 根本元素 5、材质 MeshBasicMaterial- 根底材质 MeshStandardMaterial-PBR 材质
const boxMaterial = new THREE.MeshBasicMaterial({color: 0x00ff00});
// mesh- 网格 几何体是不能被渲染的,只有几何体和材质联合成网格能力被渲染到屏幕上
const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
// 场景中增加 mesh
scene.add(boxMesh);
残缺代码
// 援用 Threejs
import * as THREE from 'three';
// 引入 GLTF 加载器 用于载入 glTF 资源
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader';
let mixer;
let playerMixer;
// 根本元素 1、定义场景
const scene = new THREE.Scene();
// 根本元素 2、相机 PerspectiveCamera- 透视相机近大远小 OrthographicCamera- 正交相机远近同样大小
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 50);
camera.position.set(5, 10, 25);
scene.background = new THREE.Color(0.2, 0.2, 0.2);
// 根本元素 3、灯光 ambientLight- 环境光 directionalLight- 方向光 pointLight- 点光源 spotLight- 聚光灯 hemisphereLight- 半球光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
scene.add(ambientLight);
const directionLight = new THREE.DirectionalLight(0xffffff, 0.2);
scene.add(directionLight);
directionLight.lookAt(new THREE.Vector3(0, 0, 0));
directionLight.castShadow = true;
directionLight.shadow.mapSize.width = 2048;
directionLight.shadow.mapSize.height = 2048;
const shadowDistance = 20;
directionLight.shadow.camera.near = 0.1;
directionLight.shadow.camera.far = 40;
directionLight.shadow.camera.left = -shadowDistance;
directionLight.shadow.camera.right = shadowDistance;
directionLight.shadow.camera.top = shadowDistance;
directionLight.shadow.camera.bottom = -shadowDistance;
directionLight.shadow.bias = -0.001;
// 根本元素 4、几何体
// const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
// 根本元素 5、材质 MeshBasicMaterial- 根底材质 MeshStandardMaterial-PBR 材质
// const boxMaterial = new THREE.MeshBasicMaterial({color: 0x00ff00});
// mesh- 网格 几何体是不能被渲染的,只有几何体和材质联合成网格能力被渲染到屏幕上
// const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial);
// 场景中增加 mesh
// scene.add(boxMesh);
// 坐标系
// const axesHelper = new THREE.AxesHelper(10);
// scene.add(axesHelper);
// 渲染器 将元素渲染进去能力看见
const renderer = new THREE.WebGLRenderer({antialias: true});
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
let playerMesh;
let actionWalk, actionIdle;
const lookTarget = new THREE.Vector3(0, 2, 0);
// 加载人物模型
new GLTFLoader().load('../resources/models/player.glb', (gltf) => {
playerMesh = gltf.scene;
scene.add(gltf.scene);
playerMesh.traverse((child) => {
child.receiveShadow = true;
child.castShadow = true;
});
playerMesh.position.set(0, 0, 11.5);
playerMesh.rotateY(Math.PI);
playerMesh.add(camera);
camera.position.set(0, 2, -5);
camera.lookAt(lookTarget);
const pointLight = new THREE.PointLight(0xffffff, 1.5);
playerMesh.add(pointLight);
pointLight.position.set(0, 1.8, -1);
playerMixer = new THREE.AnimationMixer(gltf.scene);
const clipWalk = THREE.AnimationUtils.subclip(gltf.animations[0], 'walk', 0, 30);
actionWalk = playerMixer.clipAction(clipWalk);
// actionWalk.play();
const clipIdle = THREE.AnimationUtils.subclip(gltf.animations[0], 'idle', 31, 281);
actionIdle = playerMixer.clipAction(clipIdle);
actionIdle.play();});
let isWalk = false;
const playerHalfHeight = new THREE.Vector3(0, 0.8, 0);
// 监听键盘事件 按下 w / s 管制人物后退 / 后退
window.addEventListener('keydown', (e) => {if (e.key === 'w') {const curPos = playerMesh.position.clone();
playerMesh.translateZ(1);
const frontPos = playerMesh.position.clone();
playerMesh.translateZ(-1);
const frontVector3 = frontPos.sub(curPos).normalize();
const raycasterFront = new THREE.Raycaster(playerMesh.position.clone().add(playerHalfHeight), frontVector3);
const collisionResultsFrontObjs = raycasterFront.intersectObjects(scene.children);
// 碰撞检测
if (collisionResultsFrontObjs && collisionResultsFrontObjs[0] && collisionResultsFrontObjs[0].distance > 1) {
// 管制挪动
playerMesh.translateZ(0.1);
}
if (!isWalk) {
// 人物后退动作
crossPlay(actionIdle, actionWalk);
isWalk = true;
}
}
if (e.key === 's') {playerMesh.translateZ(-0.1);
}
});
// 监听键盘事件 抬起 w 键管制人物进行
window.addEventListener('keyup', (e) => {if (e.key === 'w') {
// 工作站立动作
crossPlay(actionWalk, actionIdle);
isWalk = false;
}
});
let preClientX;
// 监听鼠标 管制人物挪动方向
window.addEventListener('mousemove', (e) => {if (preClientX && playerMesh) {playerMesh.rotateY(-(e.clientX - preClientX) * 0.01);
}
preClientX = e.clientX;
});
// 加载场景模型
new GLTFLoader().load('../resources/models/zhanguan.glb', (gltf) => {// console.log(gltf);
scene.add(gltf.scene);
// 获取每一个元素增加视频动画
gltf.scene.traverse((child) => {// console.log(child.name);
child.castShadow = true;
child.receiveShadow = true;
if (child.name === '2023') {const video = document.createElement('video');
video.src = './resources/yanhua.mp4';
video.muted = true;
video.autoplay = 'autoplay';
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({map: videoTexture});
child.material = videoMaterial;
}
if (child.name === '大屏幕 01' || child.name === '大屏幕 02' || child.name === '操作台屏幕' || child.name === '环形屏幕 2') {const video = document.createElement('video');
video.src = './resources/video01.mp4';
video.muted = true;
video.autoplay = 'autoplay';
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({map: videoTexture});
child.material = videoMaterial;
}
if (child.name === '环形屏幕') {const video = document.createElement('video');
video.src = './resources/video02.mp4';
video.muted = true;
video.autoplay = 'autoplay';
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({map: videoTexture});
child.material = videoMaterial;
}
if (child.name === '柱子屏幕') {const video = document.createElement('video');
video.src = './resources/yanhua.mp4';
video.muted = true;
video.autoplay = 'autoplay';
video.loop = true;
video.play();
const videoTexture = new THREE.VideoTexture(video);
const videoMaterial = new THREE.MeshBasicMaterial({map: videoTexture});
child.material = videoMaterial;
}
});
mixer = new THREE.AnimationMixer(gltf.scene);
const clips = gltf.animations; // 播放所有动画
clips.forEach(function (clip) {const action = mixer.clipAction(clip);
action.loop = THREE.LoopOnce;
// 停在最初一帧
action.clampWhenFinished = true;
action.play();});
});
function crossPlay(curAction, newAction) {curAction.fadeOut(0.3);
newAction.reset();
newAction.setEffectiveWeight(1);
newAction.play();
newAction.fadeIn(0.3);
}
// 动画
function animate() {requestAnimationFrame(animate);
renderer.render(scene, camera);
if (mixer) {mixer.update(0.02);
}
if (playerMixer) {playerMixer.update(0.015);
}
}
animate();
素材、源码和演示成果稍后附在评论区,有什么问题欢送留言
以上就是在 threejs 特训中学到的 0 根底实现人物看展成果, 退出猿创营 (v:dashuailaoyuan),一起交流学习吧~