简体   繁体   中英

Swap out MainTex pixels with other textures' via surface Shader (Unity)

The main texture of my surface shader is a Google Maps image tile, similar to this: 在此输入图像描述 .

I want to replace pixels that are close to a specified color with that from a separate texture. What is working now is the following:

Shader "MyShader"
{
    Properties
    {
        _MainTex("Base (RGB) Trans (A)", 2D) = "white" {}

        _GrassTexture("Grass Texture", 2D) = "white" {}
        _RoadTexture("Road Texture", 2D) = "white" {}
        _WaterTexture("Water Texture", 2D) = "white" {}
    }

    SubShader
    {
        Tags{ "Queue" = "Transparent-1" "IgnoreProjector" = "True" "ForceNoShadowCasting" = "True" "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert alpha approxview halfasview noforwardadd nometa 

        uniform sampler2D _MainTex;
        uniform sampler2D _GrassTexture;
        uniform sampler2D _RoadTexture;
        uniform sampler2D _WaterTexture;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf(Input IN, inout SurfaceOutput o)
        {
            fixed4 ct = tex2D(_MainTex, IN.uv_MainTex);

            // if the red (or blue) channel of the pixel is within a 
            // specific range, get either a 1 or a 0 (true/false).
            int grassCond = int(ct.r >= 0.45) * int(0.46 >= ct.r);
            int waterCond = int(ct.r >= 0.14) * int(0.15 >= ct.r);
            int roadCond = int(ct.b >= 0.23) * int(0.24 >= ct.b);

            // if none of the above conditions is a 1, then we want to keep our
            // current pixel's color:
            half defaultCond = 1 - grassCond - waterCond - roadCond;

            // get the pixel from each texture, multiple by their check condition
            // to get:
            //    fixed4(0,0,0,0) if this isn't the right texture for this pixel
            //    or fixed4(r,g,b,1) from the texture if it is the right pixel
            fixed4 grass = grassCond * tex2D(_GrassTexture, IN.uv_MainTex);
            fixed4 water = waterCond * tex2D(_WaterTexture, IN.uv_MainTex);
            fixed4 road = roadCond * tex2D(_RoadTexture, IN.uv_MainTex);
            fixed4 def = defaultCond * ct; // just used the MainTex pixel

            // then use the found pixels as the Albedo
            o.Albedo = (grass + road + water + def).rgb;
            o.Alpha = 1;
        }
        ENDCG
    }

    Fallback "None"
}

This is the first shader I've ever written, and it probably isn't very performant. It seems counter intuitive to me to call tex2D on each texture for every pixel to just throw that data away, but I couldn't think of a better way to do this without if/else (which I read were bad for GPUs).

This is a Unity Surface Shader, and not a fragment/vertex shader. I know there is a step that happens behind the scenes that will generate the fragment/vertex shader for me (adding in the scene's lighting, fog, etc.). This shader is applied to 100 256x256px map tiles (2560x2560 pixels in total). The grass/road/water textures are all 256x256 pixels as well.

My question is: is there a better, more performant way of accomplishing what I'm doing here? The game runs on Android and iOS.

I'm not a specialist in Shader performance, but assuming you have a relatively small number of source tiles that you wish to render in the same frame it might make more sense to store the result of the pixel replacement and reuse it.

As you are stating that the resulting image is going to be the same size as your source tile, just render the source tile using your surface shader (without any lighting though, you may want to consider using a simple, flat pixel shader!) into a RenderTexture once and then use that RenderTexture as source for your world rendering. That way you are doing the expensive work only once per source tile and thus it isn't even important anymore whether your shader is well optimized.

If all textures are static, you might even consider not doing this at runtime, but just translate them once in the Editor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM