I have a scene with a RenderTexture set as target to a Camera. There’s also a script attached to the camera, that exports the RenderTexture to a PNG.
Objects using the new standard shader are rendered properly, opaque or transparent.
However, when using a custom surface shader, as soon as my shader has “alpha” as surface shader parameter, the shader stops writing alpha values to the RenderTexture.
What can I do to make the shader render the alpha values to the RenderTexture? Is this a bug or some weird surface shader limitation? I want to avoid having to write all the shaders again as CG shaders and figuring out all the lighting and shadow stuff.
I attached my simple test project as a UnityPackage.
Everything looks fine to me, but there are two things you might want to check:
Make sure your camera has a clear color (unless you had something else in mind)
Discard fully transparent pixels from your shader*
What I mean by this is that even your fully transparent pixels with alpha working correctly will go on to the lighting stage unless you discard them. There’s a way to do it with shader properties (AlphaTest or something), but I do it with code in the shader. You can add something like this to your surface shader “surf”:
Actually, doesn’t seem to be related to RenderTexture. I tried using Texture2D.ReadPixels to read directly from the screen and the shader still doesn’t write alpha.
Have you added “keepalpha” ? The documentation mentions that only opaque shaders will default to writing 1 to the alpha value, but it might fix the issue here.
I believe the problem you are facing here is that for transparent surface shaders Unity automatically masks out the alpha channel using ‘ColorMask RGB’. This means the alpha channel/component is never rendered to in the screen buffer or rendertexture.
I’m unsure if you can fix this directly within surface shaders, but you can fix it by using pre-compiled versions of the shaders. If you need to use the default Unity shaders then download the ‘built-in shaders’ pack from Unity for the version you are using, then
Create or add shader to Unity Project.
Select shader.
In the inspector click on ‘Show Generated code’
In MonoDevelop when it opens the compiled shader, do select all and copy.
Create a new shader, give it an appropriate name.
Paste the compiled shader into this one, remember to make sure its using a unique shader name.
Search for ‘ColorMask RGB’ and replace with ‘ColorMask RGBA’, or actually you could probably just comment it out completely.
Profit!
Obviously its quite a bit of a pain, not sure if there is an easier way, but at least it only applies to transparent shaders. Be careful when copying compiled shader code that the shader is set up to generate all permutations and api versions you’ll need.
Yeah, but thought it would be worth a try regardless. I think Noisecrime’s got a very intuitive thought. I would think that’s quite likely to be the issue.
Having checked it would appear the ColorMask setting is solely due to legacy reasons and Unity’s old alpha-based glow technique. Hopefully this means it will be addressed in future release and removed or made optional.
I have the same problem. My custom shaders aren’t drawing to the alpha in rendertextures and my objects are invisible. Does writing pure CG shaders fix this?
Why doesn’t Unity provide a decent set of mobile shaders?!?! I don’t want frickin’ physically based uber-real objects in my mobile game. I want normal mapped sprites that can be used for efficient rendering using the full range of techniques. Years on from 2D mode and 2D is still an uphill struggle with problem after problem caused by lack of attention from Unity. It really wouldn’t take them long to put together a batch of mobile optimised shaders to match the capabilities of the past few years of devices.
Thanks for this post! I had the same problem in my old Unity 5.6.
My surface shader tend to skip alpha for my render texture. Although the camera rendered the object correctly to the editor small window || directly to screen. Adding alpha || alpha:fade || others options inside shader had incorrect rendering. So, i’ve tried the solution posted by by Noisecrime. It worked out. Indeed, Mask RGB to Mask RGBA is an elegant solution.