Three.js 实战【1】—— 3D全景视图开发
- 摘要
- 1、3D视图Demo
- 2、准备工作——搭建好一个开发环境
- 3、RGBELoader——高范围动态图像加载器
- 4、HDR——高动态范围图像
- 5、使用HDR实现3D全景视图
- 6、直接通过图片纹理进行实现
摘要
在现代开发过程当中,3D开发是越来越不可或缺的一部门,在前面的文章当中简单的说明了一些threeJs的基础,从这里开始,我们将对ThreeJs整体进行一定的应用。
1、3D视图Demo
我们先看一下这篇文章主要要实现的一个demo效果,现如今在第三方租房、买房App、或者百度、高德地图上会出现这种全景视图,当手势操作时,他对应的景色也会发生一定的变化。又或者你最近刷抖音看到了HM玩的图寻,给了一张可以旋转的3D全景图片,然后找这张图片在哪拍摄的,你可能会好奇这种效果在前端是如何实现的。这就是这篇文章将会教会你的一个小demo。由于视频过大,直接插入链接,可在该链接查看效果:https://live.csdn.net/v/297465
2、准备工作——搭建好一个开发环境
在这里创建好一个vue脚手架的项目,这里不做过多的赘述,并且安装threeJs的依赖,创建好一个vue文件的threejs开发的基本模板,也就是对之前介绍的抽离出来放到了vue当中,启动项目查看效果是否出现,这里就简单的添加了一个坐标系用来验证3D效果
<template>
<div id="home" class="home">
</div>
</template>
<script>
import * as THREE from 'three'
import {
OrbitControls
} from 'three/examples/jsm/controls/OrbitControls.js'
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 1);
scene.add(camera);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
export default {
data() {
return {
controls: null
}
},
mounted() {
this.windownEvent()
document.getElementById('home').appendChild(renderer.domElement);
this.render()
},
methods: {
windownEvent() {
window.addEventListener('resize', (e) => {
// 宽高比
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
})
},
render() {
renderer.render(scene, camera);
requestAnimationFrame(this.render);
},
}
}
</script>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#home {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
3、RGBELoader——高范围动态图像加载器
Three.js 中 RGBELoader 是一种用于加载高动态范围图像(High Dynamic Range,HDR)的 Loader,它可以将 .hdr 或 .rgbe 格式的 HDR 图像文件加载到 Three.js 的 WebGL 场景中,并方便地应用于场景渲染和光照等方面。
下面是 RGBELoader 主要的特点和使用方式:
- 支持 .hdr 和 .rgbe 格式的 HDR 图像。.hdr 格式是包含 HDR 光照信息的图片格式,其中每个像素值通常表示颜色和亮度等信息;.rgbe 格式节省了存储空间,采用纯文本的方式保存 HDR 图像,但需要进行额外的解析和处理操作。
- 支持流式加载、按需解码、异步数据读取等功能。在加载时,可以设置图片数据类型(UnsignedByteType、FloatType 等),控制是否自动 gamma 校正,以及是否生成 mipmap 等参数。
- 提供了多种加载结果的返回类型,如 HDRBufferAttribute、DataTexture、CubeTexture 等。这些返回类型可以方便地根据实际需要来进行灵活的结果调整和下一步操作。
- 可以与其它 Three.js 特性和工具集成。比如,可以将 HDR 图像转换为 cube map 并与 PBR 材质、天空盒、环境映射等一起使用,实现高质量的场景渲染效果。
4、HDR——高动态范围图像
HDR是一种用于表示图像的技术,它可以在不失真地呈现高亮度和阴影细节的情况下,显示更广泛、更丰富的颜色和亮度范围。常见的传统图像格式,如 JPEG、PNG 等都是 LDR (Low Dynamic Range) 图像,其表示的颜色和亮度值受到比较大的限制,并且不能储存超出一定范围的亮度信息。
HDR 图像通常被保存为高精度的 32 位或 16 位浮点数格式,每个像素颜色通道的值可以表示比较大的亮度范围,例如标准化后的亮度值可以达到 0 到 1 之间或更高。使用 HDR 技术,有助于突破 LDR 图像表现能力的瓶颈,解决了过曝和欠曝等高光/暗部细节损失的问题。
相比于 LDR 图像,HDR 图像具有很高的动态范围,支持更加复杂和真实的光影效果。通过处理 HDR 图像,可以对导致渲染物体看上去真实的环境光、反射和折射、散射等进行精确的计算和模拟,从而呈现更真实精细的场景。例如,可以创建栩栩如生的光源、镜面和玻璃材质,产生更自然的阴影和高光反射效果等。
除了在娱乐和游戏领域得到广泛应用外,HDR 技术还被用于物理模拟、医学成像、地球科学、工业制造和计算机视觉等领域。值得一提的是,HDR 图像的制作和处理需要耗费较大的计算资源和专业知识,且其数据量相对较大,需要特殊的压缩技术来减少文件大小。
5、使用HDR实现3D全景视图
在熟悉了一下RGBELoader和HDR大概的就了解了这样子的全景视图是如何做的,就像前面说到的Threejs的纹理一样,我们通过ThreeJs的RGBELoader加载HDR静态文件渲染就完事了。具体如何实现呢?直接看以下代码:
先导入这个RGBELoader加载器,并且引入对应的HDR资源,这里新加了一个方法,通过RGBELoader加载器异步加载这个HDR资源,加载完成之后会得到一个texture纹理,将得到的纹理渲染到场景之上就完成了这个3D全景视图的开发了。最后在mounted生命周期上调用一下这个addHdr方法就实现了效果:也就是:https://live.csdn.net/v/297465
注:当然了这里你也可以不通过import引入HDR,直接将HDR地址写到loadAsync()当中也是一样。但是由于Vue是会先编译的,如果你没有对HDR文件编译打包进行处理,这里直接写路径是找不到这个资源的
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader'
import BOLLROOM from '../assets/hdr/ballroom_4k.hdr'
addHdr() {
rgbeloader.loadAsync(BOLLROOM).then((texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping
scene.background = texture
scene.environment = texture
})
}
这里推荐几个获取HDR资源的网站:
- polyhaven https://polyhaven.com/zh/hdris
- 爱给网 https://www.aigei.com/s?q=HDR
6、直接通过图片纹理进行实现
在前面我们可以创建一个3D的正方体,并且我们发现给正方体设置texture的时候可以渲染两面,那这一个的实现思路就是:给正方体设置的大一点,而我们的视角(摄像头)设置到正方体的内部,这样我们看到的就是正方体的内部六个面,而再通过加载图片得到纹理,将纹理依次设置到六个面上,这样从里面看起来依然还是全景视图的效果。看一下代码如何实现:
- 这里先引入对应六个面的图片,
- 而后创建一个101010的正方体(此时设置的视角在正方体内部)
- 创建数组 arr,存储了 6 张图片的路径,用于作为正方体的纹理贴图。
- 新建数组 boxMaterial,遍历 arr 数组中所有的元素 item,对于每个 item,加载其对应的图片纹理并将其添加到 boxMaterial 数组中创建一个包含图片纹理的 MeshBasicMaterial 对象。
- 使用上述创建好的 boxMaterial 数组,新建一个 Mesh 对象 cube,并将 geometry 和 boxMaterial 分别传入其中初始化。
- 将正方体的 geometry 进行缩放 transform,使其在 z 轴方向翻转(因为 WebGL 中 z 轴默认为屏幕内,而 Three.js 中 y 轴默认为屏幕内),从而让正方体可以面向摄像机。
- 最后通过 scene.add() 方法将正方体 cube 添加到场景中,使其在渲染时显示出来。
查看效果:https://live.csdn.net/v/297468
// 先引入六个面的图片
import ONE from '../assets/img/1.jpg'
import TWO from '../assets/img/2.jpg'
import TWOPLUS from '../assets/img/3.jpg'
import FOUR from '../assets/img/4.jpg'
import FIVE from '../assets/img/5.jpg'
import SIX from '../assets/img/6.jpg'
addBox() {
const geometry = new THREE.BoxGeometry(10, 10, 10);
var arr = [ONE, TWO, TWOPLUS, FOUR, FIVE, SIX]
var boxMaterial = []
arr.forEach(item => {
let textureLoader = new THREE.TextureLoader().load(item)
boxMaterial.push(new THREE.MeshBasicMaterial({ map: textureLoader }))
})
console.log(boxMaterial)
const cube = new THREE.Mesh(geometry, boxMaterial);
cube.geometry.scale(1, 1, -1)
scene.add(cube);
},