使用 Three.js 搭建元宇宙基础交互 | 大帅老猿 Three.js 特训

大家好,今天使用 Three.js 搭建一个简单的元宇宙项目。

预览效果

首先先看预览效果:预览

初始化项目

首先我们使用 vite 创建 vanilla-ts 项目,并且安装 Three.js。

pnpm create vite three-demo-4 --template vanilla-ts
cd three-demo-4
pnpm i
pnpm install three
pnpm i --save-dev @types/three

使用 pnpm run dev 启动项目,打开 http://localhost:5173/,可以看到 vite 初始化的页面。

我们直接把 main.tsstyle.css 里面原来的代码删掉,在里面写我们的代码。

创建场景、相机和渲染器

// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 50);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.set(0, 3, 25);

添加背景色及灯光

// 添加背景色及灯光
scene.background = new THREE.Color(0.2, 0.2, 0.2);

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));

添加展馆

// 添加展馆
let mixer: AnimationMixer;
new GLTFLoader().load('../resources/models/zhanguan.glb', (gltf) => {

  scene.add(gltf.scene);

  mixer = new THREE.AnimationMixer(gltf.scene);
})

渲染场景

// 渲染场景
function animate() {
  requestAnimationFrame(animate);

  renderer.render(scene, camera);

  if (mixer) {
    mixer.update(0.02);
  }
}

animate();

当浏览器窗口变化时,实时调整

window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

这时候打开 http://localhost:5173/ 可以看到如下界面:

在这里插入图片描述

添加视频

我们继续给这个展馆添加各个屏幕及视频

new GLTFLoader().load('../resources/models/zhanguan.glb', (gltf) => {

  scene.add(gltf.scene);

  gltf.scene.traverse((child) => {

    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 = true;
      video.loop = true;
      video.play();
      const videoTexture = new THREE.VideoTexture(video);
      const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
      (child as THREE.Mesh).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 = true;
      video.loop = true;
      video.play();
      const videoTexture = new THREE.VideoTexture(video);
      const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
      (child as THREE.Mesh).material = videoMaterial;
    }

    if (child.name === '环形屏幕') {
      const video = document.createElement('video');
      video.src = "./resources/video02.mp4";
      video.muted = true;
      video.autoplay = true;
      video.loop = true;
      video.play();
      const videoTexture = new THREE.VideoTexture(video);
      const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
      (child as THREE.Mesh).material = videoMaterial;
    }

    if (child.name === '柱子屏幕') {
      const video = document.createElement('video');
      video.src = "./resources/yanhua.mp4";
      video.muted = true;
      video.autoplay = true;
      video.loop = true;
      video.play();
      const videoTexture = new THREE.VideoTexture(video);
      const videoMaterial = new THREE.MeshBasicMaterial({ map: videoTexture });
      (child as THREE.Mesh).material = videoMaterial;
    }
  })

  mixer = new THREE.AnimationMixer(gltf.scene);
})

打开 http://localhost:5173/ 会看到展馆里的屏幕和柱子等,都加上了视频

在这里插入图片描述

添加人物

然后把人物加到展馆里面,并且更新 animate 函数

// 添加人物
let playerMixer: AnimationMixer;
let playerMesh: THREE.Group
let actionWalk: AnimationAction
let actionIdle: AnimationAction
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);

  // 人物停止时候的状态
  const clipIdle = THREE.AnimationUtils.subclip(gltf.animations[0], 'idle', 31, 281);
  actionIdle = playerMixer.clipAction(clipIdle);
  actionIdle.play();
});
function animate() {
  requestAnimationFrame(animate);

  renderer.render(scene, camera);

  if (mixer) {
    mixer.update(0.02);
  }

  if (playerMixer) {
    playerMixer.update(0.015);
  }
}

此时,页面显示是这样:

在这里插入图片描述

控制人物行走

下面让鼠标控制转镜头,按键盘的 W 让人物可以在展馆里行走

let isWalk = false;
const playerHalfHeight = new THREE.Vector3(0, 0.8, 0);
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;
    }
  }
})

window.addEventListener('keyup', (e) => {
  if (e.key === 'w') {
    crossPlay(actionWalk, actionIdle);
    isWalk = false;
  }
});

let preClientX: number;
window.addEventListener('mousemove', (e) => {
  if (preClientX && playerMesh) {
    playerMesh.rotateY(-(e.clientX - preClientX) * 0.01);
  }
  preClientX = e.clientX;
});

function crossPlay(curAction: AnimationAction, newAction: AnimationAction) {
  curAction.fadeOut(0.3);
  newAction.reset();
  newAction.setEffectiveWeight(1);
  newAction.play();
  newAction.fadeIn(0.3);
}

添加阴影

最后给展馆设置阴影

// 设置阴影
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;

好了,完成了,预览效果请点击:预览

仓库地址

仓库地址

加入猿创营 (v:dashuailaoyuan),一起交流学习。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Three.js是一个基于WebGL的JavaScript库,用于创建和展示3D图形和动画。它提供了一系列易于使用的功能和工具,可以帮助开发者在网页上实现虚拟宇宙。 要实现虚拟宇宙,你可以使用Three.js的以下功能: 1. 场景(Scene):创建一个场景对象,用于容纳所有的3D元素,如模型、灯光和相机等。 2. 模型(Model):使用Three.js加载3D模型文件,如OBJ、FBX或者glTF格式的模型。你可以使用现有的模型库,也可以自己创建模型。 3. 材质(Material):为模型添加材质,可以是颜色、纹理或者其他效。Three.js提供了各种内置的材质类型,也支持自定义材质。 4. 灯光(Light):在场景中添加灯光,以提供逼真的光照效果。Three.js支持多种灯光类型,如环境光、平行光、点光源等。 5. 相机(Camera):设置相机来观察场景中的物体。你可以使用透视相机或正交相机,根据需要调整相机的位置和视角。 6. 动画(Animation):使用Three.js的动画系统来创建和控制物体的动画效果。你可以定义关键帧动画或者使用Tween.js等插件来实现更复杂的动画效果。 7. 用户交互(User Interaction):通过监听鼠标、键盘或触摸事件,实现用户与虚拟宇宙交互。你可以控制相机的移动、模型的选择和操作等。 8. 物理引擎(Physics Engine):如果需要模拟真实的物理效果,可以集成Three.js与物理引擎库,如Cannon.js或Physijs等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值