In surface shaders if you define an Input struct value with the name uv_
or uv#_
it automatically defines the _ST
variables, applies the TRANSFORM_TEX
, and packs the UVs. It also ignores the semantics (: TEXCOORD0
) on those lines.
All you need to do to apply the scale & offset and pack is to define your UVs.
struct Input {
float2 uv_Splat0;
float2 uv_Splat1;
};
That’s it. Internally those are being packed in the vertex shader and unpacked in the fragment shader before being handed to the surf() function.
If you look at the surface shader’s generated code you’ll see this:
// in the vertex shader
o.pack0.xy = TRANSFORM_TEX(v.texcoord, _Splat0);
o.pack0.zw = TRANSFORM_TEX(v.texcoord, _Splat1);
// in the fragment shader
surfIN.uv_Splat0 = IN.pack0.xy;
surfIN.uv_Splat1 = IN.pack0.zw;
The only reason to use a custom vert function for something like this is to modify the UVs manually or to skip the TRANSFORM_TEX
in the vertex shader, which honestly you should do when using multiple scale & offsets on the same texcoord like you do with splat maps. For that you want to define an input without the uv_
prefix.
struct Input {
float2 texcoord;
};
void vert(inout appdata_full v, out Input o)
{
o.texcoord = v.texcoord;
}
float4 _Splat0_ST, _Splat1_ST;;
void surf (Input IN, inout SurfaceOutput o) {
float2 uv_Splat0 = TRANSFORM_TEX(IN.texcoord0, _Splat0);
float2 uv_Splat1 = TRANSFORM_TEX(IN.texcoord0, _Splat1);
// do stuff
This way you’re only transferring the UV once, and each TRANSFORM_TEX is only a single additional MAD instruction, so it’s basically free. The only reason to do it in the vertex shader is to prevent dependent texture reads on mobile when using OpenGLES 2.0 devices, but packing the UVs also causes dependent texture reads so just using a surface shader to begin with is already causing this.
edit: You still need the custom vert function for this case as there’s a long time bug since Unity 5.1 where it culls UVs when defining Inputs with texcoord semantics under some cases.