For my hexagon planet, I want to modify the costal tiles with some custom shapes, so that there isn’t just a boring and abrupt separation between land and see. I found it easiest to explain with the following illustration:

So the idea is to have two UV maps per tile. One for color, roughness and bump maps. The other for the shape/mask map (black and white tile)
Is that possible and how would I go about doing it? Is there even a texture masking option anywhere, or does this have to be done with a shader?
You could do it with multiple materials and masking, but injecting some custom functionality into a shader is probably more efficient.
To do it with masking, you could use you mask texture as an opacity map.. then use the inverse of it to mask the other tile..
No you don’t have to use a shader. I would suggest using an html canvas for this task.
You could create an image or texture with all of your hexagons adjacent to each other - like a 2D terrain map.. You can then load that image and save it to the html canvas. You can then extract sections out of the canvas using the getImageData command. The sections you extract can be square and should map properly to your hexagon tile objects.
You could use this same approach for all your maps: color, roughness and bump.
p.s. I had not seen the discussion regarding mapping hexagonal tiles on a sphere. I have seen hexagonal maps used for 2D maps, like war games. But I did not realize that they could be mapped to a sphere. Pretty cool!
So essentially one single large global texture for the entire planet, if I’m understanding correctly? It will be quite large. There are about 52.000+ tiles and each hexagon texture size is about 60x66 pixels. But I will give it a try 
I have started what seems like a very, very long journey into shader use, so maybe that is where I will end up. However, if I use an opacity texture map, will I have to add two materials for each tile, since there needs to always be an ocean tile on the bottom? And performance wise, what will the impact be when it comes to shader vs multiple textures? The planet is a mergedgeometry with about 52.000+ tiles.
Ouch! If you are going to give each tile it’s own unique texture, you will probably have to create several images.
The maximum size of the canvas varies by operating system and browser. For mobile, the max appears to be 4096x4096. For Chrome, the max size is 16384x16384.
I would experiment with smaller sphere sizes first - like the one in your example.
If you need help with the canvas code, I could help with that. It will mostly involve a lot of looping.
You could create a shader to do this, but I am not sure if it would be that much faster than using the canvas. Shaders are more helpful if you are performing tasks during runtime.
Using canvas means generating unique combinations for every pair… which is a waste of memory, if instead you can just use the hardware to composite arbitrary pieces.
Some 11 years ago I wanted to have an image scroll through a screen of hexagons. Instead of dealing with individual hexagons, the screen was just a single object with a texture of the image. The hexagons were just a net over the screen. So, with a good texture of a grid you would not need to worry about coloring individual tiles.
The image below is not from that model, I just made it to illustrate the idea:
I was about to agree. But when I run the math, I am not sure.
If you have hexagon images with vertical sides of 10 pixels, the area for each pixel would have an area of about 260 pixels (10^2 * 2.598). If you have 52k tiles with unique images, you would need an area of 13.520 million pixels, which sounds big. But this is number of pixels could easily fit in a single 4096x4096 image.
(As a cross-check I cut a section out of PavelBoytchev’s map and computed the area required. The hexagons have vertical sides of about 30 pixels. Both the computed and measured area were about the same.)
Packing the hex textures into a single image efficiently will involve some challenges. For example, hex #0 would be the north pole. Hexes #1-6 would be the next lateral level down. Hexes 7-18 would be the next lateral level down. Etc. This image would look pretty confusing. You would have to add a border area of 1 pixel to the non-vertical areas because, in this compressed image, the hexes would not be aligned vertically.
Creating this kind of image by hand would be impossible. So you would need a program that could extract images from a visible map (Mercator?) and save them on a single image.
Painful, but possible.
But I do agree, that it would be nice to make do with fewer textures. For example, oceans could generally be represented with a single blue image (70% of the earth is covered by water). The same is probably true for the basic land biomes, like grasslands, desert, snow, etc. And it would require only a relatively small number of special images to create divisions between all these biomes.
Regarding manthrax’s suggestion of using computer-generated textures, I should note that I used that kind of approach when creating a “farmland” sandbox for my flight demos.
I had the computer draw shapes (e.g. lines of crops) into an array and then converted them to images. For nearby squares, I used a single image. For more distant squares, I used the canvas to create a 3x3 image that included a specified arrangement of the images. For even more distant squares, I used the canvas to create a 3x3 image of the 3x3 image (a 9x9 image).
Because the closest squares include images which are repeated several times, I have gotten the resolution down to a pixel for every couple of inches. At the same time, by using the bigger squares, I have created a display that stretches out almost 50 miles. Although I use less than 10 different field-types, I have created a display which looks random.
I realize that you are using hexagons, not squares, the same principles could apply (after all, at the heart of a hexagon is a rectangle). And you may not need that level of resolution, in which case you don’t need to repeat the textures.
Note that because the fine detail disappears at altitude, I have overlaid textures that make things visible from higher altitudes. (I did the same thing with my animated ocean sandbox because the waves tend to disappear above a certain distance, leaving you with a monotonous-looking ocean.)
Is each hexagon its own geometry, and if yes, how did you give it a border? I can’t seem to find a method for doing that for my merged geometries. I might be looking using the wrong terminology.
A couple of quick questions:
-
How big do you want to make the textures? I computed that, if you do a 1 to 1 mapping, you could fit all your hexagons with vertical side of 10 on in a single 4192 image. Is that 10 size big enough, or did you want something more detailed? If you want something textures, another option would be to not do a 1 to 1 mapping, but to create a smaller set of images which could be assigned to hexagons.
-
Looking at your hex sphere, are you able to create this using properly proportioned hexagons? Or do you have to modify their shape slightly to make them work on a curve?
Regarding borders, I was thinking of giving each hexagon a little additional size, just in case the diagonals needed an extra pixel or so. To explain visually, the image by PavelBoytchev has black borders. I would eliminate those borders. I would increase the size of the each hexagon image to include a 1 pixel border around the image.
When you extract the image for each hexagon, the shape will be a square. The corners will include superfluous pixels. But those pixels will not appear when you map the texture onto the hexagon (since they are mapped into thin air). Only the pixels that fit on the hexagon will be mapped.
I will see if I can create a quick example.

