Transparent surfaces blending wrong.

Hello everyone;

Consider the attached image of a bottle as rendered in unity with the shader below.
The white arrow and the checker grid are added via post processing to emphasize the problem here:
I want the plastic of the bottle to appear translucent, and the label to be solid. When the plastic overlaps the label, the label is also made translucent.

The shader code is based off of what is described at the end of this article: Unity - Manual: ShaderLab: legacy alpha testing

The image is being rendered to a texture, which is how I scraped it for this post.

I’m aware that there exists the Transparent/Diffuse shader, but areas containing non-one alpha chanels render black for some reason.

What I find curious, is that “Blend SrcAlpha OneMinusSrcAlpha” causes the same results in other cases that I’ve experimented with.

Any help would be much appreciated.

Thanks,
Lunpa

[edit]
It occurs to me in retrospect that the red/green/gray color scheme in the attached image might be hard to see for some. If so, let me know, and I’ll post a different version.
[/edit]

Shader "Custom/Transparent" {
	Properties {
		_MainTex ("Texture", 2D) = "white" { }
	}
	SubShader {
		Cull Off		
		Pass {
			AlphaTest Equal 1.0
			SetTexture [_MainTex] {}
		}
		Pass {
			ZWrite off
			ZTest Less
			AlphaTest Less 1.0
			Blend SrcAlpha OneMinusSrcAlpha
			SetTexture [_MainTex] {}
		}

	}
}

Neither this nor your results make any sense to me. I’d love for you to upload a .unitypackage so I could check it out.

However, I don’t see a reason why you’re using that shader. Why don’t you just use two submeshes with a different material on each?

Shader "Custom/Cull Off Texture" {
	
Properties {
	_MainTex ("Texture", 2D) = ""
}

SubShader {
	Cull Off
	Pass {
		SetTexture[_MainTex]
	}
}

}
Shader "Custom/Solid Transparent Color" {
	
Properties {
	_Color ("Color  (A = Opacity)", Color) = (1,1,1,.5)
}

SubShader {
	Tags {Queue = Transparent}
	Cull Off
	ZWrite Off
	Blend SrcAlpha OneMinusSrcAlpha
	Color [_Color]
	Pass {}
}

}

What you’re seeing is the alpha blended shader writing to the alpha channel, which causes whatever shader you are using to show your RenderTexture (probably the built-in GUI shader) to render the area semi-transparently as well. Semi-transparent shaders should always use “Colormask RGB” to prevent this.

I’d love to share the package, but this is for my work, and they generally are very fussy about that sort of thing. The bottle you see above I made to demonstrate the problem, because I can’t actually share what I’m working on.

Why am I using this method? It has been my experience (at least as an opengl programmer) that to render semi transparent objects in a scene, the “correct” way is to render all of your opaque stuff in one pass, and then in a second pass, render all of your translucent stuff ( with a specific Z order, don’t write to the depth buffer, etc ) in a second pass with alpha blending. This article outlines the theory behind using semi-transparent objects in opengl, if you’re wondering where I came up with this. This article explains how to do it in unity. Also, consider that the second article is in the official unity documentation, and I’m copying the method more or less verbatim.

Why don’t I “just use” two sub meshes? Well, I don’t see how that would solve anything, since your shader pair does more or less the same as mine, except you don’t control when they happen, and so I imagine that would produce more errors (since transparent things could draw before opaque things) without addressing at all the real problem (why are some things transparent when they shouldn’t be).

Also, "just use"ing two sub meshes means remaking a few thousand assets, which are otherwise fine. I’m pretty sure its my code that is at fault, not the assets.

I think I follow what you’re saying. Where should I invoke “Colormask RGB”? Putting it in the shader produces either a black image, or a black image with a magenta solid bottle, so that can’t be right =)

Also, the render texture itself is never rendered on screen. This project is for generating thumbnail images. The line which produces the output png is this:

File.WriteAllBytes(path, tex.EncodeToPNG());

Where “tex” is the render texture.

Your articles are not about the same thing. My shaders represent your first article, which is about rendering queues. The unity example is relevant to you, only if there is actually alpha testing necessary, which you did not demonstrate. All this two-pass stuff is better left to multiple materials, if the two passes are unrelated to each other, as yours are.

This is what I am trying to do. The two passes are totally related to one another. Now, the alpha test bit is important, because as I understand it, that is how you’d do this sort of thing in unity. Obviously, I’m not very experienced with unity, if my post count is any indicator.

