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.
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)
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
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:
Shader "Unlit/BC4AlphaShader"
{
Properties
{
_Color("Main Color (With Alpha)", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent"}
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
ColorMask RGB
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv).rrrr; // R8/BC4 texture data is in RED channel, so fill all our color channels with .r
//fixed4 col = tex2D(_MainTex, i.uv).aaaa; // Alpha8 color is stored in Alpha channel, use this line instead if going to use Alpha8 format.
col.rgb *= _Color; //Multiply our RGB by our Tint color, changing our white into the color.
UNITY_APPLY_FOG(i.fogCoord, col); //Fog will only be calculated if you have it enabled in game
return col;
}
ENDCG
}
}
}
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.
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
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv).rrrr; //fill all color channels with the red channel
col.rgba *= _Color; //Multiply RGBA by the color (added A to allow fading it out with A of _Color)
col.rgb = _Color; //Set RGB back to the pure color value of _Color
return col;
}
You may notice the edges being brighter in that case. But to do that, simply do:
fixed4 col = _Color;
col.a *= tex2D(_MainTex, i.uv).r;
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 =)