1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
import * as THREE from 'https://unpkg.com/three@0.160.0/build/three.module.js';
import { createSun } from './sun.js';
import { createMountains } from './mountains.js';
// Scene setup
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x090012);
scene.fog = new THREE.Fog(0x090012, 20, 120);
const camera = new THREE.PerspectiveCamera(
60,
window.innerWidth / window.innerHeight,
0.1,
500
);
camera.position.set(0, 2, 15);
camera.rotation.x = -0.1;
const renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('bg'),
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
// Grid
const size = 200;
const divisions = 200;
const grid = new THREE.GridHelper(size, divisions, 0xff00ff, 0xff00ff);
grid.material.transparent = true;
grid.material.opacity = 0.4;
scene.add(grid);
// --- Occlusion floor (invisible) ---
const floorGeometry = new THREE.PlaneGeometry(size, size);
const floorMaterial = new THREE.MeshBasicMaterial({
color: 0x090012, // matches background
opacity: 0.5, // invisible
transparent: true
});
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2; // horizontal
floor.position.y = 0; // same height as grid
scene.add(floor);
// Sun
const { sun, glow } = createSun();
scene.add(sun);
scene.add(glow);
let scroll = 0;
let sunAngle = 0;
// Mountains
const mountains = createMountains();
scene.add(mountains);
// Animation function
function animate() {
scroll += 0.05;
grid.position.z = scroll % 10;
renderer.render(scene, camera);
}
// Start loop with pause/resume on tab visibility
renderer.setAnimationLoop(animate);
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
renderer.setAnimationLoop(null);
} else {
renderer.setAnimationLoop(animate);
}
});
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
|