Now, I appreciate the authority you have over these concepts, but I want to be very clear that remaking a few thousand assets is not an option.

Can you post a picture of the magenta outline? That’s not something I’ve seen before.

Colormask RGB should really be used for all shaders using alpha blending, because you rarely want to use the alpha for anything but blending. Note that if you don’t mask out the alpha channel, the standard blending function will still be applied, so the shader will write:

srcAlpha*srcAlpha + (1 - srcAlpha)*dstAlpha

Getting correct results in your situation is tricky, because you are trying to mix opaque and semi-transparent rendering with a semi-transparent rendering of the result (whether by RenderTexture or thumbnails). What you could do is:

  • Render to a transparent background R = G = B. Changing the brightness of the background will affect the brightness of transparent objects.
  • Render RGB of opaque objects with Z writing
  • Render RGBA of transparent objects with alpha blending and do not mask out the alpha
  • Render A of opaque objects, just as 1.0 everywhere

This will look Ok when you render the resulting texture using alpha blending. However, the color and alpha value of the transparent parts will be incorrect (generally too dark and too transparent).

Your actual passes may be related to each other, but you haven’t demonstrated that. Your label is an opaque texture. Your plastic is a solid color. There is no reason to use two passes relying on alpha testing, instead of using two separate submeshes. You don’t seem to have enough understanding of how any of this works, but if you can’t offer us any assets to improve, you’re going to need to ask smaller questions to improve your own understanding, so that you can preserve your secrecy an work with your presumably ill-created assets.

Start by using the appropriate rendering queue. The Unity doc uses it but you didn’t copy it over. All queues above 2500 sort back-to-front.

Here’s the shader that produces the weird magenta effect. I noticed it also produces a parse error =) whoops!

Shader "Custom/Alpha Blending" {
	Properties {
		_MainTex ("Texture", 2D) = "white" { }
	}
	SubShader {
		Cull Off
		
		Pass {
			AlphaTest Equal 1.0
			SetTexture [_MainTex] {}
		}
		
		Pass {
			ZWrite off
			ZTest Less
			AlphaTest Less 1.0
			Blend SrcAlpha OneMinusSrcAlpha
			SetTexture [_MainTex] {}
			Colormask RGB
		}
	}
}

I guess the order is important =) This produces something entirely different:
(see the other attached image)

Shader "Custom/Alpha Blending" {
	Properties {
		_MainTex ("Texture", 2D) = "white" { }
	}
	SubShader {
		Cull Off
		
		Pass {
			AlphaTest Equal 1.0
			SetTexture [_MainTex] {}
		}
		
		Pass {
			Colormask RGB
			ZWrite off
			ZTest Less
			AlphaTest Less 1.0
			Blend SrcAlpha OneMinusSrcAlpha
			SetTexture [_MainTex] {}
		}
	}
}

Close, but not quite there, since it loses transparency that doesn’t overlap anything. I added an extra pass, and now it renders perfectly!

Shader "Custom/Alpha Blending" {
	Properties {
		_MainTex ("Texture", 2D) = "white" { }
	}
	SubShader {
		Cull Off
		
		Pass {
			ZWrite off
			ZTest Less
			AlphaTest Less 1.0
			Blend SrcAlpha OneMinusSrcAlpha
			SetTexture [_MainTex] {}
		}
		
		Pass {
			AlphaTest Equal 1.0
			SetTexture [_MainTex] {}
		}
		
		Pass {
			Colormask RGB
			ZWrite off
			ZTest Less
			AlphaTest Less 1.0
			Blend SrcAlpha OneMinusSrcAlpha
			SetTexture [_MainTex] {}
		}
	}
}

Thank you soo much for the help, Daniel =)



Hooray for the ignore feature! ;-D

Hooray for flexible people eg not you Jessy.

There are over 8,000 assets that would have had to change. And also, your solution wouldn’t have solved the actual problem, because it ended up a quirk of how blending works, and me requiring an alpha channel in the buffer.

Your solution would have taken months to implement, probably would not have worked anyway, since it was about semantics and not the actual problem.

Also, the deadline for this project is like this week.

I think I’ll take the 20 minute practical fix.

Anyways, problem solved, so if you want the last word, go ahead and reply, but I’m not going to return to this thread.

That’s a pretty weird solution, but since the transparent parts of your bottle are a single, flat grey, it seems adequate for your needs. If anyone else finds this thread looking for a general solution to this problem, the order of operations I detailed above will give you more accurate results, but the issue is actually more complicated than any of the solutions in this thread.