While rendering to terrainData.heightmapTexture I discovered that writing 1.0f to pixels doesn't result in terrain of maximum height (as specified in "Terrain Height" inspector field) but 0.5 does (1.0 is twice that and not available for manual brush edits). Seems odd/surprising but I expect there is sensible reason behind it. Can sb explain this behavior?
(image shows terrain after rendering a sine wave (0.0-1.0 range) to it. I was using a compute shader > Graphics.CopyTexture > terrainData.DirtyHeightmapRegion path)
Answer from Unity Technologies' engineer:
The heightmap implementation itself is signed but is treated as unsigned when rendering so we only have half the precision available to use for height values. That's why all of our Terrain painting shaders clamp the returned value between 0f and .5f so that we don't end up writing signed values into the heightmap. If you were to put in values greater than .5, you'll see the Terrain surface "wrap" to negative height values. I can't say why this was done but it probably has stayed this way because it would take a lot of code changes to make either of them signed or unsigned to match.
The values are normalized so that we can get the most precision we can out of the .5f for a given Terrain's max height. 0 being a world height offset of 0 and .5f being terrain.terrainData.size.y (the max height)