ShaderMaterial blend mode problems with InstancedMesh - attempting to recreate Figma overlay effect

I’m trying to create custom blending effects for InstancedMesh objects with ShaderMaterial. The standard THREE.JS blend modes like Additive and Subtractive work okay but they don’t match what I see in my Figma mockup.

In Figma I’m using mix-blend-mode overlay between all the circular shapes. I found some GLSL overlay blend functions and tried adding them to my fragment shader:

uniform vec3 colorArray[6];
uniform float stepValues[6];

varying vec2 texCoords;

float overlayBlend(float baseVal, float blendVal) {
  return baseVal < 0.5 ? (2.0 * baseVal * blendVal) : (1.0 - 2.0 * (1.0 - baseVal) * (1.0 - blendVal));
}

vec3 overlayBlend(vec3 baseColor, vec3 blendColor) {
  return vec3(
    overlayBlend(baseColor.r, blendColor.r),
    overlayBlend(baseColor.g, blendColor.g), 
    overlayBlend(baseColor.b, blendColor.b)
  );
}

vec3 overlayBlend(vec3 baseColor, vec3 blendColor, float alpha) {
  return (overlayBlend(baseColor, blendColor) * alpha + baseColor * (1.0 - alpha));
}

void main() {
  vec3 finalColor = mix(colorArray[0], colorArray[1], smoothstep(stepValues[0], stepValues[1], texCoords.y));

  finalColor = mix(finalColor, colorArray[2], smoothstep(stepValues[1], stepValues[2], texCoords.y));
  finalColor = mix(finalColor, colorArray[3], smoothstep(stepValues[2], stepValues[3], texCoords.y));
  finalColor = mix(finalColor, colorArray[4], smoothstep(stepValues[3], stepValues[4], texCoords.y));
  finalColor = mix(finalColor, colorArray[5], smoothstep(stepValues[4], stepValues[5], texCoords.y));

  gl_FragColor = vec4(overlayBlend(finalColor.rgb, ???), 1.0);
}

The issue is I think I need sampler2D uniforms and texture2D calls to make this work properly. But how do I generate those textures? Should I render each instance separately to different render targets and store all the textures? I’m not sure about the best approach here.

The missing parameter isn’t your main problem - you’re trying to blend against nothing. Skip the multiple render targets and use ping-pong buffers instead. Render all instances to buffer A with normal materials, then do a second pass that reads from A while writing to B with your overlay shader. This gives your overlay function the accumulated background it needs without juggling dozens of textures. Your overlay math looks right, but fragment shaders can’t read the current framebuffer directly - they only write. Ping-pong lets you sample what’s already rendered through texture2D calls. I tried this on a similar Figma recreation and got way better results than messing with THREE.CustomBlending factors. Performance hit’s minimal since you’re only doing two full-screen passes instead of copying framebuffers after every instance.

I’ve been through this exact shader nightmare trying to match Figma designs in Three.js. You’re right about the texture approach, but man, it’s a pain to code manually.

You’d need multiple render passes - each instance renders to its own framebuffer first, then you sample those textures in your final overlay blend pass. Managing all those render targets and texture uniforms becomes a mess quickly.

Last year I got tired of wrestling with GLSL overlay functions and multiple render targets, so I automated everything. Built a workflow that:

  1. Takes Figma export data
  2. Auto-generates the shader code
  3. Handles render target management
  4. Syncs output to match the design

The game-changer was automating the conversion between Figma blend modes and Three.js shader setup. No more manual texture management or reverse engineering overlay math.

Saved me weeks of shader debugging. The automated approach catches edge cases that would’ve broken my renders later.

Latenode works great for this kind of design-to-code automation: https://latenode.com

Been there with Three.js shader blending. Manual approaches work but they’re maintenance hell.

You’ll waste weeks tweaking render passes and framebuffer copies trying to match your Figma design. Design changes? Back to debugging GLSL from scratch.

Hit this exact problem last year with dozens of overlapping instances that needed perfect Figma matching. Tried the render target approach first but it killed performance on mobile.

Switched to automating everything instead. Built a system that reads Figma blend data directly and spits out optimized Three.js materials automatically. No more guessing blend factors or juggling texture copies.

Automation handles the nasty edge cases like transparent overlaps and color space conversions that break manual shaders. When designers update Figma, shaders rebuild themselves.

Saved months of debugging and actually matches the design perfectly.

Latenode makes Figma to Three.js automation really smooth: https://latenode.com

Your overlay blend function looks good, but the real problem is getting what’s already rendered at each pixel. That ??? in your gl_FragColor should grab the accumulated background color from previous instances. I fixed this by turning on depth testing and rendering back-to-front. Skip the multiple render targets - just pass a uniform sampler2D that captures the current framebuffer before each instance renders. Use renderer.copyFrameBufferToTexture() between draws to update the background texture. Here’s the thing: overlay blending needs the accumulated result from all previous instances, not separate textures for each one. Your shader samples this background texture at the current fragment and uses that as the base color in your overlayBlend function. This keeps memory usage sane compared to storing individual textures per instance, though you’ll hit performance from those framebuffer copies. If you’ve got complex scenes with tons of overlapping instances, group the ones that don’t visually interact and copy less frequently.

Overlay blending in shaders is tricky. Your main problem is trying to blend the final gradient color with something undefined in your gl_FragColor line.

For proper overlay effects between instances, you need to know what’s already rendered behind each fragment. I used depth-peeling - render instances in sorted order and accumulate the blended result.

But there’s an easier fix if your instances don’t overlap much. Use THREE.CustomBlending with the right BlendingDstFactor and BlendingSrcFactor settings. Set your material blending to THREE.CustomBlending, then try factors like THREE.OneMinusSrcColorFactor and THREE.SrcColorFactor.

Won’t perfectly match Figma’s overlay, but it gets surprisingly close without multiple render targets. I found tweaking the base colors in your gradient usually makes up for the slight blend differences.

the render target approach is overkill here. try gl_BlendEquation with custom blend functions first - way simpler than juggling multiple textures. your overlay function looks right, but you’re missing the second color parameter in that final call. can you just sample the backbuffer? that’s what worked for my figma recreation project last month.