Three.js并加载跨域图像

时间:2022-06-16 03:46:40

I know this has been asked before, and I've read ever question and answer I've been able to find, but nothing works.

我知道以前曾经问过这个问题,而且我已经阅读了我能找到的问题和答案,但没有任何效果。

I'm running this on a local server (IIS). I'm trying to load an image from imgur and then use that as texture for an object using the code:

我在本地服务器(IIS)上运行它。我正在尝试从imgur加载图像,然后使用代码将其用作对象的纹理:

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;
    }

But it's giving the error:

但它给出了错误:

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

I've tried placing THREE.ImageUtils.crossOrigin = "anonymous";, or some variation of, at the beginning of my code, at the end, and at other various points. I've added a web.config with

我尝试在我的代码开头,最后和其他不同的点放置THREE.ImageUtils.crossOrigin =“anonymous”;或者一些变体。我添加了一个web.config

<?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>

but that didn't work. This also doesn't work on a site hosted on bitbucket.org, which to me says I'm missing something in my code.

但那没用。这也不适用于bitbucket.org上托管的网站,对我来说,我在代码中遗漏了一些内容。

It seems to be failing at the sphere.material = new THREE.MeshBasicMaterial({map: mapOverlay, needsUpdate: true});; line, as if I comment that out then there's no error (but then the mesh isn't updated).

它似乎在sphere.material = new THREE.MeshBasicMaterial({map:mapOverlay,needsUpdate:true});;一行,好像我发表评论然后没有错误(但网格没有更新)。

I'm really at a loss of what else to try here and any help would be appreciated.

我真的不知道在这里尝试什么,任何帮助将不胜感激。

3 个解决方案

#1


49  

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

注意:您不要在THREE.ImageUtils.loadTexture中使用new

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.

为了将图像跨源加载到WebGL中,发送图像的服务器必须使用正确的标头进行响应。仅仅说你想要使用图像交叉来源是不够的。所有这一切都告诉服务器您要求使用该图像的权限。

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.

你可以设置img.crossOrigin,或者在THREE的情况下将THREE.ImageUtils.crossOrigin设置为'','anonymous',它与''相同,或者你可以将它设置为'use-credentials',它可以发送更多信息给服务器。浏览器看到您设置了crossOrigin并将某些标头发送到服务器。服务器读取这些标头,决定您的域是否有权使用该图像,如果您有权限,它会将某些标头发送回浏览器。浏览器,如果它看到这些标题将允许您使用该图像。

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.

最重要的一点就是服务器必须发送标题。大多数服务器不发送这些标头。 imgur.com显然。我怀疑bitlocker没有,但我没有测试它。

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.

你还必须设置crossOrigin。如果你不这样做,浏览器将不允许你以你不应该的方式使用img,即使服务器发送正确的标题。

#2


23  

UPDATE: Deprecated method

更新:不推荐使用的方法

I came across this problem and applied solution from the answer to find it not working due to the deprecated method in newer releases of the THREE.js. I'm posting this answer in case anyone get the same issue. Despite deprecation, information provided by gman in original answer are most helpful and I recommend reading it.

我遇到了这个问题并从答案中应用了解决方案,因为THREE.js的新版本中已弃用的方法导致它无法正常工作。我发布这个答案,万一有人得到同样的问题。尽管弃用,gman在原始答案中提供的信息是最有帮助的,我建议阅读它。

THREE.ImageUtils.loadTexture()

method became deprecated since the original question and answer.

方法因原始问答而被弃用。

Current way to load the 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 ) {}
);

#3


0  

I found a solution for images and JSON models according to the cross-domain issue. This always works, also on Localhost.

我根据跨域问题找到了图像和JSON模型的解决方案。这总是有效,也在Localhost上。

In my case, I'm loading the game from NodeJS at port 3001, to work with Socket.io. I want the 3d models from port 80.

在我的情况下,我正在从端口3001的NodeJS加载游戏,以使用Socket.io。我想要80端口的3D模型。

Assume all models are in directory: game/dist/models/**/*

假设所有模型都在目录中:game / dist / models / ** / *

I created a PHP file game/dist/models/json.php:

我创建了一个PHP文件游戏/ 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 );
    });

Have fun!

#1


49  

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

注意:您不要在THREE.ImageUtils.loadTexture中使用new

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.

为了将图像跨源加载到WebGL中,发送图像的服务器必须使用正确的标头进行响应。仅仅说你想要使用图像交叉来源是不够的。所有这一切都告诉服务器您要求使用该图像的权限。

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.

你可以设置img.crossOrigin,或者在THREE的情况下将THREE.ImageUtils.crossOrigin设置为'','anonymous',它与''相同,或者你可以将它设置为'use-credentials',它可以发送更多信息给服务器。浏览器看到您设置了crossOrigin并将某些标头发送到服务器。服务器读取这些标头,决定您的域是否有权使用该图像,如果您有权限,它会将某些标头发送回浏览器。浏览器,如果它看到这些标题将允许您使用该图像。

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.

最重要的一点就是服务器必须发送标题。大多数服务器不发送这些标头。 imgur.com显然。我怀疑bitlocker没有,但我没有测试它。

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.

你还必须设置crossOrigin。如果你不这样做,浏览器将不允许你以你不应该的方式使用img,即使服务器发送正确的标题。

#2


23  

UPDATE: Deprecated method

更新:不推荐使用的方法

I came across this problem and applied solution from the answer to find it not working due to the deprecated method in newer releases of the THREE.js. I'm posting this answer in case anyone get the same issue. Despite deprecation, information provided by gman in original answer are most helpful and I recommend reading it.

我遇到了这个问题并从答案中应用了解决方案,因为THREE.js的新版本中已弃用的方法导致它无法正常工作。我发布这个答案,万一有人得到同样的问题。尽管弃用,gman在原始答案中提供的信息是最有帮助的,我建议阅读它。

THREE.ImageUtils.loadTexture()

method became deprecated since the original question and answer.

方法因原始问答而被弃用。

Current way to load the 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 ) {}
);

#3


0  

I found a solution for images and JSON models according to the cross-domain issue. This always works, also on Localhost.

我根据跨域问题找到了图像和JSON模型的解决方案。这总是有效,也在Localhost上。

In my case, I'm loading the game from NodeJS at port 3001, to work with Socket.io. I want the 3d models from port 80.

在我的情况下,我正在从端口3001的NodeJS加载游戏,以使用Socket.io。我想要80端口的3D模型。

Assume all models are in directory: game/dist/models/**/*

假设所有模型都在目录中:game / dist / models / ** / *

I created a PHP file game/dist/models/json.php:

我创建了一个PHP文件游戏/ 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 );
    });

Have fun!