How can I prevent an image from appearing greyed out when loading it onto the scene background in Three.js?

The Probem
I’m trying to figure out how to load an image onto the scene background without it appear greyed out by using the TextureLoader() class.

I’m following a three.js tutorial Three.js Tutorial For Absolute Beginners - YouTube and I’m following what he’s doing basically but when I try to load the same way as he does it(using the same image as him), the image appears greyed out. I went to his repository and clone it and it still appears the same so I’m not sure if how he does it is outdated or not or maybe some other factors (browsers,image,etc). I tried doing some research but still I can’t find anything that’s helpful.

Here’s how it looks for me:

Here’s how it looks in his tutorial:

This is the entire code or you can check it on his repository ( GitHub - WaelYasmina/threetutorial )

Code

import * as THREE from 'three'
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'dat.gui'

import nebula from '../img/nebula.jpg'
import stars from '../img/stars.jpg'

const renderer = new THREE.WebGLRenderer();

renderer.shadowMap.enabled = true

renderer.setSize(window.innerWidth, window.innerHeight)

document.body.appendChild(renderer.domElement)

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(
    45,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
)

const orbit = new OrbitControls(camera, renderer.domElement)

const axesHelper = new THREE.AxesHelper(3)
scene.add(axesHelper)

camera.position.set(30,30,30)
orbit.update()

//Box
//First Phase
const boxGeometry = new THREE.BoxGeometry()
//Second Phase
const boxMaterial = new THREE.MeshBasicMaterial({color:0x00FF00})
//Third Phase
const box = new THREE.Mesh(boxGeometry,boxMaterial)
scene.add(box)

//Plane
const planeGeometry = new THREE.PlaneGeometry(30,30)
const planeMaterial = new THREE.MeshStandardMaterial({
    color: 0xFFFFFF,
    side: THREE.DoubleSide
})
const plane = new THREE.Mesh(planeGeometry,planeMaterial)
scene.add(plane)
plane.rotation.x = Math.PI * -0.5
plane.receiveShadow = true

//Grid Helper
const gridHelper = new THREE.GridHelper(30)
scene.add(gridHelper)

//Sphere
const sphereGeometry = new THREE.SphereGeometry(4,50,50)
const sphereMaterial = new THREE.MeshStandardMaterial({
    color: 0x0000FF,
    wireframe: false
})
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
scene.add(sphere)
sphere.position.set(-10,10,0)
sphere.castShadow = true

//Ambient Light
const ambientLight = new THREE.AmbientLight(0x333333)
scene.add(ambientLight)

//Directional Light
// const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 0.8)
// scene.add(directionalLight)
// directionalLight.position.set(-30,50,0)
// directionalLight.castShadow = true
// directionalLight.shadow.camera.bottom = -12

// const dLightHelper = new THREE.DirectionalLightHelper(directionalLight,5)
// scene.add(dLightHelper)

// const dLightShadowHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
// scene.add(dLightShadowHelper)

//Spotlight
const spotLight = new THREE.SpotLight(0xFFFFFF)
scene.add(spotLight)
spotLight.position.set(-100,100,0)
spotLight.castShadow = true
spotLight.angle = 0.2

const sLightHelper = new THREE.SpotLightHelper(spotLight)
scene.add(sLightHelper)

//FOG
//scene.fog = new THREE.Fog(0xFFFFFF, 0, 200)

// scene.fog = new THREE.FogExp2(0xFFFFFF, 0.01)
// renderer.setClearColor(0xFFEA00)

//Texture
const textureLoader = new THREE.TextureLoader()
scene.background = textureLoader.load(stars)

// const cubeTextureLoader = new THREE.CubeTextureLoader()
// scene.background = cubeTextureLoader.load([
//     nebula,
//     nebula,
//     stars,
//     stars,
//     stars,
//     stars
// ])

//GUI
const gui = new dat.GUI()
const options = {
    sphereColor : '#ffea00',
    wireframe: false,
    speed: 0.01,
    angle: 0.2,
    penumbra: 0,
    intensity: 1
}

// color gui
gui.addColor(options, 'sphereColor').onChange(function(e){
    sphere.material.color.set(e)
})

// wireframe gui
gui.add(options, 'wireframe').onChange(function(e){
    sphere.material.wireframe = e
})

// speed gui
gui.add(options, 'speed', 0, 0.1)

gui.add(options, 'angle', 0, 0.1)
gui.add(options, 'penumbra', 0, 0.1)
gui.add(options, 'intensity', 0, 1)



//Animation
let step = 0



function animate(time) {
    box.rotation.x = time/1000
    box.rotation.y = time/1000

    step += options.speed 
    sphere.position.y = 10* Math.abs(Math.sin(step))
    
    spotLight.angle = options.angle
    spotLight.penumbra = options.penumbra
    spotLight.intensity = options.intensity
    sLightHelper.update()
    
    renderer.render(scene, camera)

}

renderer.setAnimationLoop(animate)

What I have did to try to solve it
I tried looking at the documentation on TextureLoader() three.js docs but I don’t find anything that works.

I also check on this part of the documentation about backgrounds and Skyboxes and modified my code hoping that it will solve my problem(the image is still greyed out)

Modified Code

import * as THREE from 'three'
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'
import * as dat from 'dat.gui'

