Terrain Basemap vertex displacement

Hi, everyone!
In my custom terrain shader i use simple vertex displacement. “Height” is taken from _Specular value of each layer and i blend it with Control texture just as any other per layer property, everything works fine, but it doesnt work for basemap shader.

My understanding is i can’t access Metallic in BaseMap, but i can in BaseMapGen shader and it doesnt work there either.

I tried:

  • calculating height in BaseMapGen shader
  • calculating height in BaseMap shader
  • calculating height in custom “Picking” and “Selection” shaders (not sure what they do)
  • pass height as a red channel of _Color property (it passes correctly because the color changes appropriately)
  • adding _Height property to main pass and using it in BaseGen shader so terrain would at least change height for all layers (no luck)

Maybe another important detail: I use emission property the same way (through _Color property) and in fragment function in BaseMap it gives correct result which leads me to believe there may be some limitations to what can be done in BaseMap/BaseGen vertex functions

Any suggestions are very much appreciated

Below is a BaseGen vertex function just in case

half CalculateHeightInternal(half4 alpha, half h0, half h1, half h2, half h3)
{
    half height = h0 * alpha.x;
    height += h1 * alpha.y;
    height += h2 * alpha.z;
    height += h3 * alpha.w;
    return height;
}

v2f vert(appdata v)
        {
            v2f o;
            UNITY_SETUP_INSTANCE_ID(v);
            v = ApplyMeshModification(v);
            UNITY_TRANSFER_INSTANCE_ID(v, i);
            half2 controlUV = (v.vertex * (_Control_TexelSize.zw - 1.0f) + 0.5f) * _Control_TexelSize.xy;
            o.uv_Control = controlUV;
            o.uv_Splat_0_1.xy = TRANSFORM_TEX(v.vertex, _Splat0);
            o.uv_Splat_0_1.zw = TRANSFORM_TEX(v.vertex, _Splat1);
            o.uv_Splat_2_3.xy = TRANSFORM_TEX(v.vertex, _Splat2);
            o.uv_Splat_2_3.zw = TRANSFORM_TEX(v.vertex, _Splat3);
            
            o.alpha = tex2Dlod(_Control, half4(v.uv, 0, 0));
            half height = CalculateHeightInternal(o.alpha, _Metallic0, _Metallic1, _Metallic2, _Metallic3);
            o.normal = (UnityObjectToWorldNormal(v.normal));
            v.vertex.xyz += v.normal.xyz * height;
            o.pos = UnityObjectToClipPos(v.vertex);
            return o;
        }

The basemap is supposed to just be a cheap swap-in for distant terrain , so you don’t need to do the blending at every pixel off in the distance. Typically you wouldn’t need to implement the height yourself. Note the comment here in the URP implementation of TerrainLitBasemapGen.shader:

    Varyings Vert(Attributes IN)
            {
                Varyings output = (Varyings) 0;
                
                output.clipPos = TransformWorldToHClip(IN.positionOS.xyz);
                
                // NOTE : This is basically coming from the vertex shader in TerrainLitPasses
                // There are other plenty of other values that the original version computes, but for this
                // pass, we are only interested in a few, so I'm just skipping the rest.
                output.uvMainAndLM.xy = IN.texcoord;
                output.uvSplat01.xy = TRANSFORM_TEX(IN.texcoord, _Splat0);
                output.uvSplat01.zw = TRANSFORM_TEX(IN.texcoord, _Splat1);
                output.uvSplat23.xy = TRANSFORM_TEX(IN.texcoord, _Splat2);
                output.uvSplat23.zw = TRANSFORM_TEX(IN.texcoord, _Splat3);

                return output;
            }

It’s not transforming the verts at all, it’s just setting the uvs. Do you actually need it? It’s a decent optimnization for vanilla terrain but if you’re doing anything fancy (animations, for example, or emission) it’s not likely to help.

You can turn it off by just setting a very high basemap distance on the terrain and you’ll never see it, assuming you are happy with the “foreground” shader.

Thank you for your explanation!
I don’t really need it, but since i do vertex displacement in main and add passes it would be nice to have somewhat smooth transition to basemap and don’t limit myself to not use only main passes.
One more thing though. I thought setting uvs is the BaseMapGen’s job and BaseMap just renders the texture that it gets from BaseMapGen. Do i understand it wrong?