Change MainTex pixels for other textures using the surface shader (Unity)

The main texture of my surface shader is a Google Maps image tile similar to this: enter image description here .

I want to replace pixels close to the specified color with a separate texture. Now the following works:

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 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's probably not very efficient. It seems contraindicated for me to call tex2D for each texture for each pixel, just to throw away this data, but I could not figure out how best to do it if / else (which I read poorly for GPUs).

This is a Unity Surface Shader, not a fragment / vertex shader. I know that there is a step that happens behind the scenes that will generate the fragment / vertex shader for me (adding scene lighting, fog, etc.). This shader applies to 100 256x256px cards (total 2560x2560 pixels). Grass / road / water textures also have 256x256 pixels.

My question is: is there a more efficient way to accomplish what I'm doing here? The game works on Android and iOS.

+5
source share
1 answer

I am not a Shader performance specialist, but provided that you have a relatively small number of source fragments that you want to display in the same frame, it might make sense to store the result of replacing the pixel and reuse it.

As you state that the resulting image will be the same size as your original fragment, just visualize the original tile using your surface shader (without any lighting, you might consider using a simple flat pixel shader!) In RenderTexture once and then use RenderTexture as the source for your rendering in the world. This way, you only do expensive work once per source plate, and therefore it doesn’t even matter if your shader is improved.

If all textures are static, you can even not do this at run time, but simply translate them once into the Editor.

0
source

Source: https://habr.com/ru/post/1268677/


All Articles