我的房间 - 3D 模型 (Three.js)
by EpicCoder88233 lines9.4 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的房间 - 3D 模型 (Three.js)</title>
<style>
body { margin: 0; overflow: hidden; background-color: #222; }
canvas { display: block; }
#info {
position: absolute;
top: 10px;
width: 100%;
text-align: center;
color: white;
font-family: sans-serif;
pointer-events: none;
}
</style>
</head>
<body>
<div id="info">鼠标左键拖拽旋转 | 滚轮缩放 | 右键平移</div>
<!-- 引入 Three.js 和相机控制器 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<script>
// 1. 初始化场景、相机和渲染器
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f4f8); // 柔和的背景色
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(8, 12, 18); // 设置相机初始位置
camera.lookAt(0, 3, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true; // 开启阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// 相机控制器
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.set(0, 3, 0);
controls.update();
// 2. 设置光照
// 环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
// 主光源(模拟窗外透进来的自然光)
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(10, 15, 10);
dirLight.castShadow = true;
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
dirLight.shadow.camera.near = 0.5;
dirLight.shadow.camera.far = 50;
dirLight.shadow.camera.left = -15;
dirLight.shadow.camera.right = 15;
dirLight.shadow.camera.top = 15;
dirLight.shadow.camera.bottom = -15;
scene.add(dirLight);
// 3. 辅助函数:创建盒子几何体
function createBox(w, h, d, color, x, y, z, castShadow = true, receiveShadow = true) {
const geometry = new THREE.BoxGeometry(w, h, d);
const material = new THREE.MeshStandardMaterial({ color: color });
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(x, y, z);
mesh.castShadow = castShadow;
mesh.receiveShadow = receiveShadow;
scene.add(mesh);
return mesh;
}
// 4. 构建房间基础结构
// 地板 (浅色地砖)
const floorGeo = new THREE.PlaneGeometry(20, 20);
const floorMat = new THREE.MeshStandardMaterial({ color: 0xe3e5e8, roughness: 0.8 });
const floor = new THREE.Mesh(floorGeo, floorMat);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
// 左侧墙壁
createBox(0.5, 12, 20, 0xfdfaf7, -10, 6, 0, false, true);
// 后侧墙壁
createBox(20, 12, 0.5, 0xfdfaf7, 0, 6, -10, false, true);
// 右侧墙壁 (留出空间模拟窗户和窗帘)
createBox(0.5, 12, 20, 0xfdfaf7, 10, 6, 0, false, true);
// 墙面水渍 (用半透明材质模拟)
const stainGeo = new THREE.PlaneGeometry(3, 6);
const stainMat = new THREE.MeshStandardMaterial({ color: 0xcdaa7d, transparent: true, opacity: 0.4 });
const stain = new THREE.Mesh(stainGeo, stainMat);
stain.position.set(8.8, 3, -8);
stain.rotation.y = -Math.PI / 2;
scene.add(stain);
// 5. 构建家具
// --- 空调 ---
createBox(4, 1.2, 0.8, 0xffffff, 0, 9, -9.5);
// --- 书桌 (右侧) ---
const deskX = 5, deskY = 2.5, deskZ = -7;
// 桌面
createBox(7, 0.3, 3, 0x8b5a2b, deskX, deskY, deskZ);
// 抽屉柜
createBox(2.5, 4, 2.8, 0x754a22, deskX - 2.25, deskY - 2.15, deskZ);
// --- 电脑显示器 ---
createBox(2.5, 1.8, 0.2, 0x1a1a1a, deskX + 2, deskY + 1.05, deskZ - 0.5);
// 显示器底座
createBox(0.8, 0.2, 0.8, 0x333333, deskX + 2, deskY + 0.2, deskZ - 0.5);
// --- 电脑主机 ---
createBox(1.5, 2.5, 2, 0x222222, deskX + 2.5, deskY - 1.25, deskZ + 0.5);
// --- 粉色椅子 ---
const chairX = 3, chairY = 1.5, chairZ = -3;
// 坐垫
createBox(2, 0.5, 2, 0xf4b6c2, chairX, chairY, chairZ);
// 靠背 (稍微倾斜)
const backGeo = new THREE.BoxGeometry(2, 2, 0.4);
const backMat = new THREE.MeshStandardMaterial({ color: 0xf4b6c2 });
const back = new THREE.Mesh(backGeo, backMat);
back.position.set(chairX, chairY + 1, chairZ - 0.8);
back.rotation.x = -0.2;
back.castShadow = true;
scene.add(back);
// 椅子腿 (简化为圆柱)
const legGeo = new THREE.CylinderGeometry(0.1, 0.1, 1.5, 8);
const legMat = new THREE.MeshStandardMaterial({ color: 0xffffff });
const leg = new THREE.Mesh(legGeo, legMat);
leg.position.set(chairX, chairY - 0.75, chairZ);
leg.castShadow = true;
scene.add(leg);
// 椅子底座
const baseGeo = new THREE.CylinderGeometry(1.2, 1.2, 0.2, 16);
const base = new THREE.Mesh(baseGeo, legMat);
base.position.set(chairX, chairY - 1.5, chairZ);
base.castShadow = true;
scene.add(base);
// --- 落地风扇 (左侧) ---
const fanX = -6, fanZ = -4;
// 风扇底座
const fanBaseGeo = new THREE.CylinderGeometry(1.2, 1.2, 0.2, 16);
const fanBaseMat = new THREE.MeshStandardMaterial({ color: 0xeeeeee });
const fanBase = new THREE.Mesh(fanBaseGeo, fanBaseMat);
fanBase.position.set(fanX, 0.1, fanZ);
fanBase.castShadow = true;
scene.add(fanBase);
// 风扇杆
const fanPoleGeo = new THREE.CylinderGeometry(0.1, 0.1, 4, 8);
const fanPole = new THREE.Mesh(fanPoleGeo, fanBaseMat);
fanPole.position.set(fanX, 2, fanZ);
fanPole.castShadow = true;
scene.add(fanPole);
// 风扇头部 (圆形外壳)
const fanHeadGeo = new THREE.CylinderGeometry(1.5, 1.5, 0.5, 32);
const fanHeadMat = new THREE.MeshStandardMaterial({ color: 0x555555 });
const fanHead = new THREE.Mesh(fanHeadGeo, fanHeadMat);
fanHead.position.set(fanX, 4.5, fanZ);
fanHead.rotation.x = Math.PI / 2;
fanHead.rotation.z = Math.PI / 2;
fanHead.castShadow = true;
scene.add(fanHead);
// 风扇网罩 (用线框圆柱体模拟)
const wireframeGeo = new THREE.CylinderGeometry(1.6, 1.6, 0.6, 16);
const wireframeMat = new THREE.MeshBasicMaterial({ color: 0xaaaaaa, wireframe: true });
const wireframe = new THREE.Mesh(wireframeGeo, wireframeMat);
wireframe.position.set(fanX, 4.5, fanZ);
wireframe.rotation.x = Math.PI / 2;
wireframe.rotation.z = Math.PI / 2;
scene.add(wireframe);
// --- 垃圾桶 (右侧) ---
const trashGeo = new THREE.CylinderGeometry(0.8, 0.6, 1.5, 16);
const trashMat = new THREE.MeshStandardMaterial({ color: 0x8fa3ad });
const trash = new THREE.Mesh(trashGeo, trashMat);
trash.position.set(8, 0.75, -4);
trash.castShadow = true;
scene.add(trash);
// --- 床铺 (前景) ---
// 床架/凉席
const bedGeo = new THREE.BoxGeometry(18, 0.8, 7);
const bedMat = new THREE.MeshStandardMaterial({ color: 0x9e5a38, roughness: 0.9 });
const bed = new THREE.Mesh(bedGeo, bedMat);
bed.position.set(0, 0.4, 6);
bed.receiveShadow = true;
scene.add(bed);
// 床铺边缘
createBox(18.2, 0.2, 7.2, 0xd3d3d3, 0, 0.9, 6, false, true);
// --- 地上的蓝色圆圈 ---
const ringGeo = new THREE.TorusGeometry(1, 0.1, 16, 32);
const ringMat = new THREE.MeshStandardMaterial({ color: 0x4a90e2 });
const ring = new THREE.Mesh(ringGeo, ringMat);
ring.position.set(-7, 0.1, -1);
ring.rotation.x = -Math.PI / 2;
scene.add(ring);
// --- 窗帘 (右侧) ---
const curtainGeo = new THREE.PlaneGeometry(3, 12);
const curtainMat = new THREE.MeshStandardMaterial({ color: 0xffffff, side: THREE.DoubleSide, transparent: true, opacity: 0.8 });
const curtain = new THREE.Mesh(curtainGeo, curtainMat);
curtain.position.set(9.8, 6, 2);
curtain.rotation.y = -Math.PI / 2;
scene.add(curtain);
// 6. 动画循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
// 7. 窗口大小适配
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// 启动
animate();
</script>
</body>
</html>Game Source: 我的房间 - 3D 模型 (Three.js)
Creator: EpicCoder88
Libraries: three
Complexity: complex (233 lines, 9.4 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: 3d-three-js-epiccoder88" to link back to the original. Then publish at arcadelab.ai/publish.