在 React 中用 Three.js 实现 Web VR 全景看房

8,033 阅读5分钟

随着 Three.js 的更新迭代,实现 Web(支持PC 和 手机) 端 VR 全景看房已不再是难事。接下来让我们一起动手在 React 中去实践一下。笔者使用最新版 Three.js - v0.120.1 并且结合 ReactNext.js 开发,可以更好地与生产项目相结合。

在线效果图 PC 和手机均可,或者手机扫码下方二维码。

VR 看房 Demo

网上虽然有一些关于 Web VR 看房的文章,但多数由于某些原因导致项目不能直接跑起来,或者不能直接应用到项目中。例如:有的是 Three.js 的版本比较老旧,有的是在HTML中通过 CDN 的方式引入Three.js

需求分析及拆解

  • React 开发环境
  • Three.js 插件
  • VR 看房

开发环境

项目初始化

  • Next.js

Next.js 脚手架快手搭建项目:

--example blog-starter-typescript 指定项目模板,完整模板地址

npx create-next-app --example blog-starter-typescript vr-demo
cd vr-demo

安装 Three.js

three.js 包是老版本的,已经被废弃,大家要安装three

npm i three

代码实现

上面我们已经准备了开发环境,并且安装的相关依赖。接下来就是重点,实现 VR 全景看房。

全景

所为全景就是我们告别传统 2D 平面图片的形式,以 3D 的效果,把观察者置身其中,实现仿若其中的效果。

  • 优点:身临其境,效果更逼真,效果更佳。
  • 缺点:素材的准备,开发难度均较大。

素材准备

3D 效果其实就是立体效果,一个正方体有6个面,假设我们深处一个房间,把房间的6个面都用照片贴上,这样我们在房间无论看向何方,其实看到的都是贴在墙面的各个照片。

所以,我们选择一个立方体,然后在立方体的6个面贴上房间的6个面对应的照片,然后假想我们身处立方体中,此时我们在立方体中转身或者抬头、低头就是看到的全景效果。

接下来我们去用代码来实现:

Three.js 需要了解的核心知识点

Three.js去创建3d世界,我们可以理解为拍摄一场舞台剧,需要舞台-场景(scene)、相机(camera)、导演-渲染器(renderer)、任务-网格(mesh)和光源-(light)。

  • 场景 scene:可以理解为舞台,一切都需要放在舞台上才可以进行拍摄。
  • 相机(camera):相当于摄像机或者人的眼睛,可以记录一切。
  • 渲染器(renderer):相当于导演或者控制器,一切指令都需要他来操控。
  • 网格(mesh):在计算机里,3D世界是由点组成的,无数的面拼接成各种形状的物体。这种模型叫做网格模型。一条线是两个点组成,一个面是3个点组成,一个物体由多个3点组成的面组成。
  • 光源(light):没有光是什么都看不到的,添加光源才可以看见效果。

Coding

  1. 创建场景 - Scene
import * as THREE from 'three';

scene = new THREE.Scene();
  1. 创建相机 - Camera

相机相当于人眼,有摄像机才可以看见场景里面的一切物体和光源。常用的相机是正交摄像机和透视摄像机

  • 正交摄像机是一个矩形可视区域,物体只有在这个区域内才是可见的物体无论距离摄像机是远或事近,物体都会被渲染成一个大小。一般应用场景是2.5d游戏如跳一跳、机械模型

  • 透视摄像机是最常用的摄像机类型,模拟人眼的视觉,近大远小(透视)。Fov表示的是视角,Fov越大,表示眼睛睁得越大,离得越远,看得更多。如果是需要模拟现实,基本都是用这个相机

// 创建相机
const w = window.innerWidth
const h = window.innerHeight
  
camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 1000); // 透视相机

camera.position.set(0, -0, 0); // 设置相机位置
camera.lookPoint = {}; // 观察点
camera.lookPoint.x = 0;
camera.lookPoint.y = 0;
camera.lookPoint.z = -1;

camera.lookAt(camera.lookPoint.x, camera.lookPoint.y, camera.lookPoint.z);
  1. 创建渲染器 - Renderer
// 创造渲染器
renderer = new THREE.WebGLRenderer();
renderer.setSize(w, h);
app.appendChild(renderer.domElement);
  1. 创建网格- Mesh

而网格(mesh)由几何体(geometry)和材质(material)构成

const baseUrl = '/static/images/vr/';

// 立方纹理
const textureLoader = new THREE.CubeTextureLoader().setPath(baseUrl); 

const arr = ['f.jpg', 'b.jpg', 'u.jpg', 'd.jpg', 'l.jpg', 'r.jpg']; // 6张纹理图依次贴在立方体的前、后、上、下、左、右的顺序放置的
const texture = textureLoader.load(arr); // 新版API

// 几何体
const geometry = new THREE.BoxGeometry(50, 50, 50); 

// 材质
const material = new THREE.MeshPhongMaterial({
  envMap: texture,
  side: THREE.DoubleSide,
});

// 创建网格
const cube = new THREE.Mesh(geometry, material); 

cube.position.set(-0, -0, -0);
scene.add(cube); // 加入场景

  1. 创建光源
//环境光
const ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);
  1. 相机位置及朝向计算
const update = () => {
  lat = Math.max(-85, Math.min(85, lat));
  phi = THREE.Math.degToRad(90 - lat);
  theta = THREE.Math.degToRad(lon);

  target.x = 500 * Math.sin(phi) * Math.cos(theta);
  target.y = 500 * Math.cos(phi);
  target.z = 500 * Math.sin(phi) * Math.sin(theta);

  camera.lookAt(target);

  renderer.render(scene, camera);
};
  1. 动画渲染函数

requestAnimationFrame 实现动画效果,优势:

  • 可以开启GPU渲染加速
  • 渲染无卡顿
const animate = () => {
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
  update();
};
  1. 处理鼠标或者手指交互

const handleTouchStart = (e) => {
  e.preventDefault();

  interactiveFlag = true;

  startX = e.touches[0].pageX;
  startY = e.touches[0].pageY;
  startLon = lon;
  startLat = lat;
};

const handleTouchMove = (e) => {
  e.preventDefault();
  lon = (startX - e.touches[0].pageX) * 0.2 + startLon;
  lat = (e.touches[0].pageY - startY) * 0.2 + startLat;
  update();
};

const handleTouchEnd = (e) => {
  e.preventDefault();

  interactiveFlag = false;
};

总结

其实,从代码量来看,在react 中用 Three.js 实现 Web VR 全景看房并不复杂,关键是要具备一定的立体思维。

更多的 Three.jsNext.js 的使用请参考官网文档,

项目源码

完整的项目代码已经公布在 github ,方便大家调试并运行:

github.com/AlexShan200…