Using Three.js to load an image from a different domain

Despite searching extensively and reading through all available resources, I am unable to find a solution to my issue.

I am currently running this code on a local server (IIS). My objective is to load an image from imgur and utilize it as a texture for an object using the following 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;
    }

However, an error stating:

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

Several attempts were made by adding

THREE.ImageUtils.crossOrigin = "anonymous";
to different parts of the code, but to no avail. Even with the inclusion of a web.config file containing:

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

The problem persists. It is worth noting that the issue also occurs on a site hosted on bitbucket.org, indicating a potential flaw in the coding process.

The error seems to originate from the line

sphere.material = new THREE.MeshBasicMaterial({map: mapOverlay, needsUpdate: true});;
, which highlights a failure to update the mesh despite preventing the error by commenting out the line.

I am unsure of what steps to take next and would appreciate any guidance or assistance offered. Thank you.

Answer №1

Latest Update

The newer versions of THREE.js now handle cross-origin images by default. The deprecated THREE.ImageUtils.loadTexture function is commonly replaced with the TextureLoader

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

Original Answer

This method works

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

Below is a demonstration:

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);  
        
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
    }
}

function render(time) {
    time *= 0.001; 
    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 need to use new when using THREE.ImageUtils.loadTexture

To load an image cross-origin into WebGL, the server must respond with the appropriate headers. Setting img.crossOrigin or THREE.ImageUtils.crossOrigin can be set to '', 'anonymous', or 'use-credentials'. The browser sends certain headers to the server which then decides if permission is granted before sending back specific headers that allow the image to be used.

Remember, it's crucial that the server sends the necessary headers. Not all servers do this automatically. While imgur.com does, other servers may not. Additionally, failure to set crossOrigin will restrict the usage of the image even if the server sends the correct headers.

Answer №2

UPDATE: Outdated Method

Upon encountering this issue, I tried the solution provided in a previous answer only to realize that it no longer works due to using a deprecated method in newer versions of THREE.js. I am sharing this updated answer for anyone facing the same problem. Despite the deprecation, the information shared by gman in the original answer is still valuable and I suggest referring to it.

THREE.ImageUtils.loadTexture()

The above method has been deprecated since the original question was asked and answered.

The current way to load a texture is as follows:

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

// enable cross origin loading
loader.crossOrigin = '';

// load the resource
loader.load('textures/land_ocean_ice_cloud_2048.jpg',
    // Function executed when the resource is loaded
    function ( texture ) {},
    // Function called during the download progress
    function ( xhr ) {},
    // Function triggered upon download errors
    function ( xhr ) {}
);

Answer №3

I have discovered a clever workaround for handling images and JSON models in the face of cross-domain restrictions. This method has proven to be effective, even when working on Localhost.

Specifically, I encountered this issue while loading a game from NodeJS on port 3001 in conjunction with Socket.io. My objective was to fetch 3D models from port 80.

To achieve this, I organized all models in the directory: game/dist/models/**/*

The solution involved creating a PHP file named game/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;
    }
}
?>

Incorporating ThreeJS:

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

Enjoy implementing this solution!

Answer №4

Take a look at the error screenshot

VM266 three.min.js:127 THREE.WebGLState: DOMException: Issue encountered with 'texImage2D' on 'WebGLRenderingContext': The image element has cross-origin data, and cannot be loaded.
    at Object.texImage2D (https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="92e6fae0f7f7d2a2bcaaa4bca2">[email protected]</a>/build/three.min.js:127:99)
    at dg.r [as setTexture2D] (https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="71051903141431415f49475f41">[email protected]</a>/build/three.min.js:103:189)

Encountered the same issue while working on my codepen demo: https://codepen.io/fritx/project/editor/AoLRoy

Resolved by updating three.js from version 0.86 to >=0.87

- <script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="70041802151530405e48465e40">[email protected]</a>/build/three.min.js"></script>
+ <script src="https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="384c504a5d5d780816000f1608">[email protected]</a>/build/three.min.js"></script><!-- >=0.87 (img.crossorigin) -->

Answer №5

For the TextureLoader, consider appending "?not-from-cache-please" to the image URL as a parameter.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using THREE.js in the pre-render function

I am encountering difficulties with updating the positions of my enemies before rendering them. Instead of creating a separate update() function, I attempted to use an onBeforeRender() function attached to each enemy object. However, nothing seems to be ...

Reconfigure a portion of a string using Vue's dynamic replacement feature

Currently tackling a problem. In my possession is a string that essentially consists of HTML code: let htmlTitle = "<a href="/news/sky-sport-hd-in-italia-dal-18-novembr">Sky Sport HD in italia dal 18 novembre</a> | <a href="/news/ecco-il-g ...

