Verwenden von Texturen in THREE.js

70

Ich beginne mit THREE.js und versuche, ein Rechteck mit einer Textur darauf zu zeichnen, das von einer einzelnen Lichtquelle beleuchtet wird. Ich denke, das ist so einfach wie es nur geht (HTML der Kürze halber weggelassen):

function loadScene() {
    var world = document.getElementById('world'),
        WIDTH = 1200,
        HEIGHT = 500,
        VIEW_ANGLE = 45,
        ASPECT = WIDTH / HEIGHT,
        NEAR = 0.1,
        FAR = 10000,

        renderer = new THREE.WebGLRenderer(),
        camera = new THREE.Camera(VIEW_ANGLE, ASPECT, NEAR, FAR),
        scene = new THREE.Scene(),
        texture = THREE.ImageUtils.loadTexture('crate.gif'),
        material = new THREE.MeshBasicMaterial({map: texture}),
        // material = new THREE.MeshPhongMaterial({color: 0xCC0000});
        geometry = new THREE.PlaneGeometry(100, 100),
        mesh = new THREE.Mesh(geometry, material),
        pointLight = new THREE.PointLight(0xFFFFFF);

    camera.position.z = 200;    
    renderer.setSize(WIDTH, HEIGHT);
    scene.addChild(mesh);
    world.appendChild(renderer.domElement);
    pointLight.position.x = 50;
    pointLight.position.y = 50;
    pointLight.position.z = 130;
    scene.addLight(pointLight); 
    renderer.render(scene, camera);
}

Das Problem ist, ich kann nichts sehen. Wenn ich das Material ändere und das kommentierte verwende, erscheint ein Quadrat, wie ich es erwarten würde. Beachten Sie, dass

  • Die Textur ist 256x256, also sind seine Seiten Zweierpotenz
  • Die Funktion wird tatsächlich aufgerufen, wenn der Körper geladen wird. in der Tat funktioniert es mit einem anderen Material.
  • Es funktioniert nicht, selbst wenn ich die Datei von einem Webserver aus bereitstelle. Es handelt sich also nicht um eine domänenübergreifende Richtlinie, die das Laden des Images nicht zulässt.

Was mache ich falsch?

Andrea
quelle

Antworten:

73

Zum Zeitpunkt des Ladens des Bildes hat der Renderer die Szene bereits gezeichnet, daher ist es zu spät. Die Lösung ist zu ändern

texture = THREE.ImageUtils.loadTexture('crate.gif'),

in

texture = THREE.ImageUtils.loadTexture('crate.gif', {}, function() {
    renderer.render(scene);
}),
Andrea
quelle
9
Heutzutage lautet die Antwort: Graben THREE.ImageUtils.loadTexture(was veraltet ist) und THREE.TextureLoader.loadstattdessen verwenden. ImageUtilshat bei mir überhaupt nicht funktioniert, aber TextureLoaderbeim ersten Versuch perfekt funktioniert. Docs: threejs.org/docs/index.html#api/loaders/TextureLoader
ArtOfWarfare
27

Andrea Lösung ist absolut richtig, ich werde nur eine weitere Implementierung schreiben, die auf der gleichen Idee basiert. Wenn Sie sich die Quelle THREE.ImageUtils.loadTexture () angesehen haben, werden Sie feststellen, dass sie das Javascript-Image-Objekt verwendet. Das Ereignis $ (window) .load wird ausgelöst, nachdem alle Bilder geladen wurden! In diesem Fall können wir unsere Szene mit den bereits geladenen Texturen rendern ...

  • CoffeeScript

    $(document).ready ->
    
        material = new THREE.MeshLambertMaterial(map: THREE.ImageUtils.loadTexture("crate.gif"))
    
        sphere   = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, rings), material)
    
        $(window).load ->
            renderer.render scene, camera
    
  • JavaScript

    $(document).ready(function() {
    
        material = new THREE.MeshLambertMaterial({ map: THREE.ImageUtils.loadTexture("crate.gif") });
    
        sphere = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, rings), material);
    
        $(window).load(function() {
            renderer.render(scene, camera);
        });
    });
    

Vielen Dank...

Mustafah
quelle
22

In Version r75 von three.js sollten Sie Folgendes verwenden:

var loader = new THREE.TextureLoader();
loader.load('texture.png', function ( texture ) {
  var geometry = new THREE.SphereGeometry(1000, 20, 20);
  var material = new THREE.MeshBasicMaterial({map: texture, overdraw: 0.5});
  var mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);
});
Himmel Yip
quelle
Gute Antwort! Ich vermute, die API hat sich seit der ursprünglichen Antwort geändert. Ihre Antwort ist funktional! Danke vielmals!
CowWarrior
Sie hätten wahrscheinlich angeben sollen, welche Revision von three.js Sie als neueste bezeichnet haben .
Harsh
6

In Version r82 von Three.js ist TextureLoader das Objekt, das zum Laden einer Textur verwendet werden soll.

Laden einer Textur ( Quellcode , Demo )

Auszug ( test.js ):

var scene = new THREE.Scene();
var ratio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight,
  0.1, 50);

var renderer = ...

[...]

/**
 * Will be called when load completes.
 * The argument will be the loaded texture.
 */
var onLoad = function (texture) {
  var objGeometry = new THREE.BoxGeometry(20, 20, 20);
  var objMaterial = new THREE.MeshPhongMaterial({
    map: texture,
    shading: THREE.FlatShading
  });

  var mesh = new THREE.Mesh(objGeometry, objMaterial);

  scene.add(mesh);

  var render = function () {
    requestAnimationFrame(render);

    mesh.rotation.x += 0.010;
    mesh.rotation.y += 0.010;

    renderer.render(scene, camera);
  };

  render();
}

