在合并之前删除 tex2D 的一部分

Removing a part of a tex2D before combining it

我正在编写统一表面着色器以缓慢应用像这样的生锈效果:

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

然后:

//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.

我成功做到了:

        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;
        }

那么我该如何完成这个着色器呢? 我找不到有关表面着色器中可用函数的详细文档。

编辑:我几乎可以使用 saturate() 得到我需要的东西;功能如下

    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

}

这样就做出了我需要的效果。现在唯一的问题是如何模糊 alpha 的边缘?

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

您不能简单地限制 _RustAmount 浮动以下的值吗?类似于:

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);

请注意,上面的代码会产生一些从纹理到生锈的突然过渡(更突然 _RustmAmount 高于零)。您最终希望在 [0,1] 范围内的钳位后重新映射每个高于零的值。

如果您需要平滑过渡,您可以将间隔 [_RustAmount,1] 重新映射到 [0,1]:

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

希望对您有所帮助

旁注:

  • 避免在着色器中分支(即使在制服上分支在现代硬件上不应该这么痛苦)
  • 如果地图使用其他 2 个纹理之一的相同组 uv 坐标(和平铺),那么您可以使用较少的纹理采样操作将其打包到相关的 alpha 通道中。
  • 因为你的着色器是不透明的,我想最终的 alpha 值是不相关的,所以我只是用了一个 float3 来最小化要 lerped 的值。