The cloned rows created with jQuery are failing to submit via the post method

Hello, I am currently working on a project in Django and could use some assistance with my JavaScript code. Specifically, I am trying to incorporate a button that adds new rows of inputs. The button does function properly as it clones the previous row usin ...

Could not complete operation: EPERM error indicating permission denied for 'stat' operation

Recently delving into Firebase Functions for my Flutter project has been a game-changer. Discovering the code generator, firebase_functions_interop, that seamlessly transforms Dart code into Javascript has made developing cloud functions in Dart a breeze. ...

Tips for adding values to an object

Behold, a complex object in multiple dimensions: let b = {} b.push({hello:'xyz'}) This method is currently inactive ...

What is preventing the successful insertion of a JSON array into a SQL database?

I am facing an issue with inserting a JSON array into an SQL database. I have an HTML table that stores its data in a JavaScript array, converts it to a JSON array, and then sends it to a PHP file. However, when trying to insert this JSON array into the da ...

What is the best way to show inline icons within menu items?

Issue Upon selecting an option from the menu, I encounter a problem where the icon (represented by a question mark inside a speech bubble) appears on a different line than the text ("Question"). Fig. 1. Display of menu after selection with the icon and t ...

Exploring the versatility of combining CSS classes with MUI 5 SX prop

Is there a way to use multiple CSS classes with the MUI 5 SX prop? I've defined a base class for my Box components and now I want to add a second class specifically for styling the text inside the Box. When I try to apply both classes like sx={styles. ...

Transforming a JSON object into a list in C#

After exploring similar posts without success, I am reaching out here for help. I have a Json data stored in a hidden field that I am trying to access in the code behind file of my markup page. My goal is to convert this Json into a List and bind it to a ...

Issue encountered when attempting to utilize Next-Auth alongside Credentials Provider to authenticate within a pre-existing system

I am currently utilizing the Next-Auth Credentials provider for authentication purposes through our existing API. Following the guidelines provided at https://next-auth.js.org/configuration/callbacks the code snippet used is as follows: callbacks: { ...

shifting the length of o to the right by zero with the

While exploring the polyfill function for Array.includes, I stumbled upon the following lines of code: // 2. Let len be ? ToLength(? Get(O, "length")). var len = o.length >>> 0; // 4. Let n be ? ToInteger(fromIndex). // (If fromIndex is undef ...

Execute the following onclick event when the button is enabled

I am trying to display a popup when the "add to cart" button is clicked (but only when the button is active!). I thought about using a div that wraps the button and changes the class from "disabled" to "enabled". I have tried using jQuery, but it could als ...

Iterating using a for loop within different functions

I am facing an issue with this project. I am planning to create a function that calculates from two different `for()` loops. The reason behind having 2 separate functions for calculation is because the data used in the calculations are fetched from differe ...

What should be transmitted to the front-end following the successful validation of a token on the server?

My process starts with a login page and then moves to the profile page. When it comes to handling the token on the backend, I use the following code: app.use(verifyToken); function verifyToken(req, res, next) { if (req.path === '/auth/google&ap ...

Error message: The function pokemon.map does not exist

I'm new to learning react and I've been working on creating a react app using the pokemon API. However, I've encountered an error that says TypeError: pokemon.map is not a function. I'm unsure why .map is not recognized as a function in ...

Getting Session from Next-Auth in API Route: A Step-by-Step Guide

When printing my session from Next Auth in a component like this, I can easily see all of its data. const session = useSession(); // ... <p>{JSON.stringify(session)}</p> I am facing an issue where I need to access the content of the session i ...

Click the button to increase the counter up to 2, and then decrease it back to 0 starting from 2

I'm struggling to implement this efficiently. My count keeps incrementing and decrementing by 1, causing me to get stuck at the value of 1 without going back down to zero. using react: jsfiddle The counter increases from 0 to 2 when onClick() is tri ...

jQuery - patience is required until all elements have completely faded away

I am facing a unique challenge: I need to trigger an action after two specific elements have been faded out simultaneously. The code snippet for this task is as follows: $("#publish-left, #publish-right, #print-run").fadeOut(function(){ //do something ...

How do I go about extracting and presenting the JSON data from the ESPN API?

In my current project, I am trying to utilize the jQuery library to work with API JSON data. Even though I am experienced in parsing JSON in PHP, I want to explore doing it with jQuery this time around. The goal is to extract information about each of the ...

Utilizing ElementRef in Angular 4 to close dropdown when clicking outside of it

I recently came across this helpful tutorial, but I'm having trouble grasping how it actually functions. Here's the code snippet I've incorporated into my TypeScript file: @Component({ host: { '(document:click)': 'onOuts ...