Three.js实战:Vue3项目中的海上场景渲染
创作时间:
2025-01-21 17:49:27
作者:
@小白创作中心
Three.js实战:Vue3项目中的海上场景渲染
本文将手把手教你使用Three.js在Vue3项目中创建一个逼真的海上场景,包括船模、水面效果和光照设置。如果你对Three.js和Vue3有一定了解,那么这篇文章将帮助你掌握更高级的场景渲染技巧。
初始化场景
首先,我们需要在Vue3项目中安装Three.js:
npm i three
然后,我们可以在Vue3组件中初始化场景、相机和渲染器:
<template>
<div id="home" class="w-full h-full"></div>
</template>
<script lang="ts" setup>
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(1, 1, 1);
scene.add(camera);
const ambientLight = new THREE.AmbientLight('white', 1);
scene.add(ambientLight);
const light = new THREE.DirectionalLight(0xffffff, 3);
scene.add(light);
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);
const render = () => {
renderer.render(scene, camera);
requestAnimationFrame(render);
};
onMounted(() => {
document.getElementById('home')?.appendChild(renderer.domElement);
render();
});
</script>
如果在项目中引入Three.js时遇到类型声明问题,可以在env.d.ts文件中添加以下声明:
declare module 'three'
declare module 'three/examples/jsm/objects/Water2';
declare module 'three/examples/jsm/controls/OrbitControls';
初始化场景的效果如下:
加载模型
首先,我们需要从Sketchfab获取GLB或GLTF格式的模型。使用GLTFLoader加载GLB模型时,需要注意GLB模型在加载后需要通过gltf.scene.children[0]获取模型实例。由于模型的大小和朝向可能不符合预期,我们需要对模型进行缩放和旋转处理:
let model: {
scale: { set: (arg0: number, arg1: number, arg2: number) => void; };
rotation: { z: number; };
traverse: (arg0: (item: { material: { name: string; }; }) => void) => void;
position: { z: number; };
};
const addShip = () => {
const gltfLoader = new GLTFLoader();
gltfLoader.load('./src/assets/glb/pirate_ship.glb', (gltf: any) => {
model = gltf.scene.children[0];
const scale = 0.001;
model.scale.set(scale, scale, scale);
model.rotation.z = Math.PI;
scene.add(model);
});
};
渲染场景贴图
为了使场景背景不再是一片黑色,我们可以加载一张HDR图片作为环境贴图。Three.js提供了多种纹理映射模式,其中EquirectangularReflectionMapping适用于全景环境贴图:
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
const rgbLoader = new RGBELoader();
const addHdr = () => {
rgbLoader.loadAsync('./src/assets/hdr/sea_2k.hdr').then((texture: { mapping: any; }) => {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.background = texture;
scene.environment = texture;
});
};
光照增强
为了使船体模型的细节更加清晰,我们需要增强场景的光照强度:
const ambientLight = new THREE.AmbientLight('white', 50);
ambientLight.position.set(10, 10, 10);
scene.add(ambientLight);
const light = new THREE.DirectionalLight('#fff', 50);
light.position.set(10, 10, 10);
scene.add(light);
增强光照后的效果如下:
船体模型纹理
全纹理
我们可以通过设置模型的材质来改变其外观。这里使用MeshPhongMaterial材质,并为其添加环境贴图:
const addTexture = () => {
const textureLoader = new THREE.TextureLoader().load('./src/assets/image/bg.jpg');
textureLoader.mapping = THREE.EquirectangularRefractionMapping;
return textureLoader;
};
model.material = new THREE.MeshPhongMaterial({
color: 0xffffff,
envMap: addTexture(),
refractionRatio: 0.75,
reflectivity: 0.99
});
分块纹理
通过遍历模型的各个部分,我们可以为不同的模型块设置不同的材质:
model.traverse((item: { material: { name: string; }; }) => {
const name = item.material?.name || '';
if (name.includes('Main')) {
item.material = colorMaterial('#e7a23f');
} else if (name.includes('Sail')) {
item.material = colorMaterial('#fff');
} else if (name.includes('Mat')) {
item.material = colorMaterial('#826b48');
} else if (name.includes('Polygon_Reduction_1__0')) {
item.material = colorMaterial('#f40');
} else if (name.includes('material')) {
item.material = colorMaterial('#000');
}
});
const colorMaterial = (color: string) => {
return new THREE.MeshLambertMaterial({
color
});
};
加载水面
Water
Three.js提供了两种水面实现方式:Water和Water2。这里我们先介绍Water的使用方法:
import { Water } from 'three/examples/jsm/objects/Water';
let water: any;
const addWater = () => {
const waterGeometry = new THREE.PlaneGeometry(10000, 10000);
water = new Water(
waterGeometry,
{
textureWidth: 512,
textureHeight: 512,
waterNormals: new THREE.TextureLoader().load('./src/Water.jpg', (texture: any) => {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
}),
sunDirection: new THREE.Vector3(),
sunColor: 0xffffff,
waterColor: 0xffffff,
distortionScale: 3.7
}
);
water.rotation.x = -Math.PI / 2;
water.position.y = -0.4;
scene.add(water);
};
const render = () => {
renderer.render(scene, camera);
water.material.uniforms['time'].value += 1.0 / 60.0;
requestAnimationFrame(render);
};
Water2
使用Water2时需要注意配置normalMap0和normalMap1属性:
import { Water } from 'three/examples/jsm/objects/Water2';
const addWater = () => {
const waterGeometry = new THREE.CircleBufferGeometry(300, 64);
const water = new Water(waterGeometry, {
textureWidth: 1024,
textureHeight: 1024,
color: '#fff',
flowDirection: new THREE.Vector2(1, 1),
scale: 1,
reflectivity: 0.3,
normalMap0: new THREE.TextureLoader().load('./src/Water.jpg'),
normalMap1: new THREE.TextureLoader().load('./src/Water.jpg')
});
water.rotation.x = -Math.PI / 2;
water.position.y = -0.4;
scene.add(water);
};
视口角度限制
为了限制相机的视角范围,我们可以调整OrbitControls的参数:
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.maxPolarAngle = Math.PI * 0.45;
controls.minDistance = 5.0;
controls.maxDistance = 15.0;
controls.update();
调整后的视口效果如下:
热门推荐
中国学生营养日:食育教育正当时
中国营养学会发布新标准:青少年这样吃才健康
南方小年与北方小年的十个有趣的差异,你知道几个?
2025年,“护住”你的存款?央行发布通知,以后存钱记得先看这点
爱的本质:从哲学到心理学的多维解读
格华止和国产二甲双胍有什么区别
进口药“消失”在公立医院,普通患者的出路在哪里?
跟着小田游浙江,吃住行玩全搞定!
浙江古镇探秘:水乡风貌与历史文化交融
浙江亲子游必打卡景点推荐:杭州乐园、杭州野生动物世界、凤凰山主题乐园、横店影视城
殡葬知识 | 丧事做七有哪些流程?从头七到断七,带你看懂做七
灵魂怀孕的歌曲如何唤起内心深藏的情感?
黄钟大吕的诗词意境:从陈文蔚的诗中品味文人生活
故宫藏金编钟:黄钟大吕背后的音律奥秘
黄钟大吕:古乐界的顶流音阶
黄钟大吕:古代帝王祭祀的神秘音律
秋季遛娃秘籍!10个户外游戏,让娃嗨翻天,惊喜效果让你瞠目结舌!
培养孩子的兴趣爱好就是培养能力
单田芳评书《隋唐演义》:传统艺术的创新演绎
探究喝茶与钙流失的关系:如何平衡饮茶习惯以维护骨骼健康
饮茶更易骨质疏松?研究揭适量饮用防骨折,每日饮几多最有效?
饮茶可防骨质疏松?研究揭示适量饮用能降低骨折风险
《隋唐演义》里的那些“流量担当”
妈妈的唠叨是最美的旋律
从“一点就炸”到情绪管理达人:掌握6大实用技巧
远离脾气暴躁:实用情绪管理指南
脾气暴躁的背后,竟是心理创伤?
生成式AI医疗应用的法律挑战与应对
光储充一体化无缝集成——智能微网的技术实现与案例分析
齐娜再登热搜,叶罗丽粉丝热议