Axiol
Axiol

Reputation: 5962

textureLoader not working for a normal map

I'm trying to load a texture with three.js to use it as a normal map. It returns no error or anything else, but the texture is not loading. I end up with a black ball

import * as THREE from 'three';

/** Build the scene. */
const canvas = document.querySelector('#canvas');
const scene = new THREE.Scene();
const textureLoader = new THREE.TextureLoader();

/** Create the sphere. */
const gemoetry = new THREE.SphereBufferGeometry(.5, 64, 64);
const material = new THREE.MeshStandardMaterial({
  metalness: 0.7,
  roughness: 0.2,
  normalMap: textureLoader.load('https://res.cloudinary.com/axiol/image/upload/v1617884764/CodePen/normalMap.png'),
  color: new THREE.Color(0x292929)
});
const sphere = new THREE.Mesh(gemoetry, material);
scene.add(sphere);

/** Set the sizes based on the windows size. */
const sizes = {
  width: window.innerWidth,
  height: window.innerHeight
};

/** Add some light. */
const pointLight = new THREE.PointLight(0xffffff, 0.1)
pointLight.position.x = 2
pointLight.position.y = 3
pointLight.position.z = 4
scene.add(pointLight)

window.addEventListener('resize', () => {
  sizes.width = window.innerWidth
  sizes.height = window.innerHeight

  camera.aspect = sizes.width / sizes.height
  camera.updateProjectionMatrix()

  renderer.setSize(sizes.width, sizes.height)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
});

/** Place the camera. */
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100);
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 2;
scene.add(camera);

/** Render the scene. */
const renderer = new THREE.WebGLRenderer({
  canvas: canvas,
  alpha: true
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.render(scene, camera);

I don't really see what I could be missing here. Any idea ?

Upvotes: 2

Views: 354

Answers (1)

Mugen87
Mugen87

Reputation: 31086

You render the scene with a normal map which is not yet fully loaded. Try it like so:

/** Build the scene. */
const canvas = document.querySelector('#canvas');
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
const textureLoader = new THREE.TextureLoader();

/** Create the sphere. */
const gemoetry = new THREE.SphereBufferGeometry(0.5, 64, 64);
const material = new THREE.MeshStandardMaterial({
  metalness: 0.7,
  roughness: 0.2,
  normalMap: textureLoader.load('https://res.cloudinary.com/axiol/image/upload/v1617884764/CodePen/normalMap.png', render),
  color: new THREE.Color(0xffffff)
});
const sphere = new THREE.Mesh(gemoetry, material);
scene.add(sphere);

/** Set the sizes based on the windows size. */
const sizes = {
  width: window.innerWidth,
  height: window.innerHeight
};

/** Add some light. */
const pointLight = new THREE.PointLight(0xffffff, 1)
pointLight.position.x = 2
pointLight.position.y = 3
pointLight.position.z = 4
scene.add(pointLight)

window.addEventListener('resize', () => {
  sizes.width = window.innerWidth
  sizes.height = window.innerHeight

  camera.aspect = sizes.width / sizes.height
  camera.updateProjectionMatrix()

  renderer.setSize(sizes.width, sizes.height)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
});

/** Place the camera. */
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100);
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 2;
scene.add(camera);

/** Render the scene. */
const renderer = new THREE.WebGLRenderer({
  canvas: canvas
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
render();

function render() {

  renderer.render(scene, camera);

}
body {
  margin:0
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>
<canvas id="canvas"></canvas>

Notice the call of render() in the onLoad() callback of TextureLoader.load().

Upvotes: 2

Related Questions