如何在React中利用Three.js打造Web VR全景看房体验
随着 Three.js
的更新迭代,实现 Web(支持PC 和 手机) 端 VR 全景看房已不再是难事。接下来让我们一起动手在 React
中去实践一下。笔者使用最新版 Three.js - v0.120.1
并且结合 React
和 Next.js
开发,可以更好地与生产项目相结合。
在线效果图 PC 和手机均可,或者手机扫码下方二维码。
网上虽然有一些关于
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
- 创建场景 -
Scene
import * as THREE from 'three';
scene = new THREE.Scene();
- 创建相机 -
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);
- 创建渲染器 -
Renderer
// 创造渲染器
renderer = new THREE.WebGLRenderer();
renderer.setSize(w, h);
app.appendChild(renderer.domElement);
- 创建网格-
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); // 加入场景
- 创建光源
//环境光
const ambient = new THREE.AmbientLight(0xffffff);
scene.add(ambient);
- 相机位置及朝向计算
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);
};
- 动画渲染函数
用requestAnimationFrame
实现动画效果,优势:
- 可以开启
GPU
渲染加速 - 渲染无卡顿
const animate = () => {
renderer.render(scene, camera);
requestAnimationFrame(animate);
update();
};
- 处理鼠标或者手指交互
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.js 和 Next.js 的使用请参考官网文档,
项目源码
完整的项目代码已经公布在 github ,方便大家调试并运行:
github.com/AlexShan200…