This is part 14 of a tutorial series about hexagon maps. Up to this point, we've used solid colors to paint our maps. Now we're going to use textures instead.

Blending Three Types

While uniform colors are clear and get the job done, they're not very interesting to look at. Upgrading to textures can greatly increase the appeal of our maps. Of course, that requires that we blend textures, instead of just colors. The Rendering 3, Combining Textures tutorial shows how you can blend multiple textures, by using a splat map. We can use a similar approach for our hex maps.

The Rendering 3 tutorial only blends four textures, and could support up to five with a single splat map. We currently use five different colors, so that could work. However, we might want to add more types later. So we should support an arbitrary amount of terrain types. This isn't possible with explicit texture properties. Instead, we have to use a texture array. We'll create one later.

When using texture arrays, we have to somehow tell the shader which textures to blend. The most complex blend is required for corner triangles, which can sit between three cells that each have a different terrain type. So we have to support blending between three different types per triangle.

Using Vertex Colors as Splat Maps Assuming we can communicate which textures to blend, we can use vertex colors to create a splat map for each triangle. As there are at most three textures in play at a time, we only need three color channels. Red represents the first texture, green corresponds with the second, and blue is for the third. Splat map triangle. Does the splat map triangle always sum to one? Yes. The three color channels define a trilinear interpolation across the surface of the triangle. They function as barycentric coordinates. For example, you have the three possible permutations of (1, 0, 0) at the corners, variants of (½, ½, 0) in the middle of the edges, and (⅓, ⅓, ⅓) at the center. If a triangle requires only a single texture, we'll just use the first channel. So its color will be solid red. In the case of a blend between two different types, we'll use both the first and second channel. So that triangle's color will be a blend between red and green. And if all three types are in play, it's a blend between red, green, and blue. Three splat map configurations. We'll use these splat map configurations no matter which textures are actually blended. So the splat map is always the same. It's the textures that vary. We'll figure out how to do that later. We have to update HexGridChunk so it creates these splat maps, instead of using the cell colors. Because we'll be using the three colors a lot, create static fields for them. static Color color1 = new Color(1f, 0f, 0f); static Color color2 = new Color(0f, 1f, 0f); static Color color3 = new Color(0f, 0f, 1f);

Cell Centers Start by replacing the color of the default cell centers. There is no blend going on here, so we simply use the first color, which is red. void TriangulateWithoutRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { TriangulateEdgeFan(center, e, color1 ); … } Red cell centers. The cell centers have now become red. They all use the first of the three textures, no matter which texture that happens to be. Their splat maps are identical, no matter which color you paint the cells.

Adjacent to Rivers We've only changed segments inside cells without rivers flowing through them. We have to do the same for segments that are adjacent to rivers. In this case, it's both an edge strip and an edge fan. Again, red is all we need. void TriangulateAdjacentToRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { … TriangulateEdgeStrip(m, color1 , e, color1 ); TriangulateEdgeFan(center, m, color1 ); … } Red segments adjacent to rivers.

Rivers Next, we have to take care of the river geometry inside the cells. They should all become red too. First, the parts at the beginning and end of rivers. void TriangulateWithRiverBeginOrEnd ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { … TriangulateEdgeStrip(m, color1 , e, color1 ); TriangulateEdgeFan(center, m, color1 ); … } And then the geometry that makes up the river banks and channel. I've also grouped the color method invocations, so the code is easier to read. void TriangulateWithRiver ( HexDirection direction, HexCell cell, Vector3 center, EdgeVertices e ) { … TriangulateEdgeStrip(m, color1 , e, color1 ); terrain.AddTriangle(centerL, m.v1, m.v2); // terrain.AddTriangleColor(cell.Color); terrain.AddQuad(centerL, center, m.v2, m.v3); // terrain.AddQuadColor(cell.Color); terrain.AddQuad(center, centerR, m.v3, m.v4); // terrain.AddQuadColor(cell.Color); terrain.AddTriangle(centerR, m.v4, m.v5); // terrain.AddTriangleColor(cell.Color); terrain.AddTriangleColor(color1); terrain.AddQuadColor(color1); terrain.AddQuadColor(color1); terrain.AddTriangleColor(color1); … } Red rivers across cells.

Edges Edges are different, because they're between two cells, which could have different terrain types. We'll use the first color for the current cell's type, and the second color for its neighbor's type. As a result, the splat map is a red-green gradient, even if both cells happen to have the same type. If both cells use the same texture, then it will simply become a blend between the same texture on both ends. void TriangulateConnection ( HexDirection direction, HexCell cell, EdgeVertices e1 ) { … if (cell.GetEdgeType(direction) == HexEdgeType.Slope) { TriangulateEdgeTerraces(e1, cell, e2, neighbor, hasRoad); } else { TriangulateEdgeStrip(e1, color1 , e2, color2 , hasRoad); } … } Red-green edges, except for terraces. Isn't the hard transition between red and green a problem? Although the edge transition is from red to green, the cell centers on both sides are red. So there appears to be a discontinuity on one side of the edge. However, this is just the splat map. The colors of adjacent triangles do not need to be linked to the same textures. In this case, what corresponds with green on one side corresponds with red on the other side. Note that this wouldn't be possible if the triangles shared vertices. Edges with terraces are more complex, because they have extra vertices. Fortunately, the existing interpolation code works just fine with our splat map colors. Simply use the first and second colors, instead of those of the begin and end cells. void TriangulateEdgeTerraces ( EdgeVertices begin, HexCell beginCell, EdgeVertices end, HexCell endCell, bool hasRoad ) { EdgeVertices e2 = EdgeVertices.TerraceLerp(begin, end, 1); Color c2 = HexMetrics.TerraceLerp( color1 , color2 , 1); TriangulateEdgeStrip(begin, color1 , e2, c2, hasRoad); for (int i = 2; i < HexMetrics.terraceSteps; i++) { EdgeVertices e1 = e2; Color c1 = c2; e2 = EdgeVertices.TerraceLerp(begin, end, i); c2 = HexMetrics.TerraceLerp( color1 , color2 , i); TriangulateEdgeStrip(e1, c1, e2, c2, hasRoad); } TriangulateEdgeStrip(e2, c2, end, color2 , hasRoad); } Red-green edge terraces.