private void GenerateVertexColorAtIndex(Index2D i)
        {
            Color c             = Color.white;
            bool  isUnderground =
                !Utilities.IsInRange(i.X, SurfaceMinIndex.X - 1, SurfaceMaxIndex.X + 1) ||
                !Utilities.IsInRange(i.Z, SurfaceMinIndex.Z - 1, SurfaceMaxIndex.Z + 1);

            if (isUnderground)
            {
                c = UndergroundColor;
            }
            else
            {
                Vector3 v = vertices[i.Z][i.X];
                float   heightFraction = Utilities.GetFraction(v.y - BaseHeight, 0, SurfaceMaxHeight);
                Color   cbh            = ColorByHeight.Evaluate(heightFraction);

                Vector3 normal      = EvaluateNormalAtIndex(i);
                float   normalDotUp = Vector3.Dot(normal, Vector3.up);
                Color   cbn         = ColorByNormal.Evaluate(normalDotUp);

                float blendFraction = ColorBlendFraction.Evaluate(heightFraction).a;
                c = Vector4.Lerp(cbh, cbn, blendFraction);
            }

            int cIndex = To1DIndex(i.X, i.Z);

            vertexColors[cIndex] = c;
        }
        public void UpdateLookupTextures()
        {
            if (colorByHeightMap != null)
            {
                Object.DestroyImmediate(colorByHeightMap, true);
            }
            if (colorByNormalMap != null)
            {
                Object.DestroyImmediate(colorByNormalMap, true);
            }
            if (colorBlendMap != null)
            {
                Object.DestroyImmediate(colorBlendMap, true);
            }
            int width  = 256;
            int height = 8;

            colorByHeightMap            = new Texture2D(width, height, TextureFormat.ARGB32, false, true);
            colorByHeightMap.filterMode = FilterMode.Bilinear;
            colorByHeightMap.wrapMode   = TextureWrapMode.Clamp;
            colorByHeightMap.name       = COLOR_BY_HEIGHT_MAP_NAME;

            colorByNormalMap            = new Texture2D(width, height, TextureFormat.ARGB32, false, true);
            colorByNormalMap.filterMode = FilterMode.Bilinear;
            colorByNormalMap.wrapMode   = TextureWrapMode.Clamp;
            colorByNormalMap.name       = COLOR_BY_NORMAL_MAP_NAME;

            colorBlendMap            = new Texture2D(width, height, TextureFormat.ARGB32, false, true);
            colorBlendMap.filterMode = FilterMode.Bilinear;
            colorBlendMap.wrapMode   = TextureWrapMode.Clamp;
            colorBlendMap.name       = COLOR_BLEND_MAP_NAME;

            Color[] cbhColors = new Color[width * height];
            Color[] cbnColors = new Color[width * height];
            Color[] cbColors  = new Color[width * height];

            for (int x = 0; x < width; ++x)
            {
                float f   = Mathf.InverseLerp(0, width - 1, x);
                Color cbh = ColorByHeight.Evaluate(f);
                Color cbn = ColorByNormal.Evaluate(f);
                Color cb  = Color.white * ColorBlendCurve.Evaluate(f);
                for (int y = 0; y < height; ++y)
                {
                    int index = GUtilities.To1DIndex(x, y, width);
                    cbhColors[index] = cbh;
                    cbnColors[index] = cbn;
                    cbColors[index]  = cb;
                }
            }

            colorByHeightMap.SetPixels(cbhColors);
            colorByHeightMap.Apply();
            GCommon.TryAddObjectToAsset(colorByHeightMap, TerrainData);

            colorByNormalMap.SetPixels(cbnColors);
            colorByNormalMap.Apply();
            GCommon.TryAddObjectToAsset(colorByNormalMap, TerrainData);

            colorBlendMap.SetPixels(cbColors);
            colorBlendMap.Apply();
            GCommon.TryAddObjectToAsset(colorBlendMap, TerrainData);
        }