web-dev-qa-db-fra.com

Three.js et chargement d'une image inter-domaines

Je sais que cela a déjà été demandé, et j'ai lu toutes les questions et réponses que j'ai pu trouver, mais rien ne fonctionne.

J'exécute ceci sur un serveur local (IIS). J'essaie de charger une image dans imgur, puis de l'utiliser comme texture pour un objet à l'aide du code:

var savedImage = /[^?]*$/.exec(location.search)[0];
if (savedImage != "") { savedImageLoad("http://i.imgur.com/" + savedImage + ".jpg"); };

    function savedImageLoad(image) {
        var mapOverlay = new THREE.ImageUtils.loadTexture(image);
        sphere.material = new THREE.MeshBasicMaterial({map: mapOverlay, needsUpdate: true});;
        sphere.geometry.buffersNeedUpdate = true;
        sphere.geometry.uvsNeedUpdate = true;
    }

Mais c'est donner l'erreur:

Uncaught SecurityError: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The cross-Origin image at http://i.imgur.com/uBD0g95.jpg may not be loaded.

J'ai essayé de placer THREE.ImageUtils.crossOrigin = "anonymous";, ou une variante de, au début de mon code, à la fin et à divers autres endroits. J'ai ajouté un web.config avec

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="*" />
      <add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
      <add name="Access-Control-Allow-Headers" value="Content-Type" />
    </customHeaders>
  </httpProtocol>
 </system.webServer>
</configuration>

mais ça n'a pas marché. Cela ne fonctionne pas non plus sur un site hébergé sur bitbucket.org, ce qui me dit que quelque chose manque dans mon code.

Il semble que la ligne sphere.material = new THREE.MeshBasicMaterial({map: mapOverlay, needsUpdate: true});; échoue, comme si je commentais cela, il n'y a pas d'erreur (mais le maillage n'est pas mis à jour).

Je ne sais vraiment pas quoi essayer d'autre et toute aide serait la bienvenue.

33
Kurt

Mettre à jour

Dans les versions plus récentes de THREE.js, les images d'origine croisée sont gérées par défaut. THREE.ImageUtils.loadTexture est obsolète. Il est courant d'utiliser TextureLoader

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

Réponse originale

Cela marche

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

Voici un échantillon

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>

Remarque: vous n'utilisez pas new avec THREE.ImageUtils.loadTexture

Pour charger une image cross-Origin dans WebGL, le serveur qui envoie l'image doit répondre avec les en-têtes appropriés. Il ne suffit pas de dire que vous voulez utiliser l'image cross-Origin. Tout ce que cela signifie, c'est dire au serveur que vous demandez l'autorisation d'utiliser l'image. 

Vous pouvez définir img.crossOrigin ou THREE.ImageUtils.crossOrigin dans le cas de THREE sur '', 'anonymous', qui est identique à '', ou vous pouvez le définir sur 'use-credentials', qui envoie encore plus d'informations au serveur. Le navigateur voit que vous avez défini crossOrigin et envoie certains en-têtes au serveur. Le serveur lit ces en-têtes, décide si votre domaine est autorisé à utiliser l'image et, le cas échéant, renvoie certains en-têtes au navigateur. Le navigateur, s'il voit ces en-têtes, vous permettra ensuite d'utiliser l'image.

Le point le plus important à retenir de ce qui précède est le serveur qui doit envoyer les en-têtes. La plupart des serveurs n'envoient pas ces en-têtes. imgur.com apparemment. Je soupçonne que bitlocker ne fonctionne pas bien que je ne l’aie pas testé. 

Aussi, vous devez définir crossOrigin. Si vous ne le faites pas, le navigateur ne vous autorisera pas à utiliser img d'une manière que vous n'êtes pas censé pouvoir utiliser même si le serveur envoie l'en-tête correct.

52
gman

UPDATE: méthode obsolète

Je suis tombé sur ce problème et ai appliqué la solution à partir de la réponse pour constater qu'il ne fonctionnait pas en raison de la méthode obsolète des nouvelles versions de THREE.js. Je poste cette réponse au cas où quelqu'un aurait le même problème. Malgré la dépréciation, les informations fournies par gman dans la réponse originale sont des plus utiles et je vous recommande de les lire.

THREE.ImageUtils.loadTexture()

cette méthode est devenue obsolète depuis la question et la réponse d'origine.

Manière actuelle de charger la texture:

// instantiate a loader
var loader = new THREE.TextureLoader();

//allow cross Origin loading
loader.crossOrigin = '';

// load a resource
loader.load('textures/land_ocean_ice_cloud_2048.jpg',
    // Function when resource is loaded
    function ( texture ) {},
    // Function called when download progresses
    function ( xhr ) {},
    // Function called when download errors
    function ( xhr ) {}
);
24
Michal

J'ai trouvé une solution pour les images et les modèles JSON selon le problème interdomaine . Cela fonctionne toujours, même sur Localhost.

Dans mon cas, je charge le jeu depuis NodeJS sur le port 3001 pour fonctionner avec Socket.io . Je veux les modèles 3D depuis le port 80.

Supposons que tous les modèles sont dans le répertoire: game/dist/models/**/*

J'ai créé un fichier PHP jeu/dist/models/json.php:

<?php

header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET');
header('Access-Control-Allow-Origin: http://localhost:3001');


if (isset($_GET['file']))
{
    switch($_GET['file'])
    {
        case 'house1':
            header('Content-Type: application/json');
            $file = 'houses/house1.json';
            $json = json_decode(file_get_contents($file),TRUE);
            echo json_encode($json, TRUE);
            die();
        break;
    }
}
?>

ThreeJS:

var loader = new THREE.ObjectLoader();  
    loader.load(distPath+"models/json.php?file=house1",function ( obj ) {
         scene.add( obj );
    });

S'amuser!

0
Stringified

Une capture d'écran de l'erreur

VM266 three.min.js:127 THREE.WebGLState: DOMException: Failed to execute 'texImage2D' on 'WebGLRenderingContext': The image element contains cross-Origin data, and may not be loaded.
    at Object.texImage2D (https://unpkg.com/[email protected]/build/three.min.js:127:99)
    at dg.r [as setTexture2D] (https://unpkg.com/[email protected]/build/three.min.js:103:189)

Je viens de rencontrer le même problème lorsque je travaillais sur ma démo de codepen: https://codepen.io/fritx/project/editor/AoLRoy

Résolu en passant à trois.js de 0.86 à> = 0.87

- <script src="https://unpkg.com/[email protected]/build/three.min.js"></script>
+ <script src="https://unpkg.com/[email protected]/build/three.min.js"></script><!-- >=0.87 (img.crossorigin) -->
0
Fritz Lin