Three.js and loading a cross-domain image

Update

In newer versions of THREE.js cross origin images are handled by default. THREE.ImageUtils.loadTexture is deprecated. It’s common to use TextureLoader

const loader = new THREE.TextureLoader();
const mapOverlay = loader.load('http://i.imgur.com/3tU4Vig.jpg');

Original Answer

This works

THREE.ImageUtils.crossOrigin = '';
var mapOverlay = THREE.ImageUtils.loadTexture('http://i.imgur.com/3tU4Vig.jpg');

Here’s a sample

var canvas = document.getElementById("c");
var renderer = new THREE.WebGLRenderer({canvas: canvas});

var camera = new THREE.PerspectiveCamera( 20, 1, 1, 10000 );
var scene = new THREE.Scene();
var sphereGeo = new THREE.SphereGeometry(40, 16, 8);

var light = new THREE.DirectionalLight(0xE0E0FF, 1);
light.position.set(200, 500, 200);
scene.add(light);
var light = new THREE.DirectionalLight(0xFFE0E0, 0.5);
light.position.set(-200, -500, -200);
scene.add(light);

camera.position.z = 300;

THREE.ImageUtils.crossOrigin = '';
var texture = THREE.ImageUtils.loadTexture('http://i.imgur.com/3tU4Vig.jpg');
var material = new THREE.MeshPhongMaterial({
    map: texture,
    specular: 0xFFFFFF,
    shininess: 30,
    shading: THREE.FlatShading,
});
var mesh = new THREE.Mesh(sphereGeo, material);
scene.add(mesh);

function resize() {
    var width = canvas.clientWidth;
    var height = canvas.clientHeight;
    if (canvas.width != width ||
        canvas.height != height) {
          renderer.setSize(canvas.clientWidth, canvas.clientHeight, false);  // don't update the style. Why does three.js fight CSS? It should respect CSS :(
        
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
    }
}

function render(time) {
    time *= 0.001;  // seconds
    resize();
    mesh.rotation.y = time;
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
requestAnimationFrame(render);
body {
    margin: 0;
}

#c {
    width: 100vw;
    height: 100vh;
    display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>
<canvas id="c"></canvas>

Note: You don’t use new with THREE.ImageUtils.loadTexture

In order to load an image cross-origin into WebGL the server that’s sending the image has to respond with the correct headers. It’s not enough for you to say you want to use the image cross-origin. All that does is tell the server you’re requesting permission to use the image.

You can set img.crossOrigin, or in THREE’s case THREE.ImageUtils.crossOrigin, to either '', 'anonymous' which is the same as '', or you can set it to 'use-credentials' which sends even more info to the server. The browser sees you set crossOrigin and sends certain headers to the server. The server reads those headers, decides if your domain has permission to use the image and if you do have permission it sends certain headers back to the browser. The browser, if it sees those headers will then let you use the image.

The biggest point to take away from the above is the server has to send the headers. Most servers don’t send those headers. imgur.com does apparently. I suspect bitlocker does not though I didn’t test it.

Also you have to set crossOrigin. If you don’t the browser won’t allow you to use the img in ways your not supposed to be able to even if the server sends the correct header.

Leave a Comment