0基础搭建元宇宙看展效果 | 大帅老猿threejs特训

187 阅读3分钟

新的一年,从threejs开始吧~

先看实现效果

演示.webp 安装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),一起交流学习吧~