在运行时更改 Three.js 中加载的 .obj 的纹理 [英] Change texture of loaded .obj in three.js at runtime
问题描述
我试图在加载的three.js .obj 上在运行时交换图像纹理.这是直接来自three.js示例的代码,稍作修改:
I'm trying to swap image texture at runtime on a loaded three.js .obj. Here's the code straight from three.js examples with slight modification:
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 100;
//scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x101030 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 );
scene.add( directionalLight );
//manager
var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
console.log( item, loaded, total );
};
//model
var loader = new THREE.OBJLoader( manager );
loader.load( 'obj/female02/female02.obj', function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
//create a global var to reference later when changing textures
myMesh = child;
//apply texture
myMesh.material.map = THREE.ImageUtils.loadTexture( 'textures/ash_uvgrid01.jpg');
myMesh.material.needsUpdate = true;
}
} );
object.position.y = - 80;
scene.add( object );
} );
//render
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function newTexture() {
myMesh.material.map = THREE.ImageUtils.loadTexture( 'textures/land_ocean_ice_cloud_2048.jpg');
myMesh.material.needsUpdate = true;
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//animate
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
我添加的唯一内容是 newTexture 函数和对作为 myMesh 的网格的引用.这是原始示例 (http://threejs.org/examples/webgl_loader_obj.html).该函数不会引发任何错误,但 .obj 不会更新.我知道我只是在这里遗漏了一些基本的东西..
The only thing I added was the newTexture function and a reference to the mesh as myMesh. Here's the original example (http://threejs.org/examples/webgl_loader_obj.html). The function doesn't throw any errors but the .obj does not update. I know I'm just missing something fundamental here..
更新:根据下面的优秀答案,这里是正确的代码,并添加了一些通过输入字段交换纹理的代码:
var container, stats;
var camera, scene, renderer;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var globalObject;
init();
animate();
function init() {
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 100;
//scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x101030 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 );
scene.add( directionalLight );
//manager
var manager = new THREE.LoadingManager();
manager.onProgress = function (item, loaded, total) {
console.log( item, loaded, total );
};
//model
var loader = new THREE.OBJLoader( manager );
loader.load( 'obj/female02/female02.obj', function (object) {
//store global reference to .obj
globalObject = object;
object.traverse( function (child) {
if ( child instanceof THREE.Mesh ) {
child.material.map = THREE.ImageUtils.loadTexture( 'textures/grid.jpg');
child.material.needsUpdate = true;
}
});
object.position.y = - 80;
scene.add( object );
});
//render
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//animate
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
function newTexture() {
var newTexturePath = "textures/" + document.getElementById("texture").value + "";
globalObject.traverse( function ( child ) {
if (child instanceof THREE.Mesh) {
//create a global var to reference later when changing textures
child;
//apply texture
child.material.map = THREE.ImageUtils.loadTexture(newTexturePath);
child.material.needsUpdate = true;
}
});
}
推荐答案
这里的问题是 object
变量中有多个子网格.通过只有一个 myMesh
全局变量,您只存储了最后一个子网格.因此,当您尝试使用此全局变量更新纹理时,只有一个网格得到了纹理更新,可能是一个不太明显的隐藏部分.
The problem here is that there are multiple child meshes in the object
variable. By having only one myMesh
global variable, you are storing only the last child mesh. Thus when you try to update the texture using this global variable, only one of the meshes gets a texture update, probably a small hidden part that is not clearly visible.
解决方案是:
- 存储一个
myMeshes
数组全局变量,并在每次出现时将myMesh
推入其中.然后在您的newTexture()
函数中,遍历此数组中的所有项目并更新它们的贴图/材质. - 或者,将
object
变量(从OBJLoader
的回调返回的那个)存储到一个全局变量中.然后在您的newTexture()
函数中,遍历所有网格,就像它在代码中的完成方式一样(使用traverse()
和if
语句),并更新他们的贴图/材质.
- Store a
myMeshes
array global variable and pushmyMesh
into it every time there is one. Then in yournewTexture()
function, iterate through all items in this array and update their maps/materials. - Or, store the
object
variable (the one returned fromOBJLoader
's callback) into one single global variable. Then in yournewTexture()
function, iterate through all the meshes like how it is done in the code (usingtraverse()
andif
statement), and update their maps/materials.
最后,在代码中的某处调用 newTexture() 也有帮助;)
Lastly, calling newTexture() somewhere in your code would also help ;)
这篇关于在运行时更改 Three.js 中加载的 .obj 的纹理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!