在过去的几年里,我们见证了Web3D技术的飞速发展。从电商的产品展示到数据可视化,从在线教育到虚拟展览,3D内容正在成为现代Web体验的重要组成部分。随着WebGL的普及和硬件性能的提升,在浏览器中渲染高质量的3D场景已不再是难题。
本指南将带你系统了解Web3D开发生态,掌握核心工具链,并构建你的第一个Web3D应用。
WebGL - 底层图形接口
Three.js - 最流行的3D库
Babylon.js - 企业级选择
# 创建项目结构
mkdir web3d-project
cd web3d-project
npm init -y
# 安装核心依赖
npm install three
npm install @types/three --save-dev # TypeScript类型定义
# 开发工具
npm install vite --save-dev # 推荐构建工具
npm install dat.gui --save-dev # 调试界面// vite.config.js
export default {
server: {
port: 3000,
open: true
},
build: {
target: 'esnext',
minify: 'terser'
}
}import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
class Web3DApp {
constructor() {
this.init();
this.createScene();
this.animate();
}
init() {
// 创建渲染器
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(this.renderer.domElement);
// 创建场景
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(0x222222);
// 创建相机
this.camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
this.camera.position.set(5, 5, 5);
// 添加轨道控制器
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 20, 15);
this.scene.add(directionalLight);
// 响应式调整
window.addEventListener('resize', this.onWindowResize.bind(this));
}
createScene() {
// 创建几何体
const geometry = new THREE.BoxGeometry(2, 2, 2);
// 创建材质
const material = new THREE.MeshStandardMaterial({
color: 0x00aaff,
roughness: 0.2,
metalness: 0.8
});
// 创建立方体
this.cube = new THREE.Mesh(geometry, material);
this.scene.add(this.cube);
// 添加网格地面
const gridHelper = new THREE.GridHelper(20, 20, 0x444444, 0x888888);
this.scene.add(gridHelper);
}
animate() {
requestAnimationFrame(this.animate.bind(this));
// 旋转立方体
if (this.cube) {
this.cube.rotation.x += 0.01;
this.cube.rotation.y += 0.01;
}
this.controls.update();
this.renderer.render(this.scene, this.camera);
}
onWindowResize() {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
}
}
// 启动应用
new Web3DApp();import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
class ModelLoader {
constructor(scene) {
this.scene = scene;
this.loader = new GLTFLoader();
}
async loadModel(url) {
return new Promise((resolve, reject) => {
this.loader.load(
url,
(gltf) => {
const model = gltf.scene;
model.scale.set(0.5, 0.5, 0.5);
this.scene.add(model);
resolve(model);
},
(progress) => {
console.log(`加载进度: ${(progress.loaded / progress.total * 100)}%`);
},
(error) => {
reject(error);
}
);
});
}
}
// 使用示例
const modelLoader = new ModelLoader(scene);
modelLoader.loadModel('/models/robot.glb').then(model => {
console.log('模型加载完成');
});// 1. 实例化几何体(大量相同物体)
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial();
const meshCount = 1000;
for (let i = 0; i < meshCount; i++) {
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(
Math.random() * 100 - 50,
Math.random() * 100 - 50,
Math.random() * 100 - 50
);
scene.add(mesh);
}
// 2. 使用InstancedMesh进一步提高性能
const instancedMesh = new THREE.InstancedMesh(geometry, material, meshCount);
const matrix = new THREE.Matrix4();
for (let i = 0; i < meshCount; i++) {
matrix.setPosition(
Math.random() * 100 - 50,
Math.random() * 100 - 50,
Math.random() * 100 - 50
);
instancedMesh.setMatrixAt(i, matrix);
}
scene.add(instancedMesh);class ResourceManager {
constructor() {
this.textures = new Map();
this.models = new Map();
this.textureLoader = new THREE.TextureLoader();
}
async loadTexture(url) {
if (this.textures.has(url)) {
return this.textures.get(url);
}
return new Promise((resolve) => {
this.textureLoader.load(url, (texture) => {
texture.encoding = THREE.sRGBEncoding;
this.textures.set(url, texture);
resolve(texture);
});
});
}
dispose() {
this.textures.forEach(texture => texture.dispose());
this.textures.clear();
}
}// 自定义着色器材质
const vertexShader = `
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
uniform float time;
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vec3 color = vec3(
sin(vUv.x * 10.0 + time) * 0.5 + 0.5,
cos(vUv.y * 10.0 + time) * 0.5 + 0.5,
0.5
);
gl_FragColor = vec4(color, 1.0);
}
`;
const customMaterial = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms: {
time: { value: 0.0 }
}
});class InteractionHandler {
constructor(camera, scene, renderer) {
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
this.camera = camera;
this.scene = scene;
renderer.domElement.addEventListener('click', this.onClick.bind(this));
}
onClick(event) {
// 计算鼠标位置归一化坐标
this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 更新射线
this.raycaster.setFromCamera(this.mouse, this.camera);
// 检测相交物体
const intersects = this.raycaster.intersectObjects(this.scene.children, true);
if (intersects.length > 0) {
const object = intersects[0].object;
console.log('点击了:', object);
this.onObjectSelected(object);
}
}
onObjectSelected(object) {
// 实现选择效果
object.material.emissive.setHex(0xff0000);
setTimeout(() => {
object.material.emissive.setHex(0x000000);
}, 500);
}
}// vite构建优化配置
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
three: ['three'],
vendor: ['dat.gui', 'stats.js']
}
}
},
sourcemap: true
}
}<!-- 3D内容降级处理 -->
<div class="web3d-container">
<canvas id="webgl-canvas"></canvas>
<div class="fallback-content">
<img src="fallback-image.jpg" alt="3D模型预览">
<p>您的浏览器可能不支持WebGL,请使用现代浏览器查看3D内容</p>
</div>
</div>
<style>
.web3d-container {
position: relative;
}
.fallback-content {
position: absolute;
top: 0;
left: 0;
display: none;
}
.no-webgl .fallback-content {
display: block;
}
</style>
<script>
if (!detectWebGL()) {
document.querySelector('.web3d-container').classList.add('no-webgl');
}
</script>Web3D开发虽然涉及较多概念和技术栈,但通过现代工具链的辅助,入门门槛已经大大降低。从简单的几何体渲染开始,逐步深入到着色器编程和性能优化,每一步都能带来明显的视觉和交互提升。
记住,优秀的Web3D体验不仅仅是技术的堆砌,更重要的是合理的性能平衡、渐进增强的兼容性策略,以及为用户提供真正的沉浸感。
开始构建你的第一个Web3D项目吧!从一个小立方体开始,逐渐添加光照、材质、交互,最终创造出令人惊叹的3D体验。