Display out-of-bounds UV as transparent?

Here I have the UV mab for an .obj file:


In three.js:
image

As you can see, this out-of-bounds faces are colored as black. How can I instead have them become transparent? (It is not an option to put them in-bounds with a transparent area of the texture)

UV maps have no in-bounds or out-of-bounds areas. Every pixel of them could be (and will be) used as a texture when some UV coordinates point to them.

If you do not want to use alpha maps (as this is the default way to have texture-base transparency), you might consider customizing the fragment shader to discard fragments (pixels) that fall in the out-of-bounds area. For this, you also have to implement a way for the shader to distinguish what is in and what it out either by filtering the UV coordinates, or by filtering the color from the texture.

I might have misunderstood your question. If this is the case, I’m sorry.

Maybe need bleeding, dilation, tearing.

1 Like

The default behavior of Three.js is to pick whatever color is in the image, and use that. So, to have custom behavior, I believe that the only options are to

  • make a custom material with a custom shader (see RawShaderMaterial and ShaderMaterial)
  • inject shader code into an existing WebGL material’s code (all materials have onBeforeCompile, a callback in which you can modify the shader string before it will be compiled into the GPU program)
  • compose TSL nodes together with custom logic for WebGPU (WebGPURenderer and its shader node system is highly undocumented, you’ll have to start with the TSL example, then look at examples and source code to see how APIs like wgslFn are used for providing WebGPU WGSL shader code to Three.js)

In all three cases, what you will need for the bit of custom logic is to change the fragment output color (gl_FragColor or out variable) based on some logic. Pseudo code:

if (gl_FragColor == black) {
  gl_FragColor  = vec4(0, 0, 0, 0); // zero alpha (transparent) or whatever color you want instead
  // or
  discard; // don't draw the pixel
} else {
  gl_FragColor = ... same code as before ...
}

In my case, I want to discard anything outside the image bounds (anything beyond (0,1) for either U and V. Note that in the OP anything inside the UV (any values less than 1) are not out of bounds of the image. In the OP case, there isn’t anything special about picking the color black (it is just a color) so special meaning has to be given to that (f.e. with custom injected shader code). So in my case, I might do this:

if (uv.x > 1 || uv.y > 1) {
  discard; // don't draw the pixel
} else {
  gl_FragColor = ... same code as before ...
}

(This is how answers can be written more helpfully, which I hope more people can do. Sorry that you didn’t get this until 2 years later @h_hh!)

Maybe

if(max(abs(uv.x - 0.5), abs(uv.y - 0.5)) > 0.5) discard;

:thinking:

or

if(uv.x < 0 || uv.y < 0 || uv.x > 1 || uv.y > 1) discard;

or

if(min(uv.x, uv.y) < 0 || max(uv.x, uv.y) > 1) discard;

I would go with this one:

The more comparison operations in a line, the better :flexed_biceps:

1 Like

Expanding on the suggestion from @Chaser_Code – note that mipmaps (if enabled) need to be considered. When viewed at a distance or at an angle, three.js samples from a lower-resolution copy of the image. With UVs right at the edge of the colored area, and no padding/dilation in the texture, you might be sampling from the intended colors mixed with black.

1 Like