Search Unity

Need help with shader (unlit, solid color, alpha8 texture)

Discussion in 'Shaders' started by Mornedil, Jan 25, 2020.

  1. Mornedil

    Mornedil

    Joined:
    Feb 7, 2016
    Posts:
    22
    i'm trying to create an unlit shader that takes a color value (RGBA), and a grayscale texture as an alpha mask.
    I also want it to be as cheap as possible since I want it working well on mobile platforms.
    But, I have no experience writing shaders so I've come here for help.

    This is what I've come up with so far:

    Code (csharp):
    1. Shader "Custom/UnlitColorAlphamask" {
    2.     Properties {
    3.         _Color ("Main Color (With Alpha)", Color) = (1,1,1,1)
    4.         _MainTex ("Base (RGB)", 2D) = "white" {}
    5.     }
    6.     SubShader {
    7.         Tags { "RenderType" = "Transparent" "Queue" = "Transparent"}
    8.  
    9.         ZWrite Off
    10.  
    11.         Blend SrcAlpha OneMinusSrcAlpha
    12.         ColorMask RGB
    13.  
    14.         Pass {
    15.             Lighting Off
    16.             SetTexture[_MainTex] {
    17.                 constantColor [_Color]
    18.                 Combine texture * constant DOUBLE
    19.             }
    20.         }
    21.     }
    22. }
    23.  
    It works almost like I want it, but currently it uses an RGB texture rather than an Alpha8 texture (which to me seems like a waste of file space since I only need an alpha value)
    https://www.dropbox.com/s/jxcrqo0o39oume3/shader.png?dl=0

    If I change the compression to Alpha8 however, the color turns completely black (but with working alpha).
    How can I modify this shader to take an Alpha8 compressed image as an input without that affecting it's color?


    Edit:
    Since I have no experience writing shaders I'm also wondering how well this would work on mobile platforms
     
    Last edited: Jan 25, 2020
  2. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,550
    Alpha 8 only stores the color information in the alpha channel. But that fixed function shader code isn't handling that case.

    Also those FixedFunction shaders aren't actually FixedFunction anymore but are converted to vert/frag under the hood and thus won't give you a performance boost. In fact most mobile phones these days don't have the hardware for fixed function shaders. Here is how it will be with in vert/frag form:
    Code (CSharp):
    1. Shader "Unlit/BC4AlphaShader"
    2. {
    3.     Properties
    4.     {
    5.         _Color("Main Color (With Alpha)", Color) = (1,1,1,1)
    6.         _MainTex ("Base (RGB)", 2D) = "white" {}
    7.     }
    8.     SubShader
    9.     {
    10.         Tags { "RenderType"="Transparent" "Queue"="Transparent"}
    11.         LOD 100
    12.  
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.         ColorMask RGB
    15.  
    16.         Pass
    17.         {
    18.             CGPROGRAM
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.             #pragma multi_compile_fog
    22.  
    23.             #include "UnityCG.cginc"
    24.  
    25.             struct appdata
    26.             {
    27.                 float4 vertex : POSITION;
    28.                 float2 uv : TEXCOORD0;
    29.             };
    30.  
    31.             struct v2f
    32.             {
    33.                 float2 uv : TEXCOORD0;
    34.                 UNITY_FOG_COORDS(1)
    35.                 float4 vertex : SV_POSITION;
    36.             };
    37.  
    38.             sampler2D _MainTex;
    39.             float4 _MainTex_ST;
    40.             fixed4 _Color;
    41.  
    42.             v2f vert (appdata v)
    43.             {
    44.                 v2f o;
    45.                 o.vertex = UnityObjectToClipPos(v.vertex);
    46.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    47.                 UNITY_TRANSFER_FOG(o,o.vertex);
    48.                 return o;
    49.             }
    50.  
    51.             fixed4 frag (v2f i) : SV_Target
    52.             {
    53.        
    54.                 fixed4 col = tex2D(_MainTex, i.uv).rrrr; // R8/BC4 texture data is in RED channel, so fill all our color channels with .r
    55.                 //fixed4 col = tex2D(_MainTex, i.uv).aaaa; // Alpha8 color is stored in Alpha channel, use this line instead if going to use Alpha8 format.
    56.                 col.rgb *= _Color; //Multiply our RGB by our Tint color, changing our white into the color.
    57.                 UNITY_APPLY_FOG(i.fogCoord, col); //Fog will only be calculated if you have it enabled in game
    58.  
    59.                 return col;
    60.             }
    61.             ENDCG
    62.         }
    63.     }
    64. }
    Alpha8 is a high quality uncompressed format though. I would recommend clicking on your texture, setting its "Texture Type" to "Single Channel", and then select "Channel" as "Red" and have your mask texture be stored in your texture's red channel. Then you can simply use the Compression quality settings on the "Default" tab at the bottom of the Texture Import Settings to let Unity decide the best single-channel format for a given platform and compression quality. "Crunch Compression" at a quality level of 50 tends to be pretty good for something like this.

    Keep in mind, having just one texture channel to sample isn't going to give you any significant performance advantage as the GPUs texture sampling units are built to sample all 4 channels at once, so it's just a minuscule bit of data transfer time to those units you're saving on. Mostly the savings you'll get from this are GPU/CPU memory space savings, which are indeed quite important on mobile especially. And the time it takes to load these textures is lower too.
     
    Last edited: Jan 25, 2020
    neonblitzer and Mornedil like this.
  3. Mornedil

    Mornedil

    Joined:
    Feb 7, 2016
    Posts:
    22
    Thanks! I learned a lot from reading your post and the link about FixedFunction shaders.

    The shader works pretty well, but I noticed that since the R channel is repeated for all of the RGBA channels, the RGB channels are darker in the areas where the alpha is lower. (i had this issue with my original shader too)

    How can I make it so it uses the RGB value only from _Color, and not have _MainTex affect RGB at all?

    This is what I could come up with, but I feel like I might be using an unnecessary amount of code since I'm first writing all the channels with rrrr from _MainTex and then replacing them with rgb from _Color
    Code (csharp):
    1. fixed4 frag (v2f i) : SV_Target
    2.             {
    3.                 fixed4 col = tex2D(_MainTex, i.uv).rrrr; //fill all color channels with the red channel
    4.                 col.rgba *= _Color; //Multiply RGBA by the color (added A to allow fading it out with A of _Color)
    5.                 col.rgb = _Color; //Set RGB back to the pure color value of _Color
    6.                 return col;
    7.             }
     
    Invertex likes this.
  4. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,550
    You may notice the edges being brighter in that case. But to do that, simply do:
    Code (CSharp):
    1. fixed4 col = _Color;
    2. col.a *= tex2D(_MainTex, i.uv).r;
    3. return col;
    This way you're just outputting a pure color and letting the shader blend it with existing buffer color based on the alpha value, which will be your mask. (We multiply so that you can still have additional overall transparency control from the _Color swatch's Alpha value as well if ever wanted, otherwise could just do col.a =)
     
    Mornedil likes this.
  5. Mornedil

    Mornedil

    Joined:
    Feb 7, 2016
    Posts:
    22

    Thanks! This is exactly what I was looking for