Search Unity

Texture2D.ReadPixels loses quality when capturing transparent background

Discussion in 'Scripting' started by roykoma, Jan 15, 2019.

  1. roykoma

    roykoma

    Joined:
    Dec 9, 2016
    Posts:
    176
    Hey there,

    didn't know if this would fit into graphics or scripting, but thought scripting might fit better.

    When capturing the active RenderTexture with Texture2D.ReadPixels it seems to be losing quality when the "Clear Flag" field of the camera it is capturing is set to either solid color (with an alpha value of 0) or depth only.

    I am spawning thousands of sprites which i am constantly capturing and putting the result onto a texure to save cpu and ram. I now wanted to add transparency to that which resulted in said problem.

    How it looks like with a solid background color (you can see there is no quality-loss):


    How it looks like with a transparent background color (you can see a huge quality-loss):


    This is basically all the code that is used to capture the content and put it on the texture:

    Code (CSharp):
    1. Texture2D tex = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false, true);
    2. tex.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
    3. tex.Apply();    
    4. renderedTexture = tex;
    Does anyone have any idea why this is happening, what I am doing wrong (if I am), or a better way to do this?

    Greetings
    Roy
     
  2. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Quick check, does the material have max alpha value?
     
  3. sskenth

    sskenth

    Joined:
    Sep 23, 2016
    Posts:
    54

    Hi, did you find a solution or work around found for this?
    I'm having the exact same issue...

    My material is Unlit/Transparent so theres no way to change alpha.

    FYI if I change it to a standard shader and play around with the alpha it still has no effect.
     
  4. roykoma

    roykoma

    Joined:
    Dec 9, 2016
    Posts:
    176
    Sadly I did not find a solution or a work around. I had to scrap the idea of putting the result onto a texture until the moment I save the result, to only take one, lossless screenshot.

    If you are also using it to save brushes or stuff like this I guess you could probably write the result onto the texture yourself instead of capturing it (reading the pixels directly from the image you use as brush), but this would be considerately more work, cpu usage and complicated (as you would have to do that every single brush instance placed and from this calculate the new pixel colors at it's pixel positions in the end-result). I sadly don't have the time nor the knowledge about color mulitplication/addition to help you with this out of my head though - sorry.

    But I ended up just exchanging the saved ram for saved headache as this was nothing I needed in production, but only for myself.

    Greetings
    Roy
     
    Last edited: Oct 19, 2019
    sskenth likes this.
  5. sskenth

    sskenth

    Joined:
    Sep 23, 2016
    Posts:
    54
    Ahh thats a shame, I'll keep looking.

    Thank you so much for the quick reply, really appreciate you sharing your experience, at least I know I'm not crazy for struggling with this ahah

    Kind regards

    -Sat
     
  6. sskenth

    sskenth

    Joined:
    Sep 23, 2016
    Posts:
    54

    So I think I've solved it, its not perfect because I'm using a custom shader and honestly I dont know enough about shaders to understand exactly why this works.
    But it seems like the normal shaders remove alpha/or add alpha on each render...(still dont know why its happening)

    ANYWAY

    I use a custom unlit transparent shader and have it on the textures. No more adding alpha.

    You can see the code below along with random links I used to piece this together.

    Code (CSharp):
    1. Shader "Unlit/UnlitAlpha"
    2. {
    3. Properties {
    4.     _Color ("Main Color (A=Opacity)", Color) = (1,1,1,1)
    5.     _MainTex ("Base (A=Opacity)", 2D) = ""
    6.     [HideInInspector] _SrcBlend ("__src", Float) = 1.0
    7. [HideInInspector] _DstBlend ("__dst", Float) = 0.0
    8. [HideInInspector] _ZWrite ("__zw", Float) = 1.0
    9. }
    10. Category {
    11.     Tags {"Queue"="Transparent" "IgnoreProjector"="True"}
    12.     ZWrite Off
    13.     //Blend SrcAlpha OneMinusSrcAlpha
    14.     Blend [_SrcBlend] [_DstBlend]
    15. ZWrite [_ZWrite]
    16.     SubShader {
    17.         Pass {
    18.             GLSLPROGRAM
    19.             #ifdef VERTEX
    20.             varying mediump vec2 uv;
    21.             uniform mediump vec4 _MainTex_ST;
    22.             void main() {
    23.                 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    24.                 uv = gl_MultiTexCoord0.xy * _MainTex_ST.xy + _MainTex_ST.zw;
    25.             }
    26.             #endif
    27.        
    28.             #ifdef FRAGMENT
    29.             varying mediump vec2 uv;
    30.             uniform lowp sampler2D _MainTex;
    31.             uniform lowp vec4 _Color;
    32.             void main() {
    33.                 gl_FragColor = texture2D(_MainTex, uv) * _Color;
    34.             }
    35.             #endif  
    36.             ENDGLSL
    37.         }
    38.     }
    39.     SubShader {Pass {
    40.         SetTexture[_MainTex] {Combine texture * constant ConstantColor[_Color]}
    41.     }}
    42. }
    43. }
    Here are some of the random links...

    Original unlitTransparent shader code.
    Additional information about shader stuff that I dont understand ahaha
    Some lines of code I added into the original shader to get the final shader above.

    Hope this help you or anyone else in the future, if anyone is smart enough to explain how this works or can add more value I'd love to hear your thoughts.

    Thank you

    -Sat