I'm building a custom racing silks customizer using THREE.js. My app allows users to: By the way it is something like this website here: https://hylandsportswear.com/kitbuilder/#/customise/80325870?basketIndex=1 my one is just a 2d model and uses a color picker.
Select a base color. Apply a pattern (e.g., stripes, chequered, heart). Adjust the pattern's color. Adjust the base color once pattern is also applied color changes are done using a color picker However, I'm facing an issue where the base color appears to change when a pattern is applied.
The Problem Expected Behavior: The base color remains exactly as selected (e.g., #ff3370), and the pattern is simply overlaid on top of it. Actual Behavior: When a pattern is applied, the visible base color shifts (e.g., from pink to red; for some reason it always goes darker than the intended color but eh primary colors dont have the same issue), although the hex code remains unchanged. When switching back to "solid," the base color is displayed correctly.
my setup includes this: Base Color: User-selected via an HTML . Pattern: A transparent PNG (e.g., a black heart with a transparent background). (used gimp to create it). ShaderMaterial: Used to overlay the pattern on the base color.
I have tried adjusting the fragment shader and vertexshader, i have also made sure my pattern is not the problem. It has a fully transparent background (0 opacity) and then the pattern has (1 opacity and is in the black color).
this is my code for displaying the pattern on top of the base.
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;
}),
#include <colorspace_fragment>after assigning gl_FragColor in your fragment shader, do you see a better result?