如何在Three.js中使用和混合多个带有自定义值的纹理?

7
在Three.js中,是否可以创建一个材质(着色器?),其中包含多个纹理,每个纹理都有自己的特定效果,并将它们混合在一起分配到几何体上?我想模拟具有多种表面处理技术的纸张表面,例如烫金或局部UV上光油。
我想实现的材质应具有以下特点:
1. '基础'层:
- 纸张颜色(例如白色,米色,米色等) - 镜面效果 - 反射率 - 凹凸(光滑或有纹理的纸张)
2. '图像'层(可选):
- 图像纹理(印在纸上的艺术品,与'基础'颜色叠加) 3. '箔'层(可选):
- 控制层透明度的黑白图像纹理 - 颜色(重叠的'基础'层) - 镜面效果 - 反射率
4. '浮雕/压花'层(可选):
- 凹凸纹理(提高或降低'基础'凹凸感)
5. '上光油'层(可选):
- 控制层透明度的黑白图像纹理 - 镜面效果 - 反射率 - 凹凸(轻微提高'基础'凹凸感)
所有单独的层之所以被分开,是因为我想在运行时单独控制它们(替换不同的纹理并更改值)。除了“图像”层外,我不确定其他层应使用什么混合值才能实现逼真的模拟。
为了说明应用于纸张的整理技术,请查看以下照片(由PrintHouse Corporation提供):
- 盲烫金(图案)和金色烫金(标志,标题)
![image](https://istack.dev59.com/Nlx7w.webp) - 银色烫金(徽标)和局部UV上光油(照片)
![image](https://istack.dev59.com/QFBGk.webp) - 局部UV上光油(线条)
![image](https://istack.dev59.com/LF8iw.webp)
这是否有可能?我找不到任何使用如此复杂材质的Three.js示例。如果您可以指向一个示例或解释如何通过编程方法进行处理,我将不胜感激。

这是一个很棒的项目,在我看来。@mrdoob,有什么想法吗?我相信[at]alteredq可以轻松完成这个任务。 :-) - WestLangley
2
有一个名为“MeshLayerMaterial”的系统可以允许这种材质,但是现在对我来说还为时过早。不过你可以使用自定义着色器和“ShaderMaterial”来实现这一点。 - mrdoob
1
感谢提供信息,期待MeshLayerMaterial的出现。是否有类似于我的问题复杂度的自定义着色器代码示例,并解释如何使用/混合多个uniform变量?干杯! - ALx
1个回答

1

像评论所说,你可以使用自定义着色器来实现这一点。你只需要思考如何在着色器中组合不同的层即可。 举个例子,你可以在js中设置你的着色器,就像这样:

blurTexture360 = new THREE.TextureLoader().load(imgURLBlur);
unblurTexture360 = new THREE.TextureLoader().load(imgURLUnblur);
highlight1Texture360 = new THREE.TextureLoader().load(imgURLHighlight1);
highlight2Texture360 = new THREE.TextureLoader().load(imgURLHighlight2);

blurTexture360.magFilter = THREE.LinearFilter;
blurTexture360.minFilter = THREE.LinearFilter;

const shader = THREE.ShaderLib.equirect;
uniforms = {
  u_resolution: { type: "v2", value: resolution },
  u_blur: { type: "t", value: blurTexture360 },
  u_unblur: { type: "t", value: unblurTexture360 },
  u_highlight1: { type: "t", value: highlight1Texture360 },
  u_highlight2: { type: "t", value: highlight2Texture360 },
  u_mix: { type: "f", value: 0.0 },
  u_highlightAmt: { type: "f", value: 0.0 },
  u_highlight1Bool: { type: "f", value: 1.0 },
  u_highlight2Bool: { type: "f", value: 1.0 }
};
let material360 = new THREE.ShaderMaterial({
  uniforms: uniforms,
  vertexShader: shader.vertexShader,
  fragmentShader: fragmentShader(),
  depthWrite: false,
  side: THREE.BackSide
});

然后在其他地方设置uniform变量。
uniforms.u_highlightAmt.value -= 0.05;

这是一个示例,当u_highlight1Bool或u_highlight2Bool设置为1或0时,不同的纹理将被设置为出现。希望这可以给您展示使用逻辑来影响着色器代码的示例。
function fragmentShader() {
    return `        
uniform sampler2D u_blur;
uniform sampler2D u_unblur;
uniform sampler2D u_highlight1;
uniform sampler2D u_highlight2;

uniform vec2 u_resolution;
uniform float u_mix;
uniform float u_highlight1Bool;
uniform float u_highlight2Bool;
uniform float u_highlightAmt;

varying vec3 vWorldDirection;
#define RECIPROCAL_PI2 0.15915494309189535
#define RECIPROCAL_PI 0.3183098861837907

vec2 equirectUv( in vec3 dir ) {
    // dir is assumed to be unit length
    float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;
    float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;
    return vec2( u, v );
}

void main() {
    vec3 direction = normalize( vWorldDirection );
    vec2 sampleUV = equirectUv( direction );
  vec4 blur =  texture2D(u_blur, sampleUV);
  vec4 unblur =  texture2D(u_unblur,  sampleUV);
  vec4 highlight1 = texture2D(u_highlight1, sampleUV);
  vec4 highlight2 = texture2D(u_highlight2, sampleUV);

    blur = mapTexelToLinear( blur );
  unblur = mapTexelToLinear( unblur );
  highlight1 = mapTexelToLinear(highlight1);
  highlight2 = mapTexelToLinear(highlight2);
  vec4 highlight = (highlight1*u_highlight1Bool) + (highlight2 * u_highlight2Bool);

  vec4 ret = mix(blur, unblur, u_mix);
  float thresh = ceil(highlight.r - 0.09) * u_highlightAmt;
  ret = mix(ret,highlight,thresh);

  gl_FragColor = ret;
}
`;

我从我的代码中得到这个:https://glitch.com/edit/#!/hostileterrain94?path=script%2FthreeSixtyEnv.js%3A202%3A0 它没有很好的记录,但如果你想看看它如何存在于一个更广泛的项目中,请随意瞥一眼。我不能保证它真的很有效率,但我知道它可以完成任务。

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