Surface shader with vertex color alpha blending?

I’m very new at shaders, so I’d appreciate some help.

I prefer to hard-code my shaders rather than use ShaderGraph and the like, so yeah.

I have my shader mostly working the way I want. But I have an issue with vertex colors, their alpha values, to be exact. I am coding a shader that blends the materials of two overlapping meshes, and the one at the top should have a faded material via vertex alpha to get a “blending” grass effect. Let me illustrate.

Anything I try just results in the affected mesh being a brighter hue that ignores all my other effects such as bump mapping etc, and not a properly blended effect.

Some world textures also support texture alpha AND vertex alpha.

Can you post your shader code? There’s not much anyone can do to help without seeing that, apart from tell you it is entirely possible to do what you’re trying to do.

1 Like

My code is a bit of a mess, but here.

/* SUPER SMASH BROS. FEUD - COMMON FIGHTER SHADER
* Is able to transition from default materials to metal or inked materials, and back. Masking transition effect adapted from code by SMG (ScandinavianMountainGoat), co-creator of open-source
* SSB Ultimate render library CrossMod. Written 2018-2019 by Ploaj and SMG, implemented in Unity surface shader Cg/HLSL format by Tabuu Forte Akugun. It supports composite PRM maps
*(Metallic/Roughness/Ambient Occlusion/Specularity) and composite NOR maps complete with generated Z+, noise maps and cavity textures.
*/

