Three.js,如何在不同uniform集合的网格之间共享ShaderMaterial?

7
正如标题所说,我想为不同的网格重复使用给定的ShaderMaterial,但每个网格都有不同的uniforms集合(实际上,某些uniforms可能会在网格之间变化,但不一定是全部):这可行吗?
对我来说,必须为每个网格创建一个完整的ShaderMaterial在这种情况下似乎是浪费资源的,因为我的想法是拥有一个单独的顶点/片段着色器程序,但通过不同的uniforms进行配置,它们的值将根据网格而变化。如果我为每个网格创建一个新的ShaderMaterial,我最终将得到大量的重复(顶点+片段程序+材料/ShaderMaterial类的所有其他数据成员)。
如果引擎能够在绘制网格之前调用回调函数,我可以更改uniforms并实现我想要做的事情。另一个可能性是拥有“LiteShaderMaterial”,它将持有指向共享ShaderMaterial的指针+仅适用于我的网格的特定uniforms。
请注意,我的问题与此相关:Many meshes with the same geometry and material, can I change their colors? 但仍然不同,因为我主要关心资源浪费 - 在性能方面,我认为多个ShaderMaterial或单个ShaderMaterial之间的差异不大,因为引擎应该足够聪明,注意到所有材料都具有相同的程序,并且不会重新发送它们到gfx卡。
谢谢。
2个回答

10
当克隆一个 ShaderMaterial 时,属性和顶点/片元程序是按引用复制的。只有 uniform 是按值复制的,这正是你想要的。
这应该是有效的。
您可以通过创建一个 ShaderMaterial 并使用 ShaderMaterial.clone()为每个网格克隆它,然后分配每个材质唯一的 uniform 值来证明它。
在控制台中输入“render.info”。它应该显示1个程序。
three.js r.64

谢谢你的回答,但正如我上面所说的,我并不真正关心性能,而是更关心资源:例如,每个材质都会复制顶点/片段程序以及ShaderMaterial / Material的所有其他数据成员。虽然在拥有所有内存的今天可能不是一个大问题,但是如果我需要创建数千个ShaderMaterial因为我有数千个网格,那感觉就不对了,尤其当我只有一个(或几个不同的)顶点/片段着色器,并且我所需要做的就是改变这些程序的输入。 - Popov
1
不是这样的。在克隆时,顶点/片段程序和属性是按引用复制的。只有统一变量是按值复制的,这正是您想要的。 - WestLangley
ShaderMaterial类中的顶点和片段着色器变量是字符串,因此在克隆材质时每次都会复制整个程序。此外,当我创建一个新材质时,我会得到所有其他数据成员的副本,这些值与我的初始着色器具有相同的值,但我真的不需要这些值,因为这些值不会改变(而且大部分都不会被使用)。我猜我需要上面描述的东西,一个轻量级的ShaderMaterial,我会为每个网格实例化它,并且只保留uniforms数组和对“主”着色器的引用。 - Popov
不是真的。在克隆材料时,顶点/片段程序字符串并不会每次都被复制。只有一个引用被复制。 - WestLangley
@Popov 你确定如果我们执行 object1.foo = "foo"; object2.foo = object1.foo,那么内存中只会有一个字符串实例吗?这个有证据吗? - trusktr
显示剩余2条评论

1
您可以安全地创建多个具有相同参数的ShaderMaterial实例,使用clone或其他方式。由于对于每个实例,material.needsUpdate最初为true,因此Three.js将执行一些额外的检查,但随后它将能够为所有实例重用相同的程序。
在较新的版本中,另一个选项是使用单个ShaderMaterial,但在对象的onBeforeRender函数中添加对uniforms的更改。这避免了在渲染器中不必要的调用initMaterial,但无论整体上是否使其成为更快的解决方案都需要进行测试。如果在渲染之前修改的内容过多,则可能是一种风险解决方案,在最坏的情况下,单个材质可能需要在渲染期间重新编译多次。我建议您阅读this guide以获取进一步的提示。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接