Monday, 28 April 2014

Texture mapping small regions on a sphere.

(aka)How on Earth/Gaia do I make this look pretty.


 I've been really stuck on how to proceed and I figured building out the first pillar of the classic 4X system is as good as any to work on right now. That's singling out eXplore from eXpand, eXploit, and eXterminate.  The voronoi sphere I'm very happy with, it's a great starting point as an interesting layout to explore.  It led me into this mental puzzle of what else do I need to represent on it and what is it going to look like.  As always I can look at the great Civ which has a beautiful rich 3d representation of each terrain type.  That's a bit beyond me to accomplish and instead I want to pursue a more abstracted look.  This I think is a common indie game problem where you have to be careful and pragmatic about what you can build and accomplish to a good standard, it's why pixel art and minimalism rules.  So I went searching for maps.

Map inspiration.
Top Left - LOTR
Top Right - Elder Scrolls
Bottom Left - Battle of Cerro Gordo
Bottom Right - Game of Thrones


Maps are awesome.  I found all these great fantasy and battle maps, a little sample of four that peaked my interest in the image above.  The old paper look with little ink markings seems like a really good fit for what I need.  It's got a nice texture to it and it's graphically simple enough that I thought I got do a pretty good approximation of it myself with my programmer art skills.

First problem, texture mapping.



art de programmer

I needed to get 2d coordinates for the texture mapping onto my polygons.  Well each region has a central point.  I can use that to get the longitude and latitude, then rotate it first along the longitude then the latitude so that it's sitting directly on top of the sphere.  At this point you can disregard the Y value, like you're looking straight down from the top, you're now dealing with a simple 2d case.

I use these equations for the longitude and latitude. (Sorry not very good at pasting in code)

// Return the longitude & latitude of a point on a sphere
// X=longitude  Y=latitude
public static Vector2 GetLongitudeLatitude(Vector3 position)
{
   //make sure it's a unit vector
   position.Normalize();

   //latitude will be from 0 to pi
   double latitude = Math.Acos(position.Y);

   //longitude...use dot product with prime meridian
   Vector2 vec = new Vector2(position.X, position.Z);
   vec.Normalize();
   //prime meridian
   Vector2 mer = new Vector2(0f, 1f);

   double longitude = Math.Acos(Vector2.Dot(vec, mer));
            
   //for our sphere at origin.  we know everything that's -x
   //is to the left our prime meridian, range of 0 to TwoPi
   if (position.X < 0)
   {
      longitude = MathHelper.Pi + (MathHelper.Pi - longitude);
   }

   return new Vector2((float)longitude, (float)latitude);
}

You can set the texture coordinates easily from this by just plugging in x,z positions to map to the u,v texture coordinates with any scaling/offset desired.  But we're not done!

Insetting

If you look at most old paper textures, the paper darkens as it gets to the edge.  I'd also want to fade out any icon/patterns to a blank texture to make the region transitions nicer.  For that I needed to inset the polygons.  As per bloomin usual this is not entirely trivial, and of course it's the edge cases that get you.  Namely when a very small edge is inset you neet to collapse the point.

Collapsing points to make neat insets.

Very happily though In this case I did find an amazing library that would do the work for me...and it even compiled without hassle for Unity.  It's the Clipper library.   (and like many bits of 3rd party code it used some C# syntax I've never seen before).  It also slightly threw me for a loop when I found it only worked with integers, so you scale everything by like a billion then back down again otherwise it just gives you zeroes.

Almost done with the insetting at this point, but the path you get back from clipper doesn't necessarily match.  Imagine the outer edge and the inner edge are like two railroad tracks, you need them to be in sync for laying the railroad ties, i.e. building the indices for the polygons. So that was a little tricky to sort out.  Then the last step is to reverse the rotation to place it back in the world again.

hmm - little gaps

One last glitch needed fixing.  Transforming there and back again introduced just enough of an error that little gaps appeared.  To fix I kept (and also had to order from correctly) the original positions.  So the inset will actually be ever so slightly off but you'd never notice.

Shading

Almost there.  We have our UV map and our inset.  Now we want to blend from one texture to another to create a nice faded paper effect.  Unity didn't seem to have the shaders I needed in built but I found enough info via the wiki and forums to get me there.  First was a vector colour shader,  it shows me what colour I've inputed for these vertices so I can check it for debug.  These colours will define the blending between a couple of textures, so white (outer edges) will be one texture and black (inner edges) will blend to another.

Coloured vertices to define blending

Now I just needed to plug in some actual textures into these channels.  There are looooads of 'old paper' effect tutorials on the web and I just followed one of them to create a basic main texture, then darkened it for the edges.

Old paper effect on sphere.

Not too shabby, I kept the outline as I liked the look even if it doesn't quite fit with the old timey feel.  The slightly different shading comes from a random offset for each region, otherwise the mapping will be very neat and regular which is a useful quality to be able to turn on and off if needed.

Two meshes, land and water.

Right now I really only have two terrains, land and water, so I added a second set of textures to differentiate them.  To be clear each type of terrain is it's own mesh and material in Unity.   Changing the colour was nice and easy (for once), blue for water with some little waves drawn on.  So this weeks task is mapping different terrains on and associated textures, like desert, ice, forests etc.  As a 'look' for the game though I think it's a coming together ok.  Tweaking colours is easy enough.  Sketching  little trees and hills, very simple shapes like in the maps at the top, shouldn't be too outside my artistic ability.  Once I get some more terrain types in I think it will be a pretty cool world to explore.