Hi @SteveTheodore,
These are great questions.
- I’m using multiple terrain tiles.
- The interpolated normal positions are arbitrary and not always aligned with heightmap coordinates.
I found C# logic for calculating a Sobel filter from a normalized position using height values. It returns nearly identical results to Unity’s existing GetSteepness() method – yay!
…but it is 4x slower than existing Unity logic.
I’m on board with creating a steepness map! I’m attempting to use the Unity GenNormalMap shader (above). It’s running but the values I am getting back are incorrect.
I may be using incorrect constants, or an incorrect order for terrain tile scale offsets. Creating 25 pre-generated normal maps takes 230ms – for 25 square miles of terrain. This is definitely ideal, but I need to actually use the shader correctly.
Do you or your team use this Unity shader? I’m supplying normalMapGenSize as
Vector4 normalMapGenSize = new Vector4(
1.0f / terrainData.heightmapResolution,
1.0f / terrainData.heightmapResolution,
1.0f / terrainSize.x,
1.0f / terrainSize.z
and _TerrainTileScaleOffsets as
Vector4[] terrainTilesScaleOffsets = new Vector4[9];
terrainTilesScaleOffsets[0] = CalculateTileScaleOffset(terrain.bottomNeighbor?.leftNeighbor); // BL
terrainTilesScaleOffsets[1] = CalculateTileScaleOffset(terrain.bottomNeighbor); // B
terrainTilesScaleOffsets[2] = CalculateTileScaleOffset(terrain.bottomNeighbor?.rightNeighbor); // BR
terrainTilesScaleOffsets[3] = CalculateTileScaleOffset(terrain.leftNeighbor); // L
terrainTilesScaleOffsets[4] = CalculateTileScaleOffset(terrain); // C
terrainTilesScaleOffsets[5] = CalculateTileScaleOffset(terrain.rightNeighbor); // R
terrainTilesScaleOffsets[6] = CalculateTileScaleOffset(terrain.topNeighbor?.leftNeighbor); // TL
terrainTilesScaleOffsets[7] = CalculateTileScaleOffset(terrain.topNeighbor); // T
terrainTilesScaleOffsets[8] = CalculateTileScaleOffset(terrain.topNeighbor?.rightNeighbor); // TR
NormalMapGenerationMaterial.SetVectorArray("_TerrainTilesScaleOffsets", terrainTilesScaleOffsets);
CalculateTileOffset is
float kMaxHeight = 32766;
return new Vector4(65535.0f / kMaxHeight * terrainSize.y, terrainPosition.y, 0, 0);
I then convert to an array of Vector3 normals, via the following
private Vector3[,] TextureToNormals(Texture2D texture)
{
int width = texture.width;
int height = texture.height;
Vector3[,] normalsArray = new Vector3[width, height];
Color[] pixels = texture.GetPixels();
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
Color color = pixels[y * width + x];
normalsArray[x, y] = new Vector3((color.r - 0.5f) * 2.0f, (color.g - 0.5f) * 2.0f, (color.b - 0.5f));
}
return normalsArray;
}
Do you see anything that stands out as incorrect? I haven’t been successful finding an example of how the shader is used.
And after typing all of this up, I realize that I will still have to interpolate if the x/y does not align with the precomputed normals map.
I’m really surprised at how challenging this! I’m not as capable as I would like to be with Unity’s terrain system.