You could use a similar technique to this, just a hexagonal border on the uv2 (light Map uv) of each hexagon…
I wrote it a long time ago and kinda forgot.. but i think it’s a “splat map”…
a texture with rgb values encoding the terrain type..
and that splatmap is filled with perlin noise.. and the noise is also used to raise the vertices.
The source is all unobfuscated. you can view it in the debugger…
1 - Right now each “subtexture” per hexagon (which is made of 4 faces) is 60 pixels wide and 66 pixels tall. I am only expecting something like 15 different terrain textures, and since each hexagon has its own UV-mapping, it’s pretty easy to just adjust the UV’s to cover whatever terrain it is set to.
2 - They are slightly warped, but it’s fine. And with the UV-mapping onto the texture, it actually looks fine. So the issue is 100% the coastal “masking”, but I would also like to have different types of terrain “bleed” a little into the neighburing tiles, so that the border between something like desert and forest isn’t a super hard and sharp dividing line.
Later down the line, mountains would be cool to model, but that is a nice-to-have.
Here is what I’ve got so far. I am trying to display the first 5 hexagons from the big texture on the 5 individual hexagon shapes. I have not done this before, but I have heard that it can be done and I always learn something from these new challenges.
As you can see, there two problems with the textures displayed in the hexagons:
- It seems to be displaying the same texture. I probably need to clear out the canvas. Or perhaps something to force the program to wait until each new texture is loaded.
- The textures appear rotated by either 30, 60 or 90 degrees (it is hard to tell with these particular hex materials). I thought it might be because I was using a cylinder geometry to create the hexagon and rotating that geometry. But I am only rotating the cylinder upright by 90 degrees (and not rotating the cylinder does not make a difference.). I will add some marking to the first 5 hexagons in the big texture so that I can see how much it is rotated.
Any suggestions are much appreciated.
UPDATE
I have revised the program to do what it appears that you would want - to store the texture variations and to create a reference so that the correct texture is associated with each hex.
I also created a new text texture. This shows that the materials are rotated 90 degrees and that only the last material is being displayed.
I realize that what I am doing is slightly different than what you want to do - I have put 4 color textures in the big image while you want to have a color, roughness and bump map. But the same principles should apply.
@phil_crowther If you console.log(hexTxt)…
let hexTxt = new THREE.CanvasTexture(canvas);
You’ll see that the CanvasTexture holds a reference to it’s image (canvas), when using a singular canvas, the canvas updates the images for all of the CanvasTextures referencing it, iirc each of these images need to be a separate canvas element, this can work well for a few hundred images but not sure how scalable this would be given the context of 52k tiles…
Yes, my latest variation shows that is exactly what is happening.
Try creating a canvas element for each loop of hexNum in makeHexagons() instead of using a global canvas, drawing to the ctx of each… I’m unsure as to the potential limitations of doing this with large datasets, it’s worked well for me with a few hundred in the past…
PS: rotating the geometry with geometry.rotateX (instead of rotating a mesh) will likely rotate the uvs also, as uvs are an attribute of geometry…