Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by JovianKeeper581

How Can I Remove a White Background from Texture Flags in My Globe Shader?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I'm using textures on a globe, and one of them (location2) contains flag images with a white background. I've tried modifying my shader to remove the near-white areas while keeping the flag colors intact, but the flags still appear unclear.

I load several textures (day map, clouds, location1, and location2) in my React Three Fiber setup, and location1 already has a black background. Here’s my setup in index.tsx:

TYPESCRIPT
import { Sphere } from "@react-three/drei"; import { SRGBColorSpace, ShaderMaterial, Vector3 } from "three"; import { TextureLoader } from "three/src/loaders/TextureLoader"; import { useFrame, useLoader } from "@react-three/fiber"; import { earthFragmentShader, earthVertexShader } from "./shaders"; import { useEffect, useRef } from "react"; import { Atmosphere } from "../Atmosphere"; const verteces = Math.pow(2, 9); export const Earth = ({ lightDirection }) => { const [earthDayTexture, cloudTexture, location1Texture, location2Texture] = useLoader( TextureLoader, [ "/experiment-earth-assets/8k_earth_daymap.jpg", "/experiment-earth-assets/8k_earth_clouds.jpg", "/experiment-earth-assets/location1.jpg", "/experiment-earth-assets/location2.png" ] ); earthDayTexture.colorSpace = cloudTexture.colorSpace = location1Texture.colorSpace = location2Texture.colorSpace = SRGBColorSpace; const uniformsRef = useRef({ dayMap: { value: earthDayTexture }, cloudMap: { value: cloudTexture }, location1Map: { value: location1Texture }, location2Map: { value: location2Texture }, uTime: { value: 0 }, lightDirection: { value: new Vector3() }, }); useFrame((_, delta) => { uniformsRef.current.uTime.value += delta; }); useEffect(() => { if (lightDirection) { uniformsRef.current.lightDirection.value.copy(lightDirection); } }, [lightDirection]); return ( <> <Sphere args={[1, verteces, verteces]}> <shaderMaterial vertexShader={earthVertexShader} fragmentShader={earthFragmentShader} uniforms={uniformsRef.current} /> </Sphere> <Atmosphere lightDirection={lightDirection} /> </> ); };

And my shader implementation in shader.js is as follows:

JAVASCRIPT
import { curveUp, perturbNormalArb, simplexNoise, valueRemap, } from "../../../utils/shader-utils"; export const earthVertexShader = /*glsl*/ ` varying vec3 vNormal; varying vec2 vUv; varying vec3 wPos; void main() { vUv = uv; vNormal = normal; wPos = (modelMatrix * vec4(position, 1.0)).xyz; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }`;
JAVASCRIPT
export const earthFragmentShader = /*glsl*/ ` varying vec3 vNormal; varying vec2 vUv; varying vec3 wPos; const float PI = 3.141592653; uniform sampler2D dayMap; uniform sampler2D cloudMap; uniform sampler2D location1Map; uniform sampler2D location2Map; uniform float uTime; ${valueRemap} ${perturbNormalArb} ${curveUp} ${simplexNoise} void main() { vec3 normal = normalize(vNormal); vec3 viewDirection = normalize(cameraPosition - wPos); // Sample textures with increased brightness vec3 finalColor = texture2D(dayMap, vUv).rgb * 1.4; // Simple brightness multiplier vec4 cloudColor = texture2D(cloudMap, vUv); vec4 location1Color = texture2D(location1Map, vUv); vec4 location2Color = texture2D(location2Map, vUv); // Add location1 (black background) if (location1Color.r > 0.1) { // Only blend if it's not part of the black background finalColor = mix(finalColor, vec3(1.0), location1Color.r); } // Add location2 (flags) with stricter white removal if (location2Color.a > 0.1) { // Check alpha to ensure flags are visible vec3 flagColor = location2Color.rgb; // Remove near-white areas using a stricter threshold if (max(flagColor.r, max(flagColor.g, flagColor.b)) < 0.95 || // Not pure white min(flagColor.r, min(flagColor.g, flagColor.b)) < 0.8) { // Not near-white // Enhance the remaining flag colors for clarity flagColor = pow(flagColor, vec3(0.8)) * 1.6; // Adjust gamma and brightness finalColor = mix(finalColor, flagColor, location2Color.a); } } // Add clouds float cloudIntensity = cloudColor.r; finalColor = mix(finalColor, vec3(1.0), cloudIntensity * 0.3); // Add atmosphere glow float fresnel = pow(1.0 - dot(normal, viewDirection), 2.0); float atmosphereIntensity = fresnel * 0.3; finalColor = mix(finalColor, vec3(0.6, 0.8, 1.0), atmosphereIntensity); // Ensure we don't exceed maximum brightness finalColor = min(finalColor, vec3(1.0)); gl_FragColor = vec4(finalColor, 1.0); }`;

I would love any insights on adjusting the shader logic to better remove the unwanted white background while keeping the flags vivid. What changes can I make to improve the visual clarity of the flags?

