threejs系列之:相机与投影 📷

778 阅读3分钟

写在最前

往期回顾:

  1. # threejs系列: 相机与投影 📷
  2. # threejs系列: 光源与光照🪀
  3. # threejs系列: 自定义几何体🎃
  4. # threejs系列: 几何变换(上)🍺
  5. # threejs系列: 几何变换(下)🏄‍♂️
  6. # threejs系列: 矩阵推导🎰
  7. # threejs系列: 物体详解🐧
  8. # threejs系列: 材质与贴图详解(上)🦥

本篇文章为: 1. threejs系列:threejs系列: 相机与投影 📷

相机

three.js 摄像机是用于控制三维场景视角的对象,用于控制相机的位置、方向、视野和其他属性。

PerspectiveCamera 透视投影相机

image.png

image.png

视锥体

fov:垂直视野角度,fov越小,物体越大,反之亦然。

很好理解,眯起眼睛看物体,物体会整个呈现在视野中,所以就会显得很大。

aspect: 视锥体长宽比,呈现在屏幕的宽高比。

如全屏则使用window的宽高比,如不全屏则使用Canvas(renderer.domElement)的宽高比。

far、near:可观测的最远与最近的距离。

相关

image.png

image.png

投影

投影是什么?投影是3维物体投射到2D平面的过程。生活中的物体都是3维的,而相机拍出的照片是二维的,这中间必然是受到某种"介质"的影响,而这个"介质"就是算法。

不同算法可以有不同的叫法。

透视投影

近大远小,物体的位置需受与照相机的距离影响。

正交投影

物体位置不受与照相机距离影响。

原理

在WebGL中并没有摄像机的概念,只是通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机。

怎么理解呢,就像是当你坐火车看向窗外时,你会感觉树在往后移动。

相关常用 API

lookAt

Object3D<Event>.lookAt(vector: THREE.Vector3): void

参数为一个3维向量,向量可以代表物体位置,调用方法设置相机朝向。

不要好奇为什么相机可以调用Object3D的方法,因为万物皆是Object3D,后面会将这个基类

getWorldDirection

Camera.getWorldDirection(target: THREE.Vector3): THREE.Vector3

返回当前摄像机所正视的世界空间方向的Vector3对象,俯视时z为负数

updateProjectionMatrix

更新摄像机投影矩阵,当far、near、aspect、fov等修改时需执行,常用于自适应屏幕

setViewOffset

设置相机偏移,用途常用于:多显示器拼接画面

动手实践

  1. 跟随运动

假如我们拥有一个物体随机移动,我们需要镜头始终瞄准物体。

2.gif

<template>
    <div ref="dom"></div>
</template>
<script lang="ts" setup>
import * as THREE from "three";
import { Ref, onMounted, ref } from "vue"

let { PerspectiveCamera, WebGLRenderer, Scene } = THREE;
let camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
let renderer = new WebGLRenderer();
let scene = new Scene();
let dom = ref(null);
let angle = 0;

let cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x000000 }))
scene.add(cube);
cube.position.y = 1;


let plane = new THREE.Mesh(new THREE.BoxGeometry(70, 70, 0.5), new THREE.MeshBasicMaterial({ color: 0x9D9D9D }))
plane.position.z = 0;
plane.rotation.x = -Math.PI / 2;
scene.add(plane)

camera.position.set(20, 20, -20);
camera.lookAt(cube.position);
renderer.setSize(window.innerWidth, window.innerHeight);

let animate = () => {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
    angle += 3
    cubeAnimate();

    camera.lookAt(cube.position);
}

let cubeAnimate = () => {
    cube.position.z = Math.sin(Math.PI / 180 * angle) * 3;
    cube.position.x = Math.cos(Math.PI / 180 * angle) * 3;
}

onMounted(() => {
    if (dom.value) {
        dom.value.appendChild(renderer.domElement);
        animate();
    }
})

</script>
  1. 屏幕自适应
let setSceneAdapt = () => {
    let width = window.innerWidth, height = window.innerHeight;
    camera.aspect = width / height;
    camera.updateProjectionMatrix()
    renderer.setSize(width, height);

    renderer.setPixelRatio(window.devicePixelRatio)
}

window.addEventListener("resize", setSceneAdapt)