最近在学习threejs渲染时,发现threejs可以渲染hdr文件,之前不清楚hdr这种文件是做什么的,于是查询了一番,原来是环境贴图。于是也学一下threejs如何渲染环境贴图。
效果截图
渲染动画
引入依赖
// threejs
import * as THREE from "three";
//轨道控制器
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
// hdr加载器
import {RGBELoader} from "three/examples/jsm/loaders/RGBELoader";
// 连接判断,用来控制请求地址
import {link} from "@/config/config.js";
创建场景
const scene = new THREE.Scene();
获取div宽高
并不是所有threejs项目都是全屏项目,所以我们在渲染前先创建一个div,让这个div跟随父div大小变化,这样就可以快速将项目移植到对应的项目中了。
that.width = that.$refs.canvasGLTF.offsetWidth
that.height = that.$refs.canvasGLTF.offsetHeight
创建相机
const camera = that.camera = new THREE.PerspectiveCamera(
75,
that.width / that.height,
0.1,
1000
);
camera.aspect = that.width / that.height;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
// 设置相机位置
camera.position.set(0, 0, 20);
scene.add(camera);
初始化渲染器
const renderer = that.renderer = new THREE.WebGLRenderer({
logarithmicDepthBuffer: true,
antialias: true,
});
// 设置渲染的尺寸大小
renderer.setSize(that.width, that.height);
// 开启场景中的阴影贴图
renderer.shadowMap.enabled = true;
renderer.physicallyCorrectLights = true;
renderer.setClearColor(0xcccccc, 1);
renderer.autoClear = false;
// 设置电影渲染模式
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.sortObjects = true;
renderer.logarithmicDepthBuffer = true;
renderer.setPixelRatio(window.devicePixelRatio);
创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用.update()。
controls.enableDamping = true;
添加坐标轴辅助器
// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
刷新帧
function render() {
controls.update();
renderer.render(scene, camera);
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render);
}
render();
添加hdr环境渲染
这里我们通过RGBELoader来加载hdr环境贴图,并通过EquirectangularReflectionMapping
来设置材质的反射映射,
// 添加hdr环境纹理
const loader = new RGBELoader();
loader.load(link + "demo.hdr", function (texture) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture;
scene.environment = texture;
});
显示加载进度
当资源过大时,加载文件时,一直处在黑屏状态很不友好,所以我们显示一个进度加载状态,来给用户一个实时反馈,这里我们使用loading,RGBELoader环境贴图加载器中的manager
可以实现我们想要的效果。
我们在加载资源的时候增加一个方法:handleProgress
用来处理资源加载回调。
资源加载
loader.load(link + "demo.hdr", function (texture) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture;
scene.environment = texture;
});
加载方法回调
function handleProgress(progressEvent) {
console.log('handleProgress', progressEvent.loaded, progressEvent.total)
var div = document.getElementById('LoadingInfo')
div.style.display = 'flex'
div.innerText = `加载模型中:${(progressEvent.loaded / progressEvent.total * 100).toFixed(0)}%`
if ((progressEvent.loaded / progressEvent.total * 100).toFixed(0) >= 100) {
div.style.display = 'none'
}
}
加载zip文件
three项目的资源一般文件都很大,小的几兆,大的几百兆,文件太大的话,对带宽要求会很高会拖慢加载进度,一般会对素材进行压缩处理,这里我们对环境贴图资源进行zip压缩,通过axios请求数据,解压后,再通过RGBELoader加载器加载环境贴图。
环境贴图资源zip压缩前是13.7M,zip压缩后是10.8M,压缩了2.9M.
压缩完成后,接下来我们来写请求资源的方法,使用axios去请求我们的zip资源,并通过onDownloadProgress来获取下载进度
// 获取zip资源
async getZipData() {
var that = this;
try {
const response = await axios.get(link + "demo.zip", {
responseType: 'arraybuffer',
onDownloadProgress: (progressEvent) => {
let percentCompleted = Math.round(progressEvent.loaded * 100 / progressEvent.total)
var div = document.getElementById('LoadingInfo')
div.style.display = 'flex'
div.innerText = `下载资源中: ${percentCompleted}%`
}
});
let files = new window.File([response.data], 'zipFile', {type: 'zip'});
var new_zip = new JSZip();
// 解压缩文件对象
const result = await new_zip.loadAsync(files);
// 压缩包的模型文件列表
let fileList = result.files;
for (let key in fileList) {
// 读取模型文件内容
const content = await new_zip.file(key).async('arraybuffer');
// Blob构造文件地址,通过url加载模型
let blob = new Blob([content]);
let modelUrls = URL.createObjectURL(blob);
// 处理每个模型文件地址
return new Promise((resolve, reject) => {
resolve(modelUrls);
});
}
} catch (error) {
console.error("Error fetching data:", error);
}
},
然后我们再通过RGBELoader
来加载我们的资源。
const loader = new RGBELoader();
// 下载zip资源
var modelUrls = await that.getZipData()
loader.load(modelUrls, function (texture) {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture;
scene.environment = texture;
}, that.handleProgress);
完整实例代码下载
项目运行环境 vue3 vite js nodejs 14