Shader "Super Smash Bros. Feud/Pseudo-HBSS Shader (For Stage Materials)"
{
    Properties
    {
        [Toggle(USE_VERT_RGB)] _UseVertRGB("Use Vertex Colors?", Float) = 0
        [Toggle(USE_VERT_ALPHA)]_VertAlpha("Use vertex color alpha?", Float) = 0
        _Color("Main Color", Color) = (1.0,1.0,1.0,1.0)
        _RimLightColor("Rim light color", Color) = (0.6,0.6,0.6,1.0)
        _MainTex("Base Color Map (RGB+A)", 2D) = "white" {}
        [Toggle(USE_TEX_ALPHA)]_TexAlpha("Use *.col  alpha?", Float) = 0
        [NoScaleOffset]_NormalMap("Normal Map (*_nor)", 2D) = "bump" {}
        [Toggle(USE_PRM_MAP)]_UsePrm("Use a PRM Map?", Float) = 0
        [NoScaleOffset]_PRMMap("PBR (PRM) Combined Map (*_prm)", 2D) = "white" {}
        [Toggle(SKIN_MAT)]_SkinMat("Skin Map with SSS?", Float) = 0
        _CustomVector30("Subsurface effect intensity", Vector) = (0,0,0,0)
        _CustomVector11("Subsurface Scattering color for skin", Color) = (1,1,1,1)
        [HideInInspector]_DefaultVertClr("", Color) = (0.5,0.5,0.5,1)

        [Toggle(USE_EMISSIVE)]_EmissionMap("Check if using Emissive Texture.", Float) = 0
        [NoScaleOffset]_EmiMap("Emissive Texture (*_emi)", 2D) = "black" {}
        _FlashColor("Flash Color", Color) = (0,0,0,1)
        _Cutoff("Alpha Cutoff", Range(0.01,1)) = 0.5
    }

    CGINCLUDE
        #define UNITY_SETUP_BRDF_INPUT RoughnessSetup
    ENDCG

    SubShader
    {
        Tags { "Queue" = "AlphaTest" "RenderType" = "TransparentCutout" "IgnoreProjector" = "True"}
        LOD 200

        Pass
        {
            ColorMask 0
            Lighting Off
            ZWrite On

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 texcoord : TEXCOORD0;
            };

            sampler2D _MainTex;
            fixed _Cutoff;

            v2f vert(appdata_img v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = v.texcoord;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.texcoord);
                clip(col.a - _Cutoff);
                return 0;
            }
            ENDCG
        }

        Pass
        {
            Tags {"LightMode" = "ShadowCaster"}
            ColorMask 0
            ZWrite On
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"

            struct v2f
            {
                V2F_SHADOW_CASTER;
                float2 texcoord : TEXCOORD1;
            };

            v2f vert(appdata_base v)
            {
                #if defined(PIXELSNAP_ON)
                    v.vertex = UnityPixelSnap(v.vertex);
                #endif

                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                o.texcoord = v.texcoord;
                return o;
            }

            sampler2D _MainTex;
            fixed _Cutoff;

            float4 frag(v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.texcoord);
                clip(col.a - _Cutoff);
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }

        ColorMask 0
        LOD 300
        ZWrite On
        // paste in forward rendering passes from Transparent/Diffuse
        UsePass "Transparent/Diffuse/FORWARD"

        CGPROGRAM
        #include "UnityCG.cginc"
        #include "UnityPBSLighting.cginc"
        #pragma surface surf Standard vertex:vert fullforwardshadows decal:blend exclude_path:prepass
        #pragma multi_compile _ PIXELSNAP_ON
        #pragma shader_feature SKIN_MAT
        #pragma shader_feature USE_VERT_RGB
        #pragma shader_feature USE_TEX_ALPHA
        #pragma shader_feature USE_PRM_MAP
        #pragma shader_feature USE_CAV_MAP
        #pragma shader_feature USE_EMISSIVE
        #pragma shader_feature USE_VERT_ALPHA
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _InkTex;
        sampler2D _NormalMap;
        sampler2D _PRMMap;
        sampler2D _SplatMap;
        sampler2D _EmiMap;
        sampler2D _InkBump;
        sampler2D _MetalMap;
        float4 _InkColor;
        float4 _Color;
        float4 _RimLightColor;
        fixed _Cutoff;

        struct Input
        {
            float2 uv_MainTex;
            float3 worldRefl;
            float3 viewDir;
            float3 worldPos;
            float4 vertexColor : COLOR;
            INTERNAL_DATA
        };

        fixed4 _CustomVector11;
        fixed4 _FlashColor;
        fixed4 _DefaultVertClr;
        float4 _CustomVector30;

        // Generate the Z+ direction, or blue channel, of supplied normal map.
        float GetNormalMapZ(float2 normalmap)
        {
            // Remap the 0 to 1 range of the normal map to -1 to 1.
            float r = 2 * normalmap.r - 1.0;
            float g = 2 * normalmap.g - 1.0;
            // x*x + y*y + z*z = 1.0
            float b = sqrt(1 - (r * r) + (g * g));
            // Map back to 0 to 1 range to get the equivalent texture color.
            return b * 0.5 + 0.5;
        }

        void vert(inout appdata_full v, out Input o) {
            UNITY_INITIALIZE_OUTPUT(Input, o);
            o.vertexColor = v.color;
        }

        void surf(Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex);                                // Albedo comes from a texture tinted by color
            fixed4 nor = tex2D(_NormalMap, IN.uv_MainTex);                            // Use the normal map as is for now.
            float blend = nor.b;                                                    // Set current nor.b as blend map.
            float cav = nor.a;                                                        // Set current nor.a as cavity map.
            nor.b = GetNormalMapZ(nor.rg);                                            // Generate the .b, or Z+ channel, of the normal map.
            nor.a = 1;                                                                // Cancel out Alpha on nor.
            fixed4 prm = tex2D(_PRMMap, IN.uv_MainTex);                                // Init PRM map.
            fixed3 vertRGB = IN.vertexColor.rgb;                                    // Get vertex colors.
            float sssBlend = _CustomVector30.x * prm.r;                                // Shading and color.                   
            fixed4 emi = tex2D(_EmiMap, IN.uv_MainTex);                                // Emissive maps.
            float3 albedoFinal;                                                        // Pre-final Albedo.
            half rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));        // Setup rimlights.
            float texAlpha;
            float vertAlpha;

            #if USE_TEX_ALPHA
                texAlpha = saturate(c.a * _Cutoff);
            #else
                texAlpha = 1;
            #endif

            #if USE_VERT_ALPHA
                vertAlpha = IN.vertexColor.a;
            #else
                vertAlpha = 1;
            #endif

            o.Normal = UnpackNormal(nor);                                           
            float3 worldRefl = WorldReflectionVector(IN, o.Normal);
            rim = 1.0 - saturate(dot(normalize(IN.viewDir), o.Normal));       
            #ifdef USE_PRM_MAP
                #ifdef SKIN_MAT
                    albedoFinal = lerp((c.rgb * _Color), (c.rgb * _Color  * _CustomVector11), sssBlend);
                    o.Metallic = 0;
                    o.Smoothness = 0;
                #else
                    albedoFinal = c.rgb * _Color;
                    o.Metallic = prm.r * c.rgb;
                    o.Smoothness = 1 - prm.g;
                #endif
                o.Occlusion = prm.b;
            #else
                albedoFinal = c.rgb * _Color;
                o.Metallic = 0;
                o.Smoothness = 0;
            #endif
            #ifdef USE_VERT_RGB
                o.Albedo = albedoFinal * IN.vertexColor.rgb;
            #else
                o.Albedo = albedoFinal * _DefaultVertClr;
            #endif       
            #ifdef USE_EMISSIVE
                o.Emission = _FlashColor.rgb + emi.rgb + (_RimLightColor * pow(rim, 3));
            #else
                o.Emission = _FlashColor.rgb + (_RimLightColor * pow(rim, 3));
            #endif

            o.Alpha = texAlpha * vertAlpha;

        }
        ENDCG
    }
    Fallback "VertexLit"
}

What’s this for? That’s going to mean it’s rendering the main texture twice, and without any consideration of the vertex alpha.

I don’t know, I got some of this code from a shader that allowed alphas AND no z-fighting.

So if I remove that?

Try it?

Also I’m not sure Z-fighting is the term you’re looking for. Z-fighting is when two surfaces are on the same plane, but not using the same vertices or math to calculate the screen space position, and causing the math to not perfectly match and you see a kind of striped noise where one surface or the other swaps back and forth.

That’s Z-fighting. You’re probably thinking of depth sorting, which is where surfaces that are behind another appear “on top” when they shouldn’t, which is a still unsolved problem for transparent surfaces. That first ColorMask 0 pass should be a good enough workaround for that though.

It’s okay, thanks! Removing the Forward pass thing worked. Now my alphas (texture and vertex) work flawlessly. And sorting is fine too. And I also got my shader to support Cutout and receiving shadows.

Thanks for the help!