使用Three.js实现3D小区模型,需要涉及到对于小区场景的设置、建筑物的创建、道路信息、绿化信息、灯光信息等各种不同的元素创建。下面我们就来通过一个简单的例子来实现一个简单的小区3D模型。
准备工作
首先需要安装Three.js,可以通过CDN或npm安装Three.js
CDN方式: 在HTML文件中引入Three.js库
<script src="https://cdn.jsdelivr.net/npm/three@0.146.0/build/three.min.js"></script>
npm方式: 在项目目录中运行npm install three,然后在JavaScript中导入。
接下来,就是创建一个基本的HTML文件,包含一个<canvas>元素用于渲染3D模型。下面我们就来按照步骤一步一步的搭建相关的场景。
创建场景、相机和渲染器
首先,就需要我们去创建一个Three.js的基本场景,在这个场景中主要包括了场景、相机和渲染器信息。
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 10, 20);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
添加地面
创建完场景之后,接下来就是创建地面信息,可以通过PlaneGeometry来进行创建,如下所示。
// 创建地面
const geometry = new THREE.PlaneGeometry(100, 100);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide });
const plane = new THREE.Mesh(geometry, material);
plane.rotation.x = - Math.PI / 2; // 将平面旋转到水平
scene.add(plane);
创建建筑物
地面创建好之后,接下来就是创建地面上的建筑物信息,通过BoxGeometry来表示建筑物的信息,然后通过根据需要创建不同大小的盒子代表不同的楼宇,如下所示。
// 创建建筑物
function createBuilding(x, y, z, width, height, depth, color) {
const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshBasicMaterial({ color: color });
const building = new THREE.Mesh(geometry, material);
building.position.set(x, y + height / 2, z); // 确保建筑物底部位于地面上
scene.add(building);
}
// 创建几个建筑物
createBuilding(-10, 0, -10, 5, 10, 5, 0xff0000); // 红色建筑
createBuilding(10, 0, -10, 6, 15, 6, 0x0000ff); // 蓝色建筑
添加道路
道路可以使用PlaneGeometry并设置为灰色或黑色来表示。
// 创建道路
function createRoad(x, z, width, depth) {
const geometry = new THREE.PlaneGeometry(width, depth);
const material = new THREE.MeshBasicMaterial({ color: 0x333333 });
const road = new THREE.Mesh(geometry, material);
road.rotation.x = -Math.PI / 2;
road.position.set(x, 0.01, z); // 稍微抬高道路,以避免与地面重叠
scene.add(road);
}
// 创建一条道路
createRoad(0, 0, 4, 50); // 贯穿小区的主干道
添加绿化
绿化可以用SphereGeometry和CylinderGeometry来表示树木。
// 创建树木
function createTree(x, z) {
const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.5, 3);
const trunkMaterial = new THREE.MeshBasicMaterial({ color: 0x8B4513 });
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
trunk.position.set(x, 1.5, z);
const crownGeometry = new THREE.SphereGeometry(2);
const crownMaterial = new THREE.MeshBasicMaterial({ color: 0x228B22 });
const crown = new THREE.Mesh(crownGeometry, crownMaterial);
crown.position.set(x, 5, z);
scene.add(trunk);
scene.add(crown);
}
// 创建几棵树
createTree(-5, 10);
createTree(5, 15);
添加灯光
通过DirectionalLight或PointLight,来表示灯光效果。
// 添加环境光
const ambientLight = new THREE.AmbientLight(0x404040); // 环境光
scene.add(ambientLight);
// 添加方向光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 20, 10);
scene.add(directionalLight);
渲染循环
最后,创建一个渲染循环来渲染场景。
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
交互与控制
为了更好地查看模型,可以添加OrbitControls。
// 添加轨道控制器
const controls = new THREE.OrbitControls(camera, renderer.domElement);
当然我们也可以根据需要进一步丰富模型,比如添加更多细节、纹理、交互等。也可以使用工具如Blender制作复杂的建筑模型,并导入到Three.js中。
全量代码实现
下面是全量代码实现,可以根据自己的需求来调整相关的布局实现一个好看的3D小区地图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 小区模型 - 十字路口</title>
<style>
body {
margin: 0;
}
canvas {
display: block;
}
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three@0.146.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.146.0/examples/js/controls/OrbitControls.js"></script>
<script>
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 30, 70);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建地面
const groundGeometry = new THREE.PlaneGeometry(100, 100);
const groundMaterial = new THREE.MeshStandardMaterial({
color: 0x8B4513
}); // 土壤色
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2; // 将平面旋转到水平
scene.add(ground);
// 创建建筑物
function createBuilding(x, y, z, width, height, depth, color) {
const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshStandardMaterial({
color: color
});
const building = new THREE.Mesh(geometry, material);
building.position.set(x, y + height / 2, z); // 确保建筑物底部位于地面上
scene.add(building);
}
// 添加建筑物,避免放置在十字路口的中心区域
createBuilding(-25, 0, -15, 6, 12, 6, 0xd3d3d3); // 浅灰色建筑
createBuilding(25, 0, -15, 6, 12, 6, 0xadd8e6); // 浅蓝色建筑
createBuilding(-15, 0, 25, 8, 20, 8, 0xffe4b5); // 米色建筑
// 创建道路
function createRoad(x, z, width, depth) {
const geometry = new THREE.PlaneGeometry(width, depth);
const material = new THREE.MeshStandardMaterial({
color: 0x696969
}); // 深灰色
const road = new THREE.Mesh(geometry, material);
road.rotation.x = -Math.PI / 2;
road.position.set(x, 0.01, z); // 稍微抬高道路,以避免与地面重叠
scene.add(road);
}
// 添加道路,形成十字路口
createRoad(0, 0, 10, 100); // 横向主干道
createRoad(0, 0, 100, 10); // 纵向主干道
// 创建树木
function createTree(x, z) {
const trunkGeometry = new THREE.CylinderGeometry(0.5, 0.5, 3);
const trunkMaterial = new THREE.MeshStandardMaterial({
color: 0x8b4513
}); // 深棕色
const trunk = new THREE.Mesh(trunkGeometry, trunkMaterial);
trunk.position.set(x, 1.5, z);
const crownGeometry = new THREE.SphereGeometry(2);
const crownMaterial = new THREE.MeshStandardMaterial({
color: 0x228b22
}); // 绿色
const crown = new THREE.Mesh(crownGeometry, crownMaterial);
crown.position.set(x, 5, z);
scene.add(trunk);
scene.add(crown);
}
// 添加树木
createTree(-30, -30);
createTree(30, -30);
createTree(-30, 30);
createTree(30, 30);
// 添加环境光
const ambientLight = new THREE.AmbientLight(0x404040); // 环境光
scene.add(ambientLight);
// 添加方向光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 50, 30);
scene.add(directionalLight);
// 添加轨道控制器
const controls = new THREE.OrbitControls(camera, renderer.domElement);
// 渲染循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
// 处理窗口大小变化
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>