My starfield texture was taken from NASA. Dimensions: 8192 × 4096, Color space: RGB, Alpha channel: No.
However, the texture in my scene looks completely washed out and inaccurate to the original. In fact, even the Earth texture is wacky, but I don’t mind it.
I have done a very similar demo (same textures) in react-three-fiber with its default settings, and it looked great. So this probably means it has to do with the renderer settings and not the image itself.
I tried setting alpha to true, outputColorSpace to THREE.SRGBColorSpace, toneMapping to THREE.ACESFilmicToneMapping, THREE.ColorManagement.enabled to true, toneMappingExposure to 1.0,
but none of them made a difference (slight but not better). Here is a picture of the scene with these settings:
And the react-three-fiber results:
JS code:
import * as THREE from "three";
const CAMERA_PARAMETERS = {
fov: 45,
near: 0.1,
far: 2000,
position: { x: 0, y: 0, z: 0 },
lookAt: { x: 0, y: 0, z: 1000 },
};
const EARTH_PARAMETERS = {
radius: 50,
widthSegments: 32,
heightSegments: 32,
texturePath: "/images/earth.jpeg",
position: { x: 0, y: 0, z: 1000 },
rotation: { y: 0.001 },
};
const SUNLIGHT_PARAMETERS = {
color: 0xffffff,
intensity: 1,
position: { x: 0, y: 0, z: -500 },
};
const STARFIELD_PARAMETERS = {
radius: 1000,
widthSegments: 64,
heightSegments: 64,
texturePath: "/images/starfield.jpeg",
};
function createStarfield(scene, loader) {
const starfieldGeometry = new THREE.SphereGeometry(
STARFIELD_PARAMETERS.radius,
STARFIELD_PARAMETERS.widthSegments,
STARFIELD_PARAMETERS.heightSegments
);
const starfieldTexture = loader.load(STARFIELD_PARAMETERS.texturePath);
const starfieldMaterial = new THREE.MeshBasicMaterial({
map: starfieldTexture,
side: THREE.BackSide,
});
const starfield = new THREE.Mesh(starfieldGeometry, starfieldMaterial);
scene.add(starfield);
}
function createEarth(scene, loader) {
const earthGeometry = new THREE.SphereGeometry(
EARTH_PARAMETERS.radius,
EARTH_PARAMETERS.widthSegments,
EARTH_PARAMETERS.heightSegments
);
const earthMaterial = new THREE.MeshPhongMaterial();
earthMaterial.map = loader.load(EARTH_PARAMETERS.texturePath);
const earth = new THREE.Mesh(earthGeometry, earthMaterial);
earth.position.set(
EARTH_PARAMETERS.position.x,
EARTH_PARAMETERS.position.y,
EARTH_PARAMETERS.position.z
);
earth.name = "earth";
scene.add(earth);
}
function createSunlight(scene) {
const sunlight = new THREE.DirectionalLight(
SUNLIGHT_PARAMETERS.color,
SUNLIGHT_PARAMETERS.intensity
);
sunlight.position.set(
SUNLIGHT_PARAMETERS.position.x,
SUNLIGHT_PARAMETERS.position.y,
SUNLIGHT_PARAMETERS.position.z
);
sunlight.name = "sunlight";
scene.add(sunlight);
}
function rotateEarth(scene) {
scene.getObjectByName("earth").rotation.y += 0.001;
}
function setupCamera() {
const camera = new THREE.PerspectiveCamera(
CAMERA_PARAMETERS.fov,
window.innerWidth / window.innerHeight,
CAMERA_PARAMETERS.near,
CAMERA_PARAMETERS.far
);
camera.position.set(
CAMERA_PARAMETERS.position.x,
CAMERA_PARAMETERS.position.y,
CAMERA_PARAMETERS.position.z
);
camera.lookAt(
CAMERA_PARAMETERS.lookAt.x,
CAMERA_PARAMETERS.lookAt.y,
CAMERA_PARAMETERS.lookAt.z
);
return camera;
}
function setupRenderer() {
const canvas = document.querySelector(".webgl");
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
return renderer;
}
function animate(renderer, scene, camera) {
requestAnimationFrame(() => animate(renderer, scene, camera));
rotateEarth(scene);
renderer.render(scene, camera);
}
function main() {
const scene = new THREE.Scene();
const loader = new THREE.TextureLoader();
createEarth(scene, loader);
createSunlight(scene);
createStarfield(scene, loader);
const camera = setupCamera();
const renderer = setupRenderer();
animate(renderer, scene, camera);
}
main();
React code (relevant parts):
function Earth() {
const earthTexture = useLoader(TextureLoader, earthMap);
const earthRef = useRef<Mesh>(null!);
useFrame(() => {
earthRef.current.rotation.y += 0.001;
});
return (
<mesh ref={earthRef} name="earth">
<sphereGeometry args={[50, 32, 32]} />
<meshPhongMaterial map={earthTexture} />
</mesh>
);
}
function Skybox() {
const texture = useLoader(TextureLoader, starfieldTexture);
return (
<mesh>
<sphereGeometry attach="geometry" args={[5000, 24, 24]} />
<meshBasicMaterial attach="material" map={texture} side={BackSide} />
</mesh>
);
}
function Scene() {
return (
<Canvas camera={{ near: 1, far: 10000 }}>
<directionalLight
position={[1000, 0, 2000]}
color={"0x404040"}
intensity={3}
/>
<Earth />
<Asteroid />
<Skybox />
<CameraControls />
</Canvas>
);
}
function App() {
return (
<div className="App h-screen w-screen overflow-hidden">
<Scene></Scene>
</div>
);
}
export default App;