在THREE.js中使用纹理

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

问题是,我什么也看不见。 如果我改变材料并使用注释,那么就会出现一个正方形。 注意

  • 纹理是256×256,所以它的边是两个幂
  • 该函数实际上被调用时,身体被加载; 事实上,它使用不同的材料。
  • 即使我从Web服务器提供文件,它也不起作用,所以它不是不允许加载图像的跨域策略问题。

我做错了什么?

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

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

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

Andrea的解决scheme是绝对正确的,我只是写一个基于相同想法的实现。 如果你看看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); }); }); 

谢谢…

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

在Three.js的r82版本中,TextureLoader是用于加载纹理的对象。

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

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

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

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

提取( Globe.js ):

使用Object3D创build一个新的容器,使两个网格位于同一个容器中:

 var Globe = function (radius, segments) { THREE.Object3D.call(this); this.name = "Globe"; var that = this; // instantiate a loader var loader = new THREE.TextureLoader(); 

一个称为textures ,其中每个对象都包含纹理文件的urlval用于存储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 } }; 

承诺数组,对于映射中的每个对象称为textures在数组texturePromises推入一个新的Promise,每个Promise都会调用loader.load 。 如果entry.val的值是一个有效的THREE.Texture对象,则parsing该promise。

  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.all以promise数组texturePromises作为参数。 这样做使浏览器等待所有的承诺解决,当他们这样做,我们可以加载几何和材料。

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

对于云球只需要一个纹理:

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