import nebula from '../img/nebula.jpg'
import stars from '../img/stars.jpg'



const canvas = document.querySelector('#c')
const renderer = new THREE.WebGLRenderer({
    antialias: true,
    canvas,
    alpha: true,
});




renderer.shadowMap.enabled = true

renderer.setSize(window.innerWidth, window.innerHeight)

document.body.appendChild(renderer.domElement)

const scene = new THREE.Scene()


const camera = new THREE.PerspectiveCamera(
    45,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
)

const orbit = new OrbitControls(camera, renderer.domElement)

const axesHelper = new THREE.AxesHelper(3)
scene.add(axesHelper)

camera.position.set(30,30,30)
orbit.update()

//Box
//First Phase
const boxGeometry = new THREE.BoxGeometry()
//Second Phase
const boxMaterial = new THREE.MeshBasicMaterial({color:0x00FF00})
//Third Phase
const box = new THREE.Mesh(boxGeometry,boxMaterial)
scene.add(box)

//Plane
const planeGeometry = new THREE.PlaneGeometry(30,30)
const planeMaterial = new THREE.MeshStandardMaterial({
    color: 0xFFFFFF,
    side: THREE.DoubleSide
})
const plane = new THREE.Mesh(planeGeometry,planeMaterial)
scene.add(plane)
plane.rotation.x = Math.PI * -0.5
plane.receiveShadow = true

//Grid Helper
const gridHelper = new THREE.GridHelper(30)
scene.add(gridHelper)

//Sphere
const sphereGeometry = new THREE.SphereGeometry(4,50,50)
const sphereMaterial = new THREE.MeshStandardMaterial({
    color: 0x0000FF,
    wireframe: false
})
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
scene.add(sphere)
sphere.position.set(-10,10,0)
sphere.castShadow = true

//Ambient Light
const ambientLight = new THREE.AmbientLight(0x333333)
scene.add(ambientLight)

//Directional Light
// const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 0.8)
// scene.add(directionalLight)
// directionalLight.position.set(-30,50,0)
// directionalLight.castShadow = true
// directionalLight.shadow.camera.bottom = -12

// const dLightHelper = new THREE.DirectionalLightHelper(directionalLight,5)
// scene.add(dLightHelper)

// const dLightShadowHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
// scene.add(dLightShadowHelper)

//Spotlight
const spotLight = new THREE.SpotLight(0xFFFFFF)
scene.add(spotLight)
spotLight.position.set(-100,100,0)
spotLight.castShadow = true
spotLight.angle = 0.2

const sLightHelper = new THREE.SpotLightHelper(spotLight)
scene.add(sLightHelper)

//FOG
//scene.fog = new THREE.Fog(0xFFFFFF, 0, 200)

// scene.fog = new THREE.FogExp2(0xFFFFFF, 0.01)
// renderer.setClearColor(0xFFEA00)

//Texture
const textureLoader = new THREE.TextureLoader()
const bgTexture = textureLoader.load(stars)
scene.background = bgTexture

// const cubeTextureLoader = new THREE.CubeTextureLoader()
// scene.background = cubeTextureLoader.load([
//     nebula,
//     nebula,
//     stars,
//     stars,
//     stars,
//     stars
// ])

//GUI
const gui = new dat.GUI()
const options = {
    sphereColor : '#ffea00',
    wireframe: false,
    speed: 0.01,
    angle: 0.2,
    penumbra: 0,
    intensity: 1
}

// color gui
gui.addColor(options, 'sphereColor').onChange(function(e){
    sphere.material.color.set(e)
})

// wireframe gui
gui.add(options, 'wireframe').onChange(function(e){
    sphere.material.wireframe = e
})

// speed gui
gui.add(options, 'speed', 0, 0.1)

gui.add(options, 'angle', 0, 0.1)
gui.add(options, 'penumbra', 0, 0.1)
gui.add(options, 'intensity', 0, 1)



//Animation
let step = 0



function animate(time) {
    box.rotation.x = time/1000
    box.rotation.y = time/1000

    step += options.speed 
    sphere.position.y = 10* Math.abs(Math.sin(step))
    
    spotLight.angle = options.angle
    spotLight.penumbra = options.penumbra
    spotLight.intensity = options.intensity
    sLightHelper.update()

    const canvasAspect = canvas.clientWidth / canvas.clientHeight;
    const imageAspect = bgTexture.image ? bgTexture.image.width / bgTexture.image.height : 1;
    const aspect = imageAspect / canvasAspect;

    bgTexture.offset.x = aspect > 1 ? (1 - 1 / aspect) / 2 : 0;
    bgTexture.repeat.x = aspect > 1 ? 1 / aspect : 1;

    bgTexture.offset.y = aspect > 1 ? 0 : (1 - aspect) / 2;
    bgTexture.repeat.y = aspect > 1 ? 1 : aspect;
    renderer.render(scene, camera)

    
}

renderer.setAnimationLoop(animate)

Try it like so:

const bgTexture = textureLoader.load(stars)
bgTexture.colorSpace = THREE.SRGBColorSpace
scene.background = bgTexture

Since the latest three.js version r152, the engine assumes a sRGB workflow by default. You can find more information about this change here:

1 Like

Thanks, it works now