Pixel to Hexagon Material

Hello everyone!

I’ve been trying to rebuild an old project of mine, and have started working on a material for use with hexagonal grid systems. The material is intended to take a texture, and convert each pixel in that texture into a hexagon of arbitrary size. I’m largely there, but something I’m doing is slightly off and the results speak for themselves. The top-left is what my hexagonal coordinates look like, the top-right is what a normal texture coordinate looks like, the bottom-left is how my test texture is being shown with those hex coordinates, and the bottom-right is what it looks like with normal coordinates.

As you can see, the hexagonal shapes are certainly there, but actual coordinates look to be wrong. I’m not quite sure where I’ve made the mistake. I’ve used Amit Patel’s excellent site as a reference: http://www.redblobgames.com/grids/hexagons/#pixel-to-hex

The material looks like this:

The custom node contains the following:


if(dX > dY && dX > dZ) return float3(-rY-rZ,rY,rZ); else if(dY > dZ) return float3(rX,-rX-rZ,rZ); else return float3(rX,rY,-rX-rY);

Here’s a version of the material that can be pasted into the material editor (I’m on 4.7):

If anyone can see where I’ve gone wrong, it’d be much appreciated!

Oh wow, my tile system needs this!.. unfortunately, it looks awfully expensive to perform the basic operation, and I’m still in the process of beefing up my system with POM and iterative parallax.

I wonder if there’s a more basic way to rearrange vectors without masking/appending them. Everything else you made just flew way over my head.

It is indeed intended for a tile system; it’s an important core component that’s flexible and operates as the backbone to rendering - I’ll be releasing a public version when I get it up and running, and finish my prototype.

At 75 instructions, the operation is indeed not overly cheap, and part of that is because half the shader is in a custom node. I’m willing to bet that once working it can be optimised, but let’s also remember that many of Epic’s basic sample materials weigh in at 150-200 instructions too :slight_smile:

o.o 83/33 is antonym to “expensive” nowadays

Well, yeah, but that’s insane. There are diminishing returns on overly expensive materials. After all, you’re “only” rendering in 1080p.

It seems you’re simply performing “if” commands in the custom node. You can use the “if” material node for that, multiply the values you need by -1, and subtract. Let me see if I can try this…

If you want a dynamic sun, or a sun with specular highlights, then all your materials under that sun will double in complexity. If you want particle effects that emit light, if you want stationary or dynamic lights, if you want translucent objects… all this stuff adds up and literally multiplies the cost of your material. If you can save a few instructions with minimal impact on high-quality results, do it. Unless you have a GTX something-80, do it.

I didn’t use the if nodes, on account of it being a lot of awkward node setup, but it is possible to do without. The first time I made a material that did this, I did it without ‘ifs’ at all, but the cost would have been higher.

The cost of this technique will always be the same; it’s independent of whatever else is going on in the material.

I read the blog and studied your implementation in UE4. I noticed you compressed the square root and multiples of 3 into a single constant. It made me feel happy. Unfortunately I can’t really comment further. But I would definitely be interested in any updates on this code :slight_smile:

Looks like a scale problem to me, the values of the coordinates in your hex output ramp up to 1.0 and above too fast.

I tried simply dividing the coordinates by the scale parameter before sampling, and it looks correct-ish to me:

However I didn’t really look into the math, so it might be off.

Thanks you Melak!

You’re right that it’s a scale problem. With a bit of fiddling, it looks like it’s working on my end.

Now to continue the accompanying math library and tech :smiley:

I’m not sure if this helps, but after my journey through the 7 chakras while trying to integrate POM/iterative parallax, I found out that dividing the result by the number of pixels in the height map being used will give you a similar result that you can scale more consistently. And adding a texture coordinate is a must for correct scaling. So, POM result / (map height * map width) = reliable scaling result. Pom result + texture coordinate = most basic correct scaling result. Unfortunately, this didn’t help the hexagon formula, but it’s another possibility to look at. shrugs

This is very cool indeed! I’ve been working on making 2D grids using materials for the last few days, but never considered doing something like this. I also followed a lot of Amit’s stuff when making my own grids, but somehow missed the pixel-to-grid stuff. I’ll look into it and see if I can pprovide you with any useful feedback.

A material with only a dif & normal i got static 96, dynamic 65, vertex 33, samplers 5/16 and the minimum is static 90, dynamic 59, vertex 33, samplers 4/16.

I think this isn’t much expensive.