You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

THREE.js赛车服定制器:叠加图案时底色异常变色问题求助

问题:Three.js赛车服定制器叠加图案后底色视觉偏移(Hex值未变)

我用Three.js开发赛车服定制器,支持选择底色、应用条纹/格纹/心形等图案、调整图案颜色,以及叠加图案后修改底色。但遇到一个问题:叠加图案后,底色视觉上会发生偏移(比如粉色变成深红色),但Hex代码完全没有变化;切换回纯色模式时,底色显示又恢复正常。

技术配置

  • 底色:通过HTML颜色选择器获取值
  • 图案:使用GIMP制作的透明背景PNG(图案区域不透明、为黑色,背景完全透明)
  • 叠加实现:通过ShaderMaterial编写着色器完成图案与底色的混合

已排查内容

  • 调整过顶点着色器与片元着色器的逻辑
  • 确认PNG图片的背景完全透明、图案区域不透明

核心实现代码如下:

材质更新函数

const updateMaterials = () => {
if (!silkModel) return;

const sections = [
  { name: "jacketmaterial", colorId: "jacketColor", patternId: "jacketPattern", patternColorId: "jacketPatternColor" },
  { name: "lsleevematerial", colorId: "sleevesColor", patternId: "sleevesPattern", patternColorId: "sleevesPatternColor" },
  { name: "rsleevematerial", colorId: "sleevesColor", patternId: "sleevesPattern", patternColorId: "sleevesPatternColor" },
  { name: "capmaterial", colorId: "capColor", patternId: "capPattern", patternColorId: "capPatternColor" },
];

sections.forEach(({ name, colorId, patternId, patternColorId }) => {
  const baseColorValue = document.getElementById(colorId)?.value || "#ffffff";
  const patternColorValue = document.getElementById(patternColorId)?.value || "#ffffff";
  const selectedPattern = document.getElementById(patternId)?.value || "solid";

  const baseColor = new THREE.Color(baseColorValue);
  const patternColor = new THREE.Color(patternColorValue);

  silkModel.traverse((child) => {
    if (child.isMesh && child.material && child.material.name === name) {
      console.log("Updating material for:", name);
      console.log("Base Color:", baseColorValue);
      console.log("Pattern Color:", patternColorValue);
      console.log("Selected Pattern:", selectedPattern);

      let texture =
        selectedPattern === "custom" ? customPatterns.current[name] : patterns[selectedPattern];

      console.log("Texture Info:", texture);

      if (!texture || selectedPattern === "solid") {
        child.material = new THREE.MeshStandardMaterial({
          color: baseColor,
          emissive: baseColor,
          emissiveIntensity: 0.5,
          side: THREE.DoubleSide,
          name: child.material.name,
        });
      } else {
        child.material = new THREE.ShaderMaterial({
          uniforms: {
              baseColor: { value: baseColor },
              patternColor: { value: patternColor },
              patternTexture: { value: texture },
          },
          vertexShader: `
              varying vec2 vUv;
              void main() {
                  vUv = uv;
                  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
              }
          `,
          fragmentShader: `
          uniform vec3 baseColor;
          uniform vec3 patternColor;
          uniform sampler2D patternTexture;
          varying vec2 vUv;
        
          void main() {
            vec4 texelColor = texture2D(patternTexture, vUv);
            float alpha = texelColor.a; // Transparency of the pattern
        
            // If alpha is 0, use the base color only. If alpha > 0, blend the pattern color.
            vec3 outputColor = mix(baseColor, patternColor, alpha);
        
            gl_FragColor = vec4(outputColor, 1.0);
          }
        `,
          
          side: THREE.DoubleSide,
          transparent: true, // Ensures blending with the base color
          name: child.material.name,
      });
      
      }
      child.material.needsUpdate = true;
    }
  });
});
};

纹理加载代码

heart: textureLoader.load("/textures/black_heart_pattern.png", (texture) => {
  texture.colorSpace = THREE.SRGBColorSpace;
  texture.flipY = false;
}),

解决方案

问题根源

  1. 颜色空间不匹配:纯色模式使用的MeshStandardMaterial会自动处理sRGB到线性空间的转换,但自定义ShaderMaterial中直接使用baseColor的原始值,没有做颜色空间转换,导致底色显示偏暗。
  2. 冗余的透明属性:ShaderMaterial中开启了transparent: true,但我们的混合逻辑已经通过alpha手动控制,WebGL的默认透明混合公式会干扰最终颜色显示。
  3. 材质属性不一致:ShaderMaterial缺少emissive和emissiveIntensity属性,和纯色模式的材质光照表现不一致,进一步放大颜色差异。

修复代码

更新后的ShaderMaterial实现

child.material = new THREE.ShaderMaterial({
  uniforms: {
      baseColor: { value: baseColor },
      patternColor: { value: patternColor },
      patternTexture: { value: texture },
      emissive: { value: baseColor },
      emissiveIntensity: { value: 0.5 }
  },
  vertexShader: `
      varying vec2 vUv;
      void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
  `,
  fragmentShader: `
  uniform vec3 baseColor;
  uniform vec3 patternColor;
  uniform sampler2D patternTexture;
  uniform vec3 emissive;
  uniform float emissiveIntensity;
  varying vec2 vUv;

  // sRGB转线性空间
  vec3 srgbToLinear(vec3 color) {
      return pow(color, vec3(2.2));
  }

  // 线性空间转sRGB
  vec3 linearToSrgb(vec3 color) {
      return pow(color, vec3(1.0/2.2));
  }

  void main() {
    vec4 texelColor = texture2D(patternTexture, vUv);
    float alpha = texelColor.a;

    // 转换为线性空间计算颜色混合
    vec3 baseLinear = srgbToLinear(baseColor);
    vec3 patternLinear = srgbToLinear(patternColor);
    vec3 outputLinear = mix(baseLinear, patternLinear, alpha);

    // 添加与纯色模式一致的自发光效果
    vec3 emissiveLinear = srgbToLinear(emissive) * emissiveIntensity;
    outputLinear += emissiveLinear;

    // 转换回sRGB空间输出
    gl_FragColor = vec4(linearToSrgb(outputLinear), 1.0);
  }
`,
  side: THREE.DoubleSide,
  name: child.material.name,
});

关键修复点说明

  • 新增颜色空间转换函数,确保混合计算在线性空间进行,输出时转回sRGB,和Three.js默认材质行为一致。
  • 移除transparent: true,避免默认混合逻辑干扰手动颜色混合。
  • 添加emissive和emissiveIntensity uniforms,让ShaderMaterial的光照表现和MeshStandardMaterial对齐。

内容的提问来源于stack exchange,提问作者Mahi Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 19:37:15