初识threejs

第一次接触threejs的实战,大受震惊。感觉有点神奇,和现实比较接近,所以研究记录下。现实生活中我们看到的物品,我们需要环境、物体、光源、眼睛、物体材质,这样才能在现实生活中看到画面,那这些在threejs中是怎么实现?其实这些在threejs中都有对应的api去实现,这样才能看起来接近现实。下面学习下...
Threejs 的基本要素

  • 场景
    相当于现实生活中环境

    const scene = new THREE.Scene();
  • 相机
    相当于现实生活中眼睛相机用来确定位置、方向、角度,相机看到的内容就是我们最总在屏幕上看到的内容
    threejs中相机有两种: 正交相机和透视相机(更接近于人眼看到的效果)
    初识threejs_第1张图片
    ​透视相机
    初识threejs_第2张图片
    ​正交相机

    const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
  • 灯光
    光源,有了光才能看到画面
    Ambient Light 环境光
    Directional Light 方向光
    Point Light 点光源(电灯泡)
    Spot Light 聚光灯(手电筒、汽车灯)
    Hemisphere Light 半球光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
  • 几何体
    立方体 圆柱体 球体
 const boxGeometry = new THREE.BoxGeometry(1, 1, 1); // 创建集合体
 const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 创建材质
 const boxMesh = new THREE.Mesh(boxGeometry, boxMaterial); // 创建mesh,没懂这个,估计是真实物体需要形状和材质
 scene.add(boxMesh) // 场景中添加只能是mesh
  • 材质
MeshBasicMaterial    基础材质(测试调试用得多)
MeshStandardMaterial    PBR材质(写实项目标配材质)
const boxMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
  • 渲染器
    有了所有的元素,现在就是渲染出来看效果
let renderer = new THREE.WebGLRenderer({
    antialias: true, // true/false表示是否开启反锯齿
    alpha: true, // true/false 表示是否可以设置背景色透明
    precision: 'highp', // highp/mediump/lowp 表示着色精度选择
    premultipliedAlpha: false, // true/false 表示是否可以设置像素深度(用来度量图像的分率)
    preserveDrawingBuffer: true, // true/false 表示是否保存绘图缓冲
    maxLights: 3, // 最大灯光数
    stencil: false // false/true 表示是否使用模板字体或图案
})

了解了基本元素,下面我们来实现下训练营中的练习demo

初识threejs_第3张图片

全部代码

import * as THREE from 'three';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

let mixer;
let playerMixer;

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(5, 10, 50);

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

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;


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, 18);
  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();

});

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


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) => {
  scene.add(gltf.scene);

  // 获取每一个元素添加视频动画
  gltf.scene.traverse((child) => {
    child.castShadow = true;
    child.receiveShadow = true;

    if (child.name === '2023' || 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;
    }
    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;
    }
  })

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

看下效果:(没钱不想去水印了)

你可能感兴趣的:(javascript)