Search Unity

(missing core docs): Which splat/alphamap sample is used for a specific heightmap coord?

Discussion in 'General Graphics' started by a436t4ataf, Dec 1, 2019.

  1. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    If the splatmap is identical resolution to the heightmap, then it's obvious: the coord in splatmap is the same as the coord in heightmap.

    But e.g. if your heightmap is 1025 (i.e. coords 0..1024), and your splatmap is 820 (i.e. coords 0..819), then:

    For every 5 height map samples, there are 4 splatmap samples. So ... which one does Unity use? Does Unity round up? Round down? Round to nearest?

    NB: since there is no Terrain.SampleSplatmap( Vector3 ) method, there's no explicit way to verify this. So far, I've been making guesses and trying to describe them by looking at it visually (and is it the same on all platforms? WHO KNOWS!!)
     
  2. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933


    This is with mesh-wireframe view enabled in Editor. What you're seeing:

    On the left:
    • 33x33 Unity heightmap (Unity optimizes this down to 17x17 mesh verts)
    • 32x32 Unity splatmap
    • Splatmap has a 1-pixel border of red, then a 1-pixel border of green, then blue center
    • The heightmap is sub-sampling the splatmap: the bottom left square has heightmap coords that are red, blue, red, and red. But instead of lerping red-to-blue, it samples the splatmap across the full quad
    On the right:
    • 33x33 Unity heightmap (as above ... 17x17)
    • 16x16 Unity splatmap (i.e. half-resolution)
    • Splatmap has a 1-pixel border of red, then a 1-pixel border of green, then blue center
    • The heighmap is now sampling 1-for-1 the splatmap coords, so you see what you'd expect, even though the splatmap has been defined as half the resolution
    So, takehomes:
    1. The splatmap-coord for a heightmap-coord is:
      1. Find the X splatmap-coord less than heightmap-coord
      2. Find the X splatmap-coord greater than heightmap-coord
      3. Find the Y splatmap-coord less than heightmap-coord
      4. Find the Y splatmap-coord greater than heightmap-coord
      5. Bilinearly interpolate (double-lerp) between the four values you now have; this is your final splatmap-coord
    2. If you want to store splatmap data for each heightmap coord
      1. You must store FOUR splatmap values for every ONE heightmap value (the four above)
      2. To restore the splatmap data for a single heightmap coord, you have to overwrite all FOUR surrounding splat values
      3. Setting splat values for a height value ALWAYS corrupts neighbouring heightmap coords' textures/splats, unless the splatmap is PRECISELY one less than the resolution of the heightmap (Unity's default, but not enforced, and has no warnings if you change it)
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    A height map of 33x33 will produce a full resolution terrain mesh of 33x33 vertices if there's any height in the height map. Try moving one "vertex" up to a height of 1 and you'll see the entire terrain go up to the full resolution. If you're still seeing lower than that then you're viewing the terrain from far enough away that it's dropping the terrain LOD. Try setting the terrain's Pixel Error to 1.

    The control map resolution (ie: the splat maps / alpha maps) are generally disconnected from the heightmap and rendered mesh resolution for greater flexibility, also because for world alignment you want the heightmap to be an odd resolution (33x33) so that each vertex of an evenly sized terrain falls nicely on the world grid, but since splat maps are textures they had to be power of two sizes to work on some older platforms (none of which Unity actually supports anymore).

    The fallout of this is while the 33x33 heightmap and 16x16 control map resolution appears to have a single texel per vertex around the outside edge, if you try do draw a line down the center you'll see this isn't the case as the center vertex line is half way between two control map pixels. You'd need the control map resolution to match the heightmap resolution for this to be true, but due to the legacy nature of the current terrain system this isn't possible.
     
  4. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    To my surprise ... there is (Unity2019) no limit on the alphamap resolution other than "must be greater than or equal to 16".

    So (not included above, but I tested further), the following are all valid (and work!):
    • height: 33x33, alpha: 32x32 (expected)
    • height: 33x33, alpha: 33x33 (not expected!)
    • height: 33x33, alpha: 99x99 (not expected)
    ...but anything you to do to height resolution is forceable converted by Unity to "largest POT+1 that is less than or equal to the number you attempted"

    The alphamap appears to simply be interpolated across the region with no concern or worry. I guessed that was because the interpolation is being done 100% GPU side (hence: Unity doesn't care what the texture size is, nor its relationship to the heightmap) ... and that this is also the reason there is no "Terrain.SampleAlphamap()" method to match the Terrain.SampleHeight() method.

    I may be misunderstanding your final paragraph, but ... "due to the legacy nature of the current terrain system this isn't possible." seems to be incorrect today?

    (NB: exactly what you write was what I believed to be true, and have habitually used in all projects - but I started with Unity Terrains in ... 3.5, I think? ... so maybe it's a constraint that got relaxed recently?)
     
  5. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Also this:
    ...appears to be incorrect. It's actually "is PRECISELY the resolution of the heightmap".
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    I checked this with Unity 2019.2.5f1 and the resolution of the control map (which sets the splat map resolution) always goes to the nearest power of 2 resolution, min 16x16, and the heightmap always nearest power of 2 + 1, min 33x33, thus they can never match. If there's a version of Unity where this is fixed, then great!

    And yes, the splat map is just a bilinear filtered texture sampled by the fragment shader on the GPU. The terrain mesh is setup so the UVs for the splatmap go from the center of the pixel in one corner to the center of the pixel in the other corner. There's probably no way to get an interpolated sample in c# because no one every asked for one. It'd be easy enough to generate one by doing the interpolation yourself. That's what I do (or rather, I just calculate the int position and use that).
     
  7. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Checked in Unity 2019.2.2 just now:

    Code (CSharp):
    1. terrainData.alphamapResolution = 17;
    2. Assert.AreEqual( 17, terrainData.alphamapResolution );
    ...passes the test, and also renders correctly in Editor:


    (left to right: heights = 33, splats = 16 ... 17 ... 25)

    The 16 vs 17 difference isn't clear, so I did 25 as well to make it more obvious.

    I don't know what's different in 2019.2.5, but it certainly works (worked?) fine in 2019.2.2

    EDIT: to be clear ... if you do a similar "set, then Assert" with heightmaps, the value returned by terrainData.heightmapResolution is NOT the same as the value you set - Unity does the conversion at the moment of setting the property; I would expect alphamapResolution to work the same (I thought it used to!)

    EDIT2: again, to be clear ... this is using same code as earlier post in this thread: Iterate across the alphamap coords, coloring red for 0 <= coord <1, green for 1 <= coord < 3, blue for coord >= 3. So: changing the alphamap resolution makes the blue take up more or less of the center, with the red/green borders shrinking or growing.
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    I'm setting the values from the inspector, so I would guess the inspector itself is enforcing the power of two sizes for the control maps then. I didn't try setting them from script.
     
    a436t4ataf likes this.
  9. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Aha. That would be what I remember from earlier Unity versions: inspector-enforced changes.

    But ... it seems to cause absolutely no problems at all (so far...) to set any resolution you want. So ... that's good, I guess? :)