// Function called when download progresses
var onProgress = function (xhr) {
  console.log((xhr.loaded / xhr.total * 100) + '% loaded');
};

// Function called when download errors
var onError = function (xhr) {
  console.log('An error happened');
};

var loader = new THREE.TextureLoader();
loader.load('texture.jpg', onLoad, onProgress, onError);

Laden mehrerer Texturen ( Quellcode , Demo )

In diesem Beispiel werden die Texturen in den Konstruktor des Netzes geladen, mehrere Texturen werden mit Promises geladen .

Auszug ( Globe.js ):

Erstellen Sie einen neuen Container, Object3Dindem Sie zwei Maschen im selben Container haben:

var Globe = function (radius, segments) {

  THREE.Object3D.call(this);

  this.name = "Globe";

  var that = this;

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

Eine Karte genannt , textureswo jedes Objekt das enthält urleine Textur - Datei und valfür den Wert eines Three.js Speicherung Textur - Objekts.

  // earth textures
  var textures = {
    'map': {
      url: 'relief.jpg',
      val: undefined
    },
    'bumpMap': {
      url: 'elev_bump_4k.jpg',
      val: undefined
    },
    'specularMap': {
      url: 'wateretopo.png',
      val: undefined
    }
  };

Das Array von Versprechungen, für jedes Objekt in der Karte, das als texturesPush ein neues Versprechen im Array bezeichnet wird texturePromises, wird von jedem Versprechen aufgerufen loader.load. Wenn der Wert von entry.valein gültiges THREE.TextureObjekt ist, lösen Sie das Versprechen auf.

  var texturePromises = [], path = './';

  for (var key in textures) {
    texturePromises.push(new Promise((resolve, reject) => {
      var entry = textures[key]
      var url = path + entry.url

      loader.load(url,
        texture => {
          entry.val = texture;
          if (entry.val instanceof THREE.Texture) resolve(entry);
        },
        xhr => {
          console.log(url + ' ' + (xhr.loaded / xhr.total * 100) +
            '% loaded');
        },
        xhr => {
          reject(new Error(xhr +
            'An error occurred loading while loading: ' +
            entry.url));
        }
      );
    }));
  }

Promise.allNimmt das Versprechen-Array texturePromisesals Argument. Dadurch wartet der Browser auf die Lösung aller Versprechen. Wenn dies der Fall ist, können wir die Geometrie und das Material laden.

  // load the geometry and the textures
  Promise.all(texturePromises).then(loadedTextures => {

    var geometry = new THREE.SphereGeometry(radius, segments, segments);
    var material = new THREE.MeshPhongMaterial({
      map: textures.map.val,
      bumpMap: textures.bumpMap.val,
      bumpScale: 0.005,
      specularMap: textures.specularMap.val,
      specular: new THREE.Color('grey')
    });

    var earth = that.earth = new THREE.Mesh(geometry, material);
    that.add(earth);
  });

Für die Wolkenkugel ist nur eine Textur erforderlich:

  // clouds
  loader.load('n_amer_clouds.png', map => {
    var geometry = new THREE.SphereGeometry(radius + .05, segments, segments);
    var material = new THREE.MeshPhongMaterial({
      map: map,
      transparent: true
    });

    var clouds = that.clouds = new THREE.Mesh(geometry, material);
    that.add(clouds);
  });
}

Globe.prototype = Object.create(THREE.Object3D.prototype);
Globe.prototype.constructor = Globe;
Marcs
quelle
1

Ohne Fehler Handeling

//Load background texture
 new THREE.TextureLoader();
loader.load('https://images.pexels.com/photos/1205301/pexels-photo-1205301.jpeg' , function(texture)
            {
             scene.background = texture;  
            });

Mit Fehlerbehandlung

// Function called when download progresses
var onProgress = function (xhr) {
  console.log((xhr.loaded / xhr.total * 100) + '% loaded');
};

// Function called when download errors
var onError = function (error) {
  console.log('An error happened'+error);
};

//Function  called when load completes.
var onLoad = function (texture) {
  var objGeometry = new THREE.BoxGeometry(30, 30, 30);
  var objMaterial = new THREE.MeshPhongMaterial({
    map: texture,
    shading: THREE.FlatShading
  });

  var boxMesh = new THREE.Mesh(objGeometry, objMaterial);
  scene.add(boxMesh);

  var render = function () {
    requestAnimationFrame(render);
    boxMesh.rotation.x += 0.010;
    boxMesh.rotation.y += 0.010;
      sphereMesh.rotation.y += 0.1;
    renderer.render(scene, camera);
  };

  render();
}


//LOAD TEXTURE and on completion apply it on box
var loader = new THREE.TextureLoader();
    loader.load('https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/The_Earth_seen_from_Apollo_17.jpg/1920px-The_Earth_seen_from_Apollo_17.jpg', 
                onLoad, 
                onProgress, 
                onError);

Ergebnis:

Geben Sie hier die Bildbeschreibung ein

https://codepen.io/hiteshsahu/pen/jpGLpq/

Hitesh Sahu
quelle
0

Verwenden Sie TextureLoader, um ein Bild als Textur zu laden, und wenden Sie diese Textur dann einfach auf den Szenenhintergrund an.

 new THREE.TextureLoader();
     loader.load('https://images.pexels.com/photos/1205301/pexels-photo-1205301.jpeg' , function(texture)
                {
                 scene.background = texture;  
                });

Ergebnis:

https://codepen.io/hiteshsahu/pen/jpGLpq?editors=0011

Siehe den Pen Flat Earth Three.JS von Hitesh Sahu ( @hiteshsahu ) auf CodePen .

Hitesh Sahu
quelle