Javascript 在 THREE.js 中使用纹理

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7919516/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 04:04:28  来源:igfitidea点击:

Using textures in THREE.js

javascriptthree.js

提问by Andrea

I am starting with THREE.js, and I am trying to draw a rectangle with a texture on it, lit by a single source of light. I think this is as simple as it gets (HTML omitted for brevity):

我从 THREE.js 开始,我试图绘制一个带有纹理的矩形,由单一光源照亮。我认为这很简单(为简洁起见,省略了 HTML):

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

The problem is, I cannot see anything. If I change the material and use the commented one, a square appears as I would expect. Note that

问题是,我什么都看不到。如果我更改材料并使用注释的材料,则会如我所愿地出现一个正方形。注意

  • The texture is 256x256, so its sides are power of two
  • The function is actually called when the body is loaded; indeed it works with a different material.
  • It does not work even if I serve the file from a webserver, so it is not an issue of cross-domain policy not allowing to load the image.
  • 纹理是 256x256,所以它的边是 2 的幂
  • 该函数实际上是在加载主体时调用的;事实上,它适用于不同的材料。
  • 即使我从网络服务器提供文件,它也不起作用,所以这不是跨域策略不允许加载图像的问题。

What I am I doing wrong?

我做错了什么?

回答by Andrea

By the time the image is loaded, the renderer has already drawn the scene, hence it is too late. The solution is to change

到加载图像时,渲染器已经绘制了场景,因此为时已晚。解决办法是改变

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

into

进入

texture = THREE.ImageUtils.loadTexture('crate.gif', {}, function() {
    renderer.render(scene);
}),

回答by Mustafah

Andrea solution is absolutely right, I will just write another implementation based on the same idea. If you took a look at the THREE.ImageUtils.loadTexture() sourceyou will find it uses the javascript Image object. The $(window).load event is fired after all Images are loaded ! so at that event we can render our scene with the textures already loaded...

Andrea 的解决方案是绝对正确的,我将基于相同的想法编写另一个实现。如果您查看 THREE.ImageUtils.loadTexture()源代码,您会发现它使用了 javascript Image 对象。$(window).load 事件在所有图像加载后触发!所以在那个事件中,我们可以使用已经加载的纹理渲染我们的场景......

  • 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);
        });
    });
    
  • 咖啡脚本

    $(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);
        });
    });
    

Thanks...

谢谢...

回答by Sky Yip

In version r75 of three.js, you should use:

在three.js的r75版本中,您应该使用:

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

回答by Marcs

In version r82 of Three.js TextureLoaderis the object to use for loading a texture.

在three.js所版本R82 TextureLoader是用来加载纹理的对象。

Loading one texture (source code, demo)

加载一个纹理(源代码演示

Extract (test.js):

提取(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);

Loading multiple textures (source code, demo)

加载多个纹理(源代码演示

In this example the textures are loaded inside the constructor of the mesh, multiple texture are loaded using Promises.

在这个例子中,纹理是在网格的构造函数中加载的,多个纹理是使用Promises加载的。

Extract (Globe.js):

提取(Globe.js):

Create a new container using Object3Dfor having two meshes in the same container:

创建一个新容器,Object3D用于在同一个容器中包含两个网格:

var Globe = function (radius, segments) {

  THREE.Object3D.call(this);

  this.name = "Globe";

  var that = this;

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

A map called textureswhere every object contains the urlof a texture file and valfor storing the value of a Three.js textureobject.

一个被调用的地图textures,其中每个对象都包含url一个纹理文件并val用于存储 Three.js纹理对象的值。

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

The array of promises, for each object in the map called texturespush a new Promise in the array texturePromises, every Promise will call loader.load. If the value of entry.valis a valid THREE.Textureobject, then resolve the promise.

promises 数组,对于map 中的每个对象调用texturespush 一个新的Promise 数组texturePromises,每个Promise 都会调用loader.load。如果 的值entry.val是一个有效的THREE.Texture对象,则解析承诺。

  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.alltakes the promise array texturePromisesas argument. Doing so makes the browser wait for all the promises to resolve, when they do we can load the geometry and the material.

Promise.all将 promise 数组texturePromises作为参数。这样做会使浏览器等待所有的 promise 解决,当它们解决时,我们可以加载几何体和材质。

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

For the cloud sphere only one texture is necessary:

对于云球体,只需要一种纹理:

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

回答by Hitesh Sahu

Without Error Handeling

无错误处理

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

With Error Handling

带错误处理

// 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);

Result:

结果:

enter image description here

在此处输入图片说明

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

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

回答by Hitesh Sahu

Use TextureLoader to load a image as texture and then simply apply that texture to scene background.

使用 TextureLoader 将图像加载为纹理,然后简单地将该纹理应用于场景背景。

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

Result:

结果:

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

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

看笔 Flat Earth Three.JS扁平地球三.JS由 Hitesh Sahu (@hiteshsahu@hiteshsahu) 上CodePen代码笔