Three.js multiple materials on object loaded via OBJMTLLoader

匿名 (未验证) 提交于 2019-12-03 02:33:02

问题:

I have ".obj" and ".mtl" files of a model and I'm loading it via OBJMTLLoader. ".mtl" specifies texture to apply to a model, and three.js loads model and renders it with applied texture just fine.

But here's the thing.

Once an object is loaded, I would like to apply another texture onto it. This is because first texture represents surface material of an object. And second texture is a drawing, that I'd like to position at a specific location on a model.

My question is: how to apply a second texture onto already loaded (and texturized) object?

I see that three.js creates an instance of THREE.Object3D, and that instance has "children" array with one instance of THREE.Mesh.

When I try to apply a texture to that mesh (mesh.material.map = texture), I lose initial texture.

I looked into this question about applying multiple textures and JSONLoader but didn't find an answer.

I also tried using new THREE.MeshFaceMaterial( materials ) (as suggested in this answer) but unsuccessfully.

UPDATE:

I tried @WestLangley's suggestion to use multi-material object, but am still unable to render one material on top of another one.

I made this simple demo, adapted from three.js OBJLoader ― http://dl.dropboxusercontent.com/u/822184/webgl_multiple_texture/index.html

I'm using THREE.SceneUtils.createMultiMaterialObject as suggested, passing it cloned geometry of main mesh loaded from .obj. I'm also giving it 2 textures ― one for entire surface, another one ― for front surface of the model.

But this doesn't work. I added 2 checkboxes that toggle "visible" property of corresponding materials. You can see that materials are present, but I can't see the first one from beneath second one.

The crux of the loading/rendering is as follows:

var texture = THREE.ImageUtils.loadTexture('fabric.jpg'); var texture2 = THREE.ImageUtils.loadTexture('table.jpg');  texture2.offset.set(-0.65, -2.5); texture2.repeat.set(4, 4);  var loader = new THREE.OBJLoader(); loader.addEventListener( 'load', function ( event ) {    var mainMesh = event.content.children[0].children[0];    multiMaterialObject = THREE.SceneUtils.createMultiMaterialObject(      mainMesh.geometry.clone(), [       new THREE.MeshLambertMaterial({ map: texture2 }),       new THREE.MeshLambertMaterial({ map: texture })     ]);    multiMaterialObject.position.y = -80;   scene.add(multiMaterialObject); });  loader.load( 'male02.obj' ); 

UPDATE #2

At this point, I think the best bet is to use THREE.ShaderMaterial to apply one texture onto another. I see some examples of using one texture but still unsure how to display both in overlaid state. I'm also not sure how to position texture at a specific location on a mesh.

回答1:

You have several choices:

  1. You can mix the images on the javascript side using canvas tools, and create a single material with a single texture map.

  2. You can create a multi-material object from a single geometry and an array of materials. (This approach just creates multiple identical meshes, each with one of the materials, and usually is used when one of the materials is wireframe. It may also work OK if one material is transparent.)

    THREE.SceneUtils.createMultiMaterialObject( geometry, materials );

  3. You can achieve a multi-texture effect with a custom ShaderMaterial. Have two texture inputs, and implement color mixing in the shader.

Here an example of just about the simplest three.js ShaderMaterial possible that implements mixing of two textures: https://jsfiddle.net/6bg4qdhx/3/.

EDIT: fiddle updated to three.js r.85

EDIT: fiddle resources changed to be loaded via https



回答2:

Your loaded object has geometry (along with its vertices, faces and UVs) and material. Create ShaderMaterial that combines the textures in some way that suits you and create mesh with geometry from loaded object.

Use ShaderMaterial and set both textures as uniforms, and then blend them within shader.

So, you make ShaderMaterial:

var vertShader = document.getElementById('vertex_shh').innerHTML; var fragShader = document.getElementById('fragment_shh').innerHTML;  var attributes = {}; // custom attributes  var uniforms = {    // custom uniforms (your textures)    tOne: { type: "t", value: THREE.ImageUtils.loadTexture( "cover.png" ) },   tSec: { type: "t", value: THREE.ImageUtils.loadTexture( "grass.jpg" ) }  };  var material_shh = new THREE.ShaderMaterial({    uniforms: uniforms,   attributes: attributes,   vertexShader: vertShader,   fragmentShader: fragShader  }); 

And create mesh with that material:

var me = new THREE.Mesh( my_loaded_model, material_shh ); // you previously loaded geometry of the object 

You can put simplest vertex shader:

varying vec2 vUv;  void main() {     vUv = uv;     vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );     gl_Position = projectionMatrix * mvPosition; } 

And fragment shader that will actually do the blending:

#ifdef GL_ES precision highp float; #endif  uniform sampler2D tOne; uniform sampler2D tSec;  varying vec2 vUv;  void main(void) {     vec3 c;     vec4 Ca = texture2D(tOne, vUv);     vec4 Cb = texture2D(tSec, vUv);     c = Ca.rgb * Ca.a + Cb.rgb * Cb.a * (1.0 - Ca.a);  // blending equation or wahtever suits you     gl_FragColor= vec4(c, 1.0); } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!