Search Unity

Custom UV Sets for Shaders (tutorial)

Discussion in 'Shaders' started by PocketJoyApps, Mar 16, 2019.

  1. PocketJoyApps

    PocketJoyApps

    Joined:
    Jun 3, 2015
    Posts:
    5
    This explains how to have several custom UV sets in a standard surface shader (using mesh.uv2, mesh.uv3, etc...)

    I'm posting this because I'm pretty sure there's no doc or forum post on it, it took me hours of tweaking legacy code before I figured out how to do this in the latest version

    HOW TO:
    First, note that uv4 = texcoord3, because the naming conventions use different indexing
    I'll be modifying uv4 for this example, because uv1 is standard, uv2 is lightmap, and uv3 is GI, I recommend only using uv4 and up (texcoord3 and up)

    To start, create a "Standard Surface Shader", and open the source code...

    1. Include this after CGPROGRAM
    Code (CSharp):
    1. #pragma vertex vert
    2. Include a custom appdata struct, such as
    Code (CSharp):
    1. struct appdata
    2.         {
    3.             float4 vertex : POSITION;
    4.             float2 texcoord : TEXCOORD0;
    5.             float2 texcoord3 : TEXCOORD3;
    6.             float3 normal : NORMAL;
    7.             float4 tangent: TANGENT;
    8.         };
    3. Include the Input struct
    Code (CSharp):
    1. struct Input
    2.         {
    3.             float2 uv_MainTex;
    4.             float2 myuv;
    5.         };
    Note that we're calling it myuv, DO NOT start the var name with uv, since those can get replaced at compile time (eg DON'T call it uv4_myuv)

    4. Include a custom vert function
    Code (CSharp):
    1. void vert(inout appdata v, out Input o) {
    2.             UNITY_INITIALIZE_OUTPUT(Input, o);
    3.             o.myuv = v.texcoord3;
    4.         }
    5. That's it. You can access it in the surf function using IN.myuv. For those curious, the full pipeline is as follows:
    a. "#pragma vertex vert" binds "void vert" as the vertex function of the standard surface shader program
    b. "float2 texcoord3 : TEXCOORD3;" defines the vertex shader to expect a vertex attribute for the uv set, which is passed in my the user using mesh.uv4
    c. "float2 myuv;" defines a varying in the surf shader's "Input" struct, for us to pass data to
    d. "void vert..." is ran per vertex, and "o.myuv = v.texcoord3;" copies the data from the attribute "texcoord3" to the varying "myuv"
     
    lucasd777, Quatum1000 and bgolus like this.
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    I would suggest not using .uv and using .SetUVs() instead. Each TEXCOORD is actually always a Vector4/float4 of data, which when using .uv only sets the first two values of and zeroes out the last two components. Using .SetUVs() you can set a full Vector4 is so you can pack two UVs into TEXCOORD0. For Surface Shaders you still need to use a custom vertex function to pass those two extra floats of data to the surf function though, as the uv_* variables are always assumed to be a float2 only.

    Technically you could still store data in the zw components of TEXCOORD1 and TEXCOORD2, but I've never tested to see if Unity's lighting system stomps on those values or not, even though it does not use them.
     
  3. PocketJoyApps

    PocketJoyApps

    Joined:
    Jun 3, 2015
    Posts:
    5
    Didn't know that, thanks