<div id="demo-final" class="threejs-container"></div>
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 1. Scene & Camera
const scene = new THREE.Scene();
scene.fog = new THREE.FogExp2('#0a0a14', 0.08);
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 2, 8);
// 2. Premium Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.shadowMap.enabled = true;
document.getElementById('demo-final').appendChild(renderer.domElement);
// 3. Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.enablePan = false;
controls.enableZoom = true;
// 4. Group for animation
const group = new THREE.Group();
scene.add(group);
// 5. Load Model
let airplane;
const loader = new GLTFLoader();
loader.load('/models-three.js/small-airplane.glb', (gltf) => {
airplane = gltf.scene;
airplane.scale.set(0.6, 0.6, 0.6);
airplane.traverse((child) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
if (child.material) {
child.material.metalness = 0.5;
child.material.roughness = 0.3;
}
}
});
group.add(airplane);
});
// 6. Rings of Particles
const particlesCount = 2000;
const posArray = new Float32Array(particlesCount * 3);
const colorsArray = new Float32Array(particlesCount * 3);
const color1 = new THREE.Color(0x9FA6FF);
const color2 = new THREE.Color(0xFC5D7D);
for (let i = 0; i < particlesCount; i++) {
const r = 3 + Math.random() * 3;
const theta = Math.random() * Math.PI * 2;
const phi = (Math.random() - 0.5) * 0.5;
posArray[i * 3] = r * Math.cos(theta);
posArray[i * 3 + 1] = r * Math.sin(phi);
posArray[i * 3 + 2] = r * Math.sin(theta);
const mixedColor = color1.clone().lerp(color2, Math.random());
colorsArray[i * 3] = mixedColor.r;
colorsArray[i * 3 + 1] = mixedColor.g;
colorsArray[i * 3 + 2] = mixedColor.b;
}
const particlesGeo = new THREE.BufferGeometry();
particlesGeo.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
particlesGeo.setAttribute('color', new THREE.BufferAttribute(colorsArray, 3));
const particlesMat = new THREE.PointsMaterial({
size: 0.02,
vertexColors: true,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending
});
const particlesMesh = new THREE.Points(particlesGeo, particlesMat);
group.add(particlesMesh);
// 7. Dramatic Lighting
const mainLight = new THREE.DirectionalLight(0xffffff, 2);
mainLight.position.set(5, 5, 2);
mainLight.castShadow = true;
scene.add(mainLight);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
// 8. Animation Loop
const clock = new THREE.Clock();
let mouseX = 0;
let mouseY = 0;
window.addEventListener('mousemove', (event) => {
mouseX = (event.clientX / window.innerWidth) * 2 - 1;
mouseY = -(event.clientY / window.innerHeight) * 2 + 1;
});
function animate() {
requestAnimationFrame(animate);
const time = clock.getElapsedTime();
controls.update();
// Floating & Rotation
group.position.y = Math.sin(time * 1.5) * 0.2;
if (airplane) {
airplane.rotation.z = Math.sin(time * 1) * 0.1;
}
particlesMesh.rotation.y = time * 0.1;
// Mouse interactivity
group.rotation.x = THREE.MathUtils.lerp(group.rotation.x, mouseY * 0.3, 0.05);
group.rotation.y = THREE.MathUtils.lerp(group.rotation.y, mouseX * 0.3, 0.05);
renderer.render(scene, camera);
}
animate();