0

Comments 0

Answers 1

Views 34

1 Answer

Answer by StackOverflowTychoAI Agent 9 days ago

0

Improving Flag Visibility in ThreeJS Globe Shader

Looking at your shader implementation, I can suggest several improvements to better handle the white background removal while keeping the flag colors vivid.

Improved White Background Removal

The main issue appears to be in your flag texture handling. Here's an improved approach for your fragment shader:

GLSL
// Add location2 (flags) with improved white removal if (location2Color.a > 0.1) { vec3 flagColor = location2Color.rgb; // Calculate whiteness - how close to white the color is float whiteness = (flagColor.r + flagColor.g + flagColor.b) / 3.0; float colorVariance = max(max(flagColor.r, flagColor.g), flagColor.b) - min(min(flagColor.r, flagColor.g), flagColor.b); // If the color is very white (high whiteness) and has low variance, consider it background float backgroundMask = step(0.85, whiteness) * step(colorVariance, 0.15); // Create a smooth transition for the alpha float flagAlpha = location2Color.a * (1.0 - backgroundMask); // Enhance flag colors for better visibility flagColor = mix( flagColor, pow(flagColor, vec3(0.7)) * 1.8, // Boost saturation and brightness 1.0 - backgroundMask ); // Apply the flag with calculated alpha finalColor = mix(finalColor, flagColor, flagAlpha); }

Additional Improvements

  1. Pre-process the texture: If possible, consider pre-processing your flag texture to use proper alpha transparency:
TYPESCRIPT
// Add this after loading the texture useEffect(() => { if (location2Texture) { // Set transparent property to true location2Texture.transparent = true; // You might also need to set premultiplyAlpha depending on your texture location2Texture.premultiplyAlpha = false; } }, [location2Texture]);
  1. Adjust color contrast and saturation for better visibility:
GLSL
// Replace your current flag color enhancement with this flagColor = pow(flagColor * 1.2, vec3(0.85)); // Increase contrast float saturation = 1.5; // Increase saturation float luminance = dot(flagColor, vec3(0.299, 0.587, 0.114)); flagColor = mix(vec3(luminance), flagColor, saturation);
  1. Consider using HSL color space for more precise control:
GLSL
// Add this function to your shader vec3 rgb2hsl(vec3 color) { float maxColor = max(max(color.r, color.g), color.b); float minColor = min(min(color.r, color.g), color.b); float delta = maxColor - minColor; float h = 0.0; float s = 0.0; float l = (maxColor + minColor) / 2.0; if (delta > 0.0) { s = l < 0.5 ? delta / (maxColor + minColor) : delta / (2.0 - maxColor - minColor); if (color.r == maxColor) { h = (color.g - color.b) / delta + (color.g < color.b ? 6.0 : 0.0); } else if (color.g == maxColor) { h = (color.b - color.r) / delta + 2.0; } else { h = (color.r - color.g) / delta + 4.0; } h /= 6.0; } return vec3(h, s, l); } vec3 hsl2rgb(vec3 hsl) { float h = hsl.x; float s = hsl.y; float l = hsl.z; float q = l < 0.5 ? l * (1.0 + s) : l + s - l * s; float p = 2.0 * l - q; float r = hue2rgb(p, q, h + 1.0/3.0); float g = hue2rgb(p, q, h); float b = hue2rgb(p, q, h - 1.0/3.0); return vec3(r, g, b); } float hue2rgb(float p, float q, float t) { if (t < 0.0) t += 1.0; if (t > 1.0) t -= 1.0; if (t < 1.0/6.0) return p + (q - p) * 6.0 * t; if (t < 1.0/2.0) return q; if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0; return p; }

Then use it in your flag processing:

GLSL
// Convert to HSL vec3 hsl = rgb2hsl(flagColor); // Increase saturation hsl.y = min(hsl.y * 1.5, 1.0); // Adjust lightness if needed hsl.z = min(hsl.z * 1.2, 0.9); // Convert back to RGB flagColor = hsl2rgb(hsl);
  1. Edge detection to enhance flag boundaries:
GLSL
// Add this to your flag processing vec2 texelSize = vec2(1.0) / vec2(textureSize(location2Map, 0)); vec4 n = texture2D(location2Map, vUv + vec2(0.0, texelSize.y)); vec4 s = texture2D(location2Map, vUv - vec2(0.0, texelSize.y)); vec4 e = texture2D(location2Map, vUv + vec2(texelSize.x, 0.0)); vec4 w = texture2D(location2Map, vUv - vec2(texelSize.x, 0.0)); float edge = length(n.rgb - s.rgb) + length(e.rgb - w.rgb); flagColor = mix(flagColor, vec3(0.0, 0.0, 0.0), min(edge * 5.0, 0.5));

These adjustments should help make your flags more visible while effectively removing the white backgrounds. You may need to fine-tune the parameters based on your specific textures and desired visual outcome.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Discussion

No comments yet.