How to write to the different TEXCOORD channels?

Hello!

I feel like I have a unique situation that may be a bit challenging to explain. Here goes:

I start off with a single planar mesh and use a C# script I wrote to subdivide the mesh into separate meshes. Important to note is that the UV coordinates are preserved, so all the “sub-meshes” still look like the starting mesh. This works great and wasn’t tricky to do at all, and I rather like this property of the script.

BUT, I need a separate set of UV coordinates that will use an entire texture for the purpose of vertex displacement. I know I should be taking advantage of the different TEXCOORD channels, but I don’t quite know how to write to them with a C# script. My initial intuition was to write to the different UV channels, but that doesn’t seem to work.

Here’s my relevant portion of the shader and scripts before I really started messing with them:

Shader:

struct appdata {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 tangent : TANGENT;
    float2 uv : TEXCOORD0;
    float2 uv1 : TEXCOORD1;
    float2 uv2 : TEXCOORD2;
};

struct v2f {
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
    float3 normal : TEXCOORD1;
    float2 dispUV : TEXCOORD2;
};

v2f VertexProgram(appdata v) {
    v2f i;
    UNITY_INITIALIZE_OUTPUT(v2f, i);

    i.uv = TRANSFORM_TEX(v.uv, _DispTex);

    float d = tex2Dlod(_DispTex, float4(1 - i.uv.x, i.uv.y, 0, 0)).r * _Displacement;
    //v.normal = normalize(v.normal);
    v.vertex.y -= v.normal.y * d;
    v.vertex.y += v.normal.y * _Displacement;

    i.pos = UnityObjectToClipPos(v.vertex);
    return i;
}

Script:

// Set up displacement UVs
            Vector2[] dispUVs = new Vector2[subMesh.vertexCount];
            for(int count = 0, i = 0; i < subMeshSide; i++)
            {
                for(int j = 0; j < subMeshSide; j++, count++)
                {
                    dispUVs[count] = new Vector2(j / (subMeshSide - 1), i / (subMeshSide - 1));
                }
            }
            subMesh.uv2 = dispUVs;

In the shader, the mesh’s UVs from appdata are zero based, ie: TEXCOORD0, TEXCOORD1, etc., like you already have in your shader. From script the .uv is one based, ie: uv (1), uv2, uv3, etc. That means subMesh.uv2 == TEXCOORD1. I would recommend using the SetUVs() function instead as they’re zero based just like the shaders which will reduce confusion. Additionally the SetUVs() function lets you set a Vector4/float4 value per UV instead of being limited to a Vector2 value. That means you can store both the base texture and displacement UVs in one uv set.

List<Vector4> baseUVs = new List<Vector4>();
subMesh.GetUVs(0, baseUVs);
// loop start
baseUVs[count].z = j / (subMeshSide - 1);
baseUVs[count].w = i / (subMeshSide - 1);
// loop end
subMesh.SetUVs(0, baseUVs);

Now, in the shader, you’re currently never actually using those extra UVs, only the first one. But presumably you want to pass on the original UVs for use in the fragment shader still and only need the displacement UVs in the vertex shader.

struct appdata {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 tangent : TANGENT;
    // note float4 not float2
    float4 uv : TEXCOORD0;
};

struct v2f {
    float4 pos : SV_POSITION;
    // only float2 for UVs passed to fragment
    float2 uv : TEXCOORD0;
    // no dispUV since presumably it's not needed in the fragment shader
};

v2f VertexProgram(appdata v) {
    v2f i;
    UNITY_INITIALIZE_OUTPUT(v2f, i);

    // use z and w components of v.uv for the displacement texture.
    // no need to apply TRANSFORM_TEX since presumably you don't ever want
    // to use the scale offset settings for it
    float d = tex2Dlod(_DispTex, float4(1 - v.uv.z, v.uv.w, 0, 0)).r * _Displacement;
    v.vertex.y -= v.normal.y * d;
    v.vertex.y += v.normal.y * _Displacement;

    // for base color UVs only use the x and y of v.uv, and apply _MainTex offsets (if you want them)
    i.uv = TRANSFORM_TEX(v.uv.xy, _MainTex);
    i.pos = UnityObjectToClipPos(v.vertex);
    return i;
}
1 Like

Awesome! Thank you so much! I’ll check it out later today and report back on if I was able to get it to work.

Okay, so I implemented your suggestions and got it running, but nothing overall seems to have changed… Hmm…

Here’s a clip of what’s going on.

There’s three main things.

  1. The scale is wrong
  2. The displacement is incorrect
  3. The tessellation does not follow the displacement.

Looks like I have some more debugging to do. Which is annoying because everything I have works on an ordinary mesh.

I was about to say I had to scrap the notion of preserving the original UV coordinates because it wouldn’t have worked, but upon typing it out I figured it out!

Anyway, I got it all working now. The earlier issue was that I was forgetting to adjust the relevant tessellation and fragment items as I was making adjustments. Silly me.

1 Like

Thanks,i think:
textcoord = uv = mesh