<!DOCTYPE html>
<html lang="fa">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
<title>Realistic House 3D</title>
<style>
html,body{
margin:0;
width:100%;
height:100%;
overflow:hidden;
background:#050608;
font-family:Arial,sans-serif;
}
canvas{
display:block;
}
#hud{
position:fixed;
left:18px;
top:18px;
color:white;
background:rgba(0,0,0,.42);
padding:12px 16px;
border-radius:12px;
backdrop-filter:blur(8px);
z-index:5;
pointer-events:none;
font-size:14px;
}
#cross{
position:fixed;
left:50%;
top:50%;
width:5px;
height:5px;
margin:-2px;
background:white;
border-radius:50%;
box-shadow:0 0 5px #fff;
z-index:4;
pointer-events:none;
}
#hint{
position:fixed;
bottom:22px;
left:50%;
transform:translateX(-50%);
color:white;
background:rgba(0,0,0,.55);
padding:10px 18px;
border-radius:20px;
z-index:5;
pointer-events:none;
}
#loading{
position:fixed;
right:18px;
top:18px;
color:#ddd;
background:rgba(0,0,0,.35);
padding:8px 12px;
border-radius:10px;
z-index:5;
}
</style>
</head>
<body>
<div id="hud">
WASD حرکت<br>
Shift دویدن<br>
Space پرش<br>
V تغییر دوربین<br>
ESC آزاد کردن موس
</div>
<div id="cross">+</div>
<div id="hint">برای کنترل دوربین داخل صفحه کلیک کن</div>
<div id="loading">3D Scene</div>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from "three";
import {GLTFLoader} from "three/addons/loaders/GLTFLoader.js";
// ============================================================
// SCENE
// ============================================================
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x8da6b8);
scene.fog = new THREE.FogExp2(
0x8799a3,
0.008
);
// ============================================================
// CAMERA
// ============================================================
const camera = new THREE.PerspectiveCamera(
72,
innerWidth / innerHeight,
0.05,
700
);
camera.position.set(
0,
1.7,
18
);
// ============================================================
// RENDERER
// ============================================================
const renderer = new THREE.WebGLRenderer({
antialias:true,
powerPreference:"high-performance"
});
renderer.setSize(
innerWidth,
innerHeight
);
renderer.setPixelRatio(
Math.min(devicePixelRatio,1.5)
);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.outputColorSpace =
THREE.SRGBColorSpace;
renderer.toneMapping =
THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.05;
document.body.appendChild(
renderer.domElement
);
// ============================================================
// LIGHTING
// ============================================================
// محیط
const hemi = new THREE.HemisphereLight(
0xbfd7ff,
0x3b3028,
1.8
);
scene.add(hemi);
// خورشید
const sun = new THREE.DirectionalLight(
0xffe5bd,
4.2
);
sun.position.set(
-60,
90,
35
);
sun.castShadow = true;
sun.shadow.mapSize.width = 2048;
sun.shadow.mapSize.height = 2048;
sun.shadow.camera.left = -80;
sun.shadow.camera.right = 80;
sun.shadow.camera.top = 80;
sun.shadow.camera.bottom = -80;
sun.shadow.bias = -0.0003;
scene.add(sun);
// نور گرم خانه
const houseLight = new THREE.PointLight(
0xffbd72,
55,
25
);
houseLight.position.set(
0,
3.1,
-2
);
houseLight.castShadow = true;
scene.add(houseLight);
// نور اتاق کامپیوتر
const monitorLight = new THREE.PointLight(
0x5e9cff,
20,
10
);
monitorLight.position.set(
1,
2.2,
-4
);
scene.add(monitorLight);
// ============================================================
// MATERIALS
// ============================================================
function mat(
color,
roughness=.7,
metalness=0
){
return new THREE.MeshStandardMaterial({
color,
roughness,
metalness
});
}
const concrete = mat(
0x777873,
.9
);
const wallMat = mat(
0xd6d0c4,
.82
);
const floorMat = mat(
0x73543d,
.5
);
const darkWood = mat(
0x241712,
.38
);
const blackMat = mat(
0x090b0d,
.25,
.55
);
const glassMat =
new THREE.MeshPhysicalMaterial({
color:0x9bdcff,
roughness:.08,
metalness:.05,
transmission:.5,
transparent:true,
opacity:.42
});
// ============================================================
// HELPERS
// ============================================================
const colliders = [];
function cube(
x,y,z,
sx,sy,sz,
material,
collision=true
){
const g =
new THREE.BoxGeometry(
sx,sy,sz
);
const m =
new THREE.Mesh(
g,
material
);
m.position.set(
x,y,z
);
m.castShadow = true;
m.receiveShadow = true;
scene.add(m);
if(collision){
colliders.push({
minX:x-sx/2,
maxX:x+sx/2,
minY:y-sy/2,
maxY:y+sy/2,
minZ:z-sz/2,
maxZ:z+sz/2
});
}
return m;
}
function cylinder(
x,y,z,
radius,
height,
material
){
const g =
new THREE.CylinderGeometry(
radius,
radius,
height,
32
);
const m =
new THREE.Mesh(
g,
material
);
m.position.set(
x,y,z
);
m.castShadow=true;
m.receiveShadow=true;
scene.add(m);
return m;
}
// ============================================================
// GROUND
// ============================================================
const ground =
cube(
0,
-0.25,
0,
180,
.5,
180,
mat(0x40523d,.96),
true
);
// road
cube(
0,
0.01,
48,
25,
.08,
100,
mat(0x25272a,.92),
false
);
// road lines
for(let z=-2;z<100;z+=8){
cube(
0,
.065,
z,
.35,
.02,
4,
mat(0xd4d0b7,.75),
false
);
}
// ============================================================
// HOUSE
// ============================================================
// foundation
cube(
0,
.35,
-2,
26,
.7,
20,
concrete
);
// floor
cube(
0,
.76,
-2,
24.8,
.18,
18.8,
floorMat
);
// outer walls
cube(
-12.2,
4.2,
-2,
.6,
7,
19,
wallMat
);
cube(
12.2,
4.2,
-2,
.6,
7,
19,
wallMat
);
cube(
0,
4.2,
-11.2,
24.8,
7,
.6,
wallMat
);
// back wall
cube(
0,
4.2,
7.2,
24.8,
7,
.6,
wallMat
);
// ============================================================
// FRONT WINDOWS
// ============================================================
function windowFrame(
x,
z,
width
){
cube(
x,
4.1,
z,
width,
4.2,
.18,
glassMat,
false
);
cube(
x-width/2,
4.1,
z,
.16,
4.5,
.3,
darkWood,
false
);
cube(
x+width/2,
4.1,
z,
.16,
4.5,
.3,
darkWood,
false
);
cube(
x,
4.1,
z,
width+.3,
.16,
.3,
darkWood,
false
);
cube(
x,
4.1,
z,
width+.3,
.16,
.3,
darkWood,
false
);
}
windowFrame(
-7,
-11,
6
);
windowFrame(
7,
-11,
6
);
// ============================================================
// FRONT DOOR
// ============================================================
cube(
0,
3.2,
-11.05,
3.4,
5.8,
.3,
darkWood
);
cube(
1,
3.2,
-11.24,
.12,
1.2,
.12,
mat(0xc6a75b,.25,.75),
false
);
// ============================================================
// ROOF
// ============================================================
const roofMat =
mat(
0x302e30,
.7
);
const roof =
new THREE.Mesh(
new THREE.ConeGeometry(
18,
5,
4
),
roofMat
);
roof.rotation.y =
Math.PI / 4;
roof.position.set(
0,
8.8,
-2
);
roof.scale.z=.68;
roof.castShadow=true;
scene.add(roof);
// ============================================================
// INTERIOR WALLS
// ============================================================
// computer room
cube(
-5,
4,
-2,
.25,
6.5,
8,
wallMat
);
cube(
-5,
4,
3,
9,
6.5,
.25,
wallMat
);
// ============================================================
// COMPUTER DESK
// ============================================================
const deskTop =
cube(
-8.5,
2.15,
-5,
5.5,
.28,
2.1,
darkWood
);
// legs
for(
const x of [-10.8,-6.2]
){
for(
const z of [-5.75,-4.25]
){
cube(
x,
1.1,
z,
.25,
2.1,
.25,
blackMat
);
}
}
// ============================================================
// COMPUTER CASE
// ============================================================
cube(
-10.4,
3,
-5,
1.1,
1.7,
1.1,
blackMat
);
// glass side
cube(
-9.82,
3,
-5,
.025,
1.2,
.75,
glassMat,
false
);
// RGB strips
for(
let i=0;
i<3;
i++
){
const led =
new THREE.Mesh(
new THREE.BoxGeometry(
.04,
.12,
.8
),
new THREE.MeshBasicMaterial({
color:
i===0 ?
0x00aaff :
i===1 ?
0xaa00ff :
0x00ff88
})
);
led.position.set(
-9.8,
2.55+i*.3,
-5
);
scene.add(led);
}
// ============================================================
// MONITOR
// ============================================================
cube(
-7.8,
3.45,
-5,
3.4,
2,
.16,
blackMat
);
cube(
-7.8,
3.45,
-4.89,
3.05,
1.65,
.03,
new THREE.MeshStandardMaterial({
color:0x10223c,
emissive:0x164c8a,
emissiveIntensity:2,
roughness:.15
}),
false
);
// monitor stand
cube(
-7.8,
2.55,
-5,
.22,
1.2,
.22,
blackMat
);
cube(
-7.8,
2,
-5,
1.3,
.08,
.7,
blackMat
);
// keyboard
cube(
-7.8,
2.35,
-4,
2.7,
.12,
.85,
mat(0x16181a,.5),
false
);
// keyboard keys
for(
let row=0;
row<4;
row++
){
for(
let col=0;
col<10;
col++
){
cube(
-8.9 + col*.24,
2.43,
-4.3 + row*.18,
.16,
.035,
.12,
mat(0x303236,.7),
false
);
}
}
// mouse
const mouse =
new THREE.Mesh(
new THREE.SphereGeometry(
.18,
20,
12
),
blackMat
);
mouse.scale.z=.65;
mouse.position.set(
-5.8,
2.45,
-4
);
scene.add(mouse);
// ============================================================
// CHAIR
// ============================================================
function makeChair(
x,
y,
z
){
const group =
new THREE.Group();
const seat =
new THREE.Mesh(
new THREE.BoxGeometry(
1.8,.3,1.8
),
darkWood
);
seat.position.y=1.4;
group.add(seat);
const back =
new THREE.Mesh(
new THREE.BoxGeometry(
1.8,2.2,.25
),
darkWood
);
back.position.set(
0,
2.45,
.78
);
group.add(back);
for(
const dx of [-.65,.65]
){
for(
const dz of [-.65,.65]
){
const leg =
new THREE.Mesh(
new THREE.CylinderGeometry(
.08,.08,1.4,12
),
blackMat
);
leg.position.set(
dx,
.7,
dz
);
group.add(leg);
}
}
group.position.set(
x,y,z
);
group.traverse(
o=>{
if(o.isMesh){
o.castShadow=true;
o.receiveShadow=true;
}
}
);
scene.add(group);
}
makeChair(
-5.5,
0,
-5
);
// ============================================================
// SOFA
// ============================================================
cube(
6,
1.45,
-5,
7,
1.2,
2.1,
mat(0x58423b,.8)
);
cube(
6,
2.5,
-5.7,
7,
2,
.5,
mat(0x58423b,.8)
);
// cushions
for(
let i=0;
i<4;
i++
){
const cushion =
new THREE.Mesh(
new THREE.BoxGeometry(
1.45,
.42,
1.45
),
mat(
i%2 ?
0x675149 :
0x463832,
.85
)
);
cushion.position.set(
3.5+i*1.65,
2.15,
-4.8
);
cushion.castShadow=true;
scene.add(cushion);
}
// ============================================================
// REAL GLB MODELS
// ============================================================
const loader =
new GLTFLoader();
function loadModel(
url,
position,
scale,
rotationY=0
){
loader.load(
url,
gltf=>{
const model =
gltf.scene;
model.position.copy(
position
);
model.scale.setScalar(
scale
);
model.rotation.y =
rotationY;
model.traverse(
obj=>{
if(obj.isMesh){
obj.castShadow=true;
obj.receiveShadow=true;
if(
obj.material &&
obj.material.map
){
obj.material.map.colorSpace =
THREE.SRGBColorSpace;
}
}
}
);
scene.add(model);
},
undefined,
error=>{
console.warn(
"Model failed:",
url,
error
);
}
);
}
// واقعی: مدل کلاه فضایی PBR
loadModel(
"https://threejs.org/examples/models/gltf/DamagedHelmet/glTF-Binary/DamagedHelmet.glb",
new THREE.Vector3(
-10,
2.15,
-3.8
),
.9,
Math.PI
);
// واقعی: صندلی نمونه glTF
loadModel(
"https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/SheenChair/glTF-Binary/SheenChair.glb",
new THREE.Vector3(
8,
.8,
-2
),
2,
Math.PI
);
// ============================================================
// TREES
// ============================================================
const trunkMat =
mat(0x3c2518,.95);
const leafMat =
new THREE.MeshStandardMaterial({
color:0x315a34,
roughness:.95
});
function tree(
x,
z,
size=1
){
const trunk =
new THREE.Mesh(
new THREE.CylinderGeometry(
.45*size,
.65*size,
5*size,
10
),
trunkMat
);
trunk.position.set(
x,
2.5*size,
z
);
trunk.castShadow=true;
scene.add(trunk);
for(
let i=0;
i<3;
i++
){
const crown =
new THREE.Mesh(
new THREE.IcosahedronGeometry(
2.5*size,
1
),
leafMat
);
crown.position.set(
x+(Math.random()-.5)*1.3,
(5+i*1.2)*size,
z+(Math.random()-.5)*1.3
);
crown.scale.y=.85;
crown.castShadow=true;
crown.receiveShadow=true;
scene.add(crown);
}
}
tree(-22,-5,1.2);
tree(22,-5,1.4);
tree(-25,10,1.5);
tree(25,15,1.7);
tree(-30,28,1.3);
tree(30,32,1.5);
// ============================================================
// STREET LIGHTS
// ============================================================
function streetLight(
x,
z
){
cylinder(
x,
3.2,
z,
.12,
6.4,
blackMat
);
const lamp =
new THREE.PointLight(
0xffd18b,
16,
13
);
lamp.position.set(
x,
6.5,
z
);
lamp.castShadow=true;
scene.add(lamp);
const bulb =
new THREE.Mesh(
new THREE.SphereGeometry(
.18,
16,
12
),
new THREE.MeshBasicMaterial({
color:0xffd68a
})
);
bulb.position.copy(
lamp.position
);
scene.add(bulb);
}
streetLight(-10,15);
streetLight(10,15);
streetLight(-10,35);
streetLight(10,35);
// ============================================================
// PLAYER
// ============================================================
const player = {
position:new THREE.Vector3(
0,
1.7,
16
),
velocity:new THREE.Vector3(),
yaw:0,
pitch:0,
grounded:true
};
let thirdPerson=false;
const keys={};
addEventListener(
"keydown",
e=>{
keys[e.code]=true;
if(e.code==="KeyV"){
thirdPerson=!thirdPerson;
}
}
);
addEventListener(
"keyup",
e=>{
keys[e.code]=false;
}
);
// ============================================================
// MOUSE LOOK
// ============================================================
renderer.domElement.addEventListener(
"click",
()=>{
renderer.domElement.requestPointerLock();
}
);
document.addEventListener(
"mousemove",
e=>{
if(
document.pointerLockElement !==
renderer.domElement
) return;
player.yaw -=
e.movementX*.002;
player.pitch -=
e.movementY*.002;
player.pitch =
Math.max(
-1.45,
Math.min(
1.45,
player.pitch
)
);
}
);
// ============================================================
// COLLISION
// ============================================================
function blocked(
p
){
const r=.42;
for(
const c of colliders
){
if(
p.x+r>c.minX &&
p.x-r<c.maxX &&
p.y+.85>c.minY &&
p.y-.85<c.maxY &&
p.z+r>c.minZ &&
p.z-r<c.maxZ
){
return true;
}
}
return⚠️Content was pasted as plain text and auto-formatted as a code block. Use the Code Block button in the editor for proper formatting.