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
Using textures in THREE.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 Object3D
for 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 textures
where every object contains the url
of a texture file and val
for 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 textures
push a new Promise in the array texturePromises
, every Promise will call loader.load
. If the value of entry.val
is a valid THREE.Texture
object, then resolve the promise.
promises 数组,对于map 中的每个对象调用textures
push 一个新的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.all
takes the promise array texturePromises
as 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:
结果:
回答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代码笔。