Three.js 在通过 OBJMTLLoader 加载的对象上的多种材质

Posted

技术标签:

【中文标题】Three.js 在通过 OBJMTLLoader 加载的对象上的多种材质【英文标题】:Three.js multiple materials on object loaded via OBJMTLLoader 【发布时间】:2013-05-22 18:37:14 【问题描述】:

我有一个模型的“.obj”和“.mtl”文件,我正在通过OBJMTLLoader 加载它。 ".mtl" 指定要应用于模型的纹理,three.js 加载模型并使用应用的纹理进行渲染。

但事情就是这样。

加载一个对象后,我想在其上应用另一个纹理。这是因为第一纹理代表物体的表面材料。第二个纹理是一张图纸,我想将其放置在模型上的特定位置。

我的问题是:如何将第二个纹理应用到已加载(和纹理化)的对象上

我看到three.js 创建了一个THREE.Object3D 的实例,并且该实例有一个带有THREE.Mesh 实例的“子”数组。

当我尝试将纹理应用到该网格 (mesh.material.map = texture) 时,我失去了初始纹理。

我查看了这个question about applying multiple textures and JSONLoader,但没有找到答案。

我也尝试过使用new THREE.MeshFaceMaterial( materials )(如this answer 中所建议的那样)但没有成功。

更新

我尝试了@WestLangley 使用多材质对象的建议,但仍然无法在另一种材质之上渲染一种材质。

我做了这个简单的演示,改编自three.js OBJLoader — http://dl.dropboxusercontent.com/u/822184/webgl_multiple_texture/index.html

我按照建议使用THREE.SceneUtils.createMultiMaterialObject,传递从.obj 加载的主网格的克隆几何图形。我还为其提供了 2 种纹理——一种用于整个表面,另一种用于模型的前表面。

但这不起作用。我添加了 2 个复选框来切换相应材料的“可见”属性。您可以看到材料存在,但我无法从第二个下方看到第一个。

加载/渲染的关键如下:

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

更新 #2

在这一点上,我认为最好的办法是使用THREE.ShaderMaterial 将一个纹理应用到另一个上。我看到了一些examples of using one texture,但仍然不确定如何以重叠状态显示两者。我也不确定如何在网格上的特定位置定位纹理。

【问题讨论】:

类似问题(遗憾的是目前没有任何可靠的解决方案)-***.com/questions/12494781/… 您是否找到了将第二个纹理定位在网格上特定位置的解决方案?我遇到了同样的问题,我可以使用 ShaderMaterial 将纹理渲染到另一个纹理上,但我很难理解定位的工作原理。 【参考方案1】:

您有多种选择:

    您可以使用画布工具在 javascript 端混合图像,并使用单个纹理贴图创建单个材质。

    您可以从单个几何体和一组材质创建多材质对象。 (这种方法只是创建多个相同的网格,每个网格都有一种材质,通常在其中一种材质是线框时使用。如果一种材质是透明的,它也可以正常工作。)

    THREE.SceneUtils.createMultiMaterialObject( geometry, materials );

    您可以使用自定义ShaderMaterial 实现多纹理效果。有两个纹理输入,并在着色器中实现颜色混合。

这里是实现混合两种纹理的最简单的three.js ShaderMaterial 示例:https://jsfiddle.net/6bg4qdhx/3/。


编辑:另见Is there a built-in way to layer textures within the standard PBR shader?

three.js r.92

【讨论】:

谢谢!如果我要走第二条路线——多材质对象——我将如何处理已经创建的对象(因为 loader 为我做了这个)?我假设THREE.SceneUtils.createMultiMaterialObject 创建了一个对象。有办法修改吗? 实际上,我看到createMultiMaterialObject 中发生了什么。就像你说的,它使用每种材料创建网格。我想如果我将另一个孩子添加到该加载器创建的THREE.Object3D 实例中,并给它另一个纹理,那应该可以吗?嗯,我会试试的。再次感谢! 我尝试了您的第二种方法,但没有成功。请参阅有问题的更新。我错过了什么吗? 第二种方法仅在其中一种材料是透明的情况下才有效。 multiMaterialObject.children[0].material.transparent = truemultiMaterialObject.children[0].material.opacity = 0.5。就像我说的,它通常在其中一种材料是线框时使用。 没问题。我会将进一步的问题转移到单独的问题中。着色器示例是我需要澄清的主要内容。再次感谢。【参考方案2】:

您加载的对象具有几何体(及其顶点、面和 UV)和材质。 创建 ShaderMaterial,以某种适合您的方式组合纹理,并使用加载对象的几何体创建网格。

使用 ShaderMaterial 并将两个纹理设置为统一,然后在着色器中混合它们。

所以,你制作 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

);

并使用该材料创建网格:

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

你可以放最简单的顶点着色器:

varying vec2 vUv;

void main()

    vUv = uv;
    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    gl_Position = projectionMatrix * mvPosition;

以及真正进行混合的片段着色器:

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

【讨论】:

@kangax,这是你需要的吗?

以上是关于Three.js 在通过 OBJMTLLoader 加载的对象上的多种材质的主要内容,如果未能解决你的问题,请参考以下文章

three.js如何加载带材质的obj外部模型

谁有three.js的loader扩展包下载地址 或者给我发一下 谢谢 我需要objmtlload

THREE.js Ray Intersect 通过添加 div 失败

Three.js - 3D 空间中的 2D 对象(通过 Vertices)

Three.js - 将两个形状绑定在一起?

Three.js 进阶之旅:物理效果-碰撞和声音 💥