Search code examples
unity-game-engineshadercg

Removing a part of a tex2D before combining it


Im coding a unity surface shader to slowly apply a rust effect like this:

//Take 1 base color texture.
//Take 1 rust decal texture and 1 greyscale maps.
//Take 1 float range value.

Then:

//Use the range to remove from the grayscale map all the pixels that are darker than the value itself, then make theese greysclae map the rust alpha, then apply this composited rust layer over the color texture.

I managed to do this:

        void surf (Input IN, inout SurfaceOutputStandard o) {
            half4 C = tex2D (_MainTex, IN.uv_MainTex); //Color Texture
            half4 R = tex2D (_RustTex, IN.uv_RustTex); //Rust texture
            half4 RG = tex2D (_RustGuide, IN.uv_RustGuide); //Greyscale texture

            //Here i need to compose the rust layer
            half4 RustResult = //??? Maybe a Clip() function or what? and how?

            //Here i apply the previusly composed layer over the color texture. Already tested and working.
            half4 Final = lerp (C, RustResult, RustResult.a);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }

So how i can complete this shader? I cant find a detailed documentation about the usable functuons in surface shaders.

EDIT: I almost get what i need using saturate(); functionlike the following

    Properties {
    _MainTex ("Base (RGB)", 2D) = "" {} //the color texture
    _RustTex ("Rust Texture (RGB)", 2D) = "" {} //the rust texture
    _RustGuide ("Rust Guide (A)", 2D) = "" {} //the rust greyscale texture
    _RustAmount ("Rust Amount", range(0.0, 1.0)) = 0.0 //the rust amount float value
    _RustMultiplier ("Rust Multiplier", float) = 2
}

SubShader {
    Tags { "RenderType"="Opaque" }
    LOD 200

    CGPROGRAM
    #pragma target 3.0
    #include "UnityPBSLighting.cginc"
    #pragma surface surf Standard

    sampler2D _MainTex;
    sampler2D _RustTex;
    sampler2D _RustGuide;
    float _RustAmount;
    float _RustMultiplier;

    struct Input {
        float2 uv_MainTex;
        float2 uv_RustTex;
        float2 uv_RustGuide;
    };

    void surf (Input IN, inout SurfaceOutputStandard o) {
        half4 M = tex2D (_MainTex, IN.uv_MainTex);
        half4 R = tex2D (_RustTex, IN.uv_RustTex);
        half4 RG = tex2D (_RustGuide, IN.uv_RustGuide);
        half4 RustResult;
        RustResult.rgb = R.rgb;

        if (_RustAmount > 0) {
        RustResult.a = trunc(saturate(RG.a * _RustAmount * _RustMultiplier);
        }

        half4 Final = lerp (M, RustResult, RustResult.a);
        o.Albedo = Final.rgb;
        o.Alpha = Final.a;
    }
    ENDCG
} 
FallBack Off

}

This makes the effect that I need. The only problem now is how i can blur the edges of the alpha?


Solution

  • Use the range to remove from the grayscale map all the pixels that are darker than the value itself

    Can't you simply clamp the values below _RustAmount float? Something like:

    float greyScaleMapValue = tex2D(_RustGuide, IN.uv_RustGuide).a; //assuming rust guide is stored as a single channel
    
    float clampedMap = clamp(greyScaleMapValue , _RustAmount, 1); //clamped map stores a value clamped between _RustAmount and 1 -> every pixel darker than _RustAmount are 0
    
    half3 albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    half3 rust = tex2D (_RustTex, IN.uv_RustTex).rgb;
    float3 finalCol = lerp(albedo, rust, clampedMap); // all values in the map below _RustAmount will have plain albedo value, the other will be blended with rust using the map
    
    return float4(finalCol,1);
    

    Note that the code above produce some abrupt transition from texture to rust (more abrupt more _RustmAmount is higher than zero). You want eventually to remap each value higher than zero after the clamp in [0,1] range.

    If you need to smooth the transition you can remap the interval [_RustAmount,1] into [0,1]:

    float clampedMapNormalized = (clampedMap - _RustAmount) / (1 - _RustAmount);
    

    Hope this helps

    Side note:

    • avoid branching in the shaders (even if branching on uniforms shouldn't be such a pain on modern hardware)
    • if the map uses the same sets of uv coordinates (and tiling)of one of the other 2 textures, than you can pack it inside the relative alpha channel using one less texture sample operation.
    • since your shader is opaque, I guess final alpha value isn't relevant, so I just used a float3 to minimize the values to be lerped.