I need the contrast and saturation to apply for model not for entire canvas
{glbUrl && (
<ErrorBoundary onError={handleModelError}>
<Model
key={modelKey}
url={glbUrl}
position={position}
rotation={rotation}
opacity={opacity}
selectedScreen={selectedScreen}
setAvailableScreens={setAvailableScreens}
customScreenImage={customScreenImage}
setScrubTime={setScrubTime}
screenRefs={screenRefs}
scrubTime={scrubTime}
contrast={contrast}
saturation={saturation}
directionalIntensity={directionalIntensity}
enableHDR={enableHDR}
hdrIntensity={hdrIntensity}
metalness={metalness}
vignetteOffset={vignetteOffset}
vignetteDarkness={vignetteDarkness}
/>
</ErrorBoundary>
)}
// Apply contrast and saturation to all materials in the scene
useEffect(() => {
if (ref.current) {
console.log(
`🎨 Applying contrast ${contrast}, saturation ${saturation}, vignetteOffset ${vignetteOffset}, and vignetteDarkness ${vignetteDarkness} to all materials`,
);
try {
ref.current.traverse((child) => {
if (child && child.isMesh && child.material) {
// Handle single material or material array
const materials = Array.isArray(child.material)
? child.material
: [child.material];
materials.forEach((material) => {
if (material && material.isMaterial) {
// If the material doesn't have our custom uniforms yet, set them up
if (!material.userData.hasContrastSaturation) {
// Create uniforms for contrast, saturation, vignetteOffset, and vignetteDarkness
material.userData.contrastUniform = { value: contrast * 2.0 };
material.userData.saturationUniform = {
value: saturation * 2.0,
};
material.userData.vignetteOffsetUniform = {
value: vignetteOffset,
};
material.userData.vignetteDarknessUniform = {
value: vignetteDarkness,
};
// Store original onBeforeCompile if it exists
const originalOnBeforeCompile = material.onBeforeCompile;
// Add our custom shader modification
material.onBeforeCompile = (shader) => {
// Call original onBeforeCompile if it exists
if (originalOnBeforeCompile) {
originalOnBeforeCompile(shader);
}
// Add our uniforms to the shader
shader.uniforms.contrastValue =
material.userData.contrastUniform;
shader.uniforms.saturationValue =
material.userData.saturationUniform;
shader.uniforms.vignetteOffset =
material.userData.vignetteOffsetUniform;
shader.uniforms.vignetteDarkness =
material.userData.vignetteDarknessUniform;
// Add shader code for contrast, saturation, and vignette
shader.fragmentShader = shader.fragmentShader.replace(
"#include <output_fragment>",
`
#include <output_fragment>
// BEGIN_CONTRAST_SATURATION
uniform float contrastValue;
uniform float saturationValue;
uniform float vignetteOffset;
uniform float vignetteDarkness;
// Apply contrast
vec3 midpoint = vec3(0.5);
gl_FragColor.rgb = (gl_FragColor.rgb - midpoint) * contrastValue + midpoint;
gl_FragColor.rgb = clamp(gl_FragColor.rgb, 0.0, 1.0);
// Apply saturation
vec3 gray = vec3(dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114)));
gl_FragColor.rgb = mix(gray, gl_FragColor.rgb, saturationValue);
gl_FragColor.rgb = clamp(gl_FragColor.rgb, 0.0, 1.0);
// Apply vignette effect
vec2 center = vec2(0.5, 0.5);
vec2 position = gl_FragCoord.xy / vec2(1920.0, 1080.0); // Approximate screen size
float dist = distance(position, center);
float vignette = smoothstep(vignetteOffset, 0.7, dist);
gl_FragColor.rgb = mix(gl_FragColor.rgb, gl_FragColor.rgb * (1.0 - vignetteDarkness), vignette);
// END_CONTRAST_SATURATION
`,
);
};
// Mark this material as having our contrast/saturation modifications
material.userData.hasContrastSaturation = true;
material.needsUpdate = true;
} else {
// Just update the uniform values
if (material.userData.contrastUniform) {
material.userData.contrastUniform.value = contrast * 2.0;
}
if (material.userData.saturationUniform) {
material.userData.saturationUniform.value =
saturation * 2.0;
}
if (material.userData.vignetteOffsetUniform) {
material.userData.vignetteOffsetUniform.value =
vignetteOffset;
}
if (material.userData.vignetteDarknessUniform) {
material.userData.vignetteDarknessUniform.value =
vignetteDarkness;
}
}
}
});
}
});
console.log(
"✅ Applied contrast, saturation, and vignette to all materials",
);
} catch (error) {
console.error("Error applying effects:", error);
}
}
}, [contrast, saturation, vignetteOffset, vignetteDarkness]);
// Update all material uniforms when contrast, saturation, or vignette changes
useEffect(() => {
console.log(
`🔄 Updating all material uniforms - contrast: ${contrast}, saturation: ${saturation}, vignette: ${vignetteOffset}/${vignetteDarkness}`,
);
// Helper to update uniforms on a material
const updateMaterialUniforms = (material) => {
if (material && material.userData) {
// Update contrast
if (material.userData.contrastUniform) {
material.userData.contrastUniform.value = contrast * 2.0;
}
// Update saturation
if (material.userData.saturationUniform) {
material.userData.saturationUniform.value = saturation * 2.0;
}
// Update vignette
if (material.userData.vignetteOffsetUniform) {
material.userData.vignetteOffsetUniform.value = vignetteOffset;
}
if (material.userData.vignetteDarknessUniform) {
material.userData.vignetteDarknessUniform.value = vignetteDarkness;
}
// Force material update
material.needsUpdate = true;
}
};
// Update all materials in the scene
if (ref.current) {
ref.current.traverse((child) => {
if (child && child.isMesh && child.material) {
// Handle single material or material array
const materials = Array.isArray(child.material)
? child.material
: [child.material];
materials.forEach(updateMaterialUniforms);
}
});
}
// Update screen materials
if (screenRefs.current) {
Object.values(screenRefs.current).forEach((mesh) => {
if (mesh && mesh.material) {
updateMaterialUniforms(mesh.material);
}
});
}
}, [contrast, saturation, vignetteOffset, vignetteDarkness]);