Textured planets, Keplerian orbits, an emissive Sun, rings, and
UnrealBloom post-processing โ all wired together in a single HTML file
with Three.js r160.
1
Scene, Renderer and Camera
Start with the standard Three.js boilerplate โ a
WebGLRenderer that fills the window, a
PerspectiveCamera, and a black background to simulate
space.
import * as THREE from
'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js';
import { OrbitControls } from
'https://cdn.jsdelivr.net/npm/three@0.160/examples/jsm/controls/OrbitControls.js';
import { EffectComposer } from
'https://cdn.jsdelivr.net/npm/three@0.160/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from
'https://cdn.jsdelivr.net/npm/three@0.160/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from
'https://cdn.jsdelivr.net/npm/three@0.160/examples/jsm/postprocessing/UnrealBloomPass.js';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(devicePixelRatio); renderer.setSize(innerWidth,
innerHeight); renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.9;
document.body.appendChild(renderer.domElement); const scene = new
THREE.Scene(); scene.background = new THREE.Color(0x000008); // Star
field โ 10 000 random points const starGeo = new
THREE.BufferGeometry(); const starPos = new Float32Array(30_000); for
(let i = 0; i < 30_000; i++) starPos[i] = (Math.random() - 0.5) *
2000; starGeo.setAttribute('position', new
THREE.BufferAttribute(starPos, 3)); scene.add(new
THREE.Points(starGeo, new THREE.PointsMaterial({ color: 0xffffff,
size: 0.35 }))); const camera = new THREE.PerspectiveCamera(60,
innerWidth / innerHeight, 0.1, 5000); camera.position.set(0, 80, 200);
window.addEventListener('resize', () => { renderer.setSize(innerWidth,
innerHeight); camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix(); });
Set renderer.toneMapping = THREE.ACESFilmicToneMapping โ
it boosts mid-tone contrast and is what most games use. Without it
bloom looks washed out.
2
Sun with Emissive Glow
The Sun emits its own light. Use a PointLight at the
origin and a MeshStandardMaterial with a high
emissiveIntensity so it stays bright regardless of other
lights.
const loader = new THREE.TextureLoader(); // Sun const sunTex =
loader.load('https://cdn.jsdelivr.net/gh/mrdoob/three.js@r160/examples/textures/planets/sun.jpg');
const sunMat = new THREE.MeshStandardMaterial({ map: sunTex,
emissiveMap: sunTex, emissive: new THREE.Color(1.0, 0.7, 0.1),
emissiveIntensity: 2.5, }); const sun = new THREE.Mesh(new
THREE.SphereGeometry(16, 32, 32), sunMat); scene.add(sun); // Light
from the Sun const sunLight = new THREE.PointLight(0xfff4cc, 4, 2000,
1.5); scene.add(sunLight); // Ambient for fill scene.add(new
THREE.AmbientLight(0x111122, 1.5));
The map and emissiveMap share the same
texture. The emissive channel makes the surface glow independently of
the point light โ important when the camera is far from the light
source.
3
Textured Planets and Saturn's Ring
Each planet is a SphereGeometry, grouped with its orbital
pivot so rotation happens around the Sun origin.
Wire up EffectComposer with
UnrealBloomPass so the Sun and other bright objects get a
soft glow halo.
const composer = new EffectComposer(renderer); composer.addPass(new
RenderPass(scene, camera)); const bloom = new UnrealBloomPass( new
THREE.Vector2(innerWidth, innerHeight), 1.2, // strength 0.4, //
radius 0.85 // threshold โ only pixels brighter than this glow );
composer.addPass(bloom); // Handle window resize for the composer too
window.addEventListener('resize', () => { composer.setSize(innerWidth,
innerHeight); }); // Replace renderer.render() with composer.render()
in the loop // (already done in Step 4 โ composer.render() inside
animate()) // --- Tip: selective bloom --- // To make only the Sun
bloom (not dim planets): // 1. Render the scene twice โ once with
bloom, once clean // 2. Use layers: sun.layers.enable(1); bloom pass
only renders layer 1 // See Three.js selective-bloom example for full
setup
Threshold matters: set threshold near
1.0 so only the emissive Sun exceeds it. If you set it to 0 the entire
scene blooms and everything looks foggy. Combine with
ACESFilmicToneMapping for the best result.