After a little bit of bathroom brainstorming, I got this eureka solution for moon phase, with rather realistic moon curves. No circular overlay on the moon texture, so you can do better looking waxing crescent or gibbous shapes.
The general idea is using color level manipulation to “swipe” the revealed mask along a gradient pattern. But since we’re looking for more believable phase, you can’t just use generic linear gradient spanning from 0 to 1 and call it a phase. The gradient map is linear vertically, but we need to add spherical curve to the gradient map, so that we can get that realistic phase. My preferred method is splitting a circle into two arc, each for white and black side respectively, and then blend them to make the gradient map. Or just grab this very gradient map I made with that method in CorelDRAW.
Okay, we get this curved gradient map, now what?
First order of business, how can we use this gradient map for Unreal to “swipe” along the 1 value? The answer is simple: Color Level. You can put following gradient map into GIMP or Photoshop, and play around with Level tool to get that moon phase curves.
After you get the general idea of how this Color Level strat works, time to put this thing into Unreal and make that curved swiping look.
What is going on here?
We make a float variable that we can manipulate from instanced material, ranging from 0 to 1. We can utilize this exposed scalar parameter to get the moon phase working in-game.
Then, we put that value to an If node, so we can do the second half of the phase without putting another gradient map. The If node goes like this: If the phase is < 50%, we use the inverted gradient map where the 1.0 value starts from the right arc, doing the first half of the phase; if the phase is 50%, we simply reveal the entire moon; if the phase is > 50%, we use the gradient map as is where the 1.0 value starts from the left arc, doing the second half of the phase.
After the If node’s out of the way, we feed the result to 3PointLevels function node’s Texture (S) input pin. For the New Black Value (S), New Middle Value (S), and the New White Value (S), I found that those -5, 5, 1 constants gives me the desired result, but feel free to experiment with different constant values.
Now, for the Middle Point (S), this input pin is where we do the whole “swiping” along phase thing. Bring the Phase scalar parameter we just made, multiply it by 4, then subtract it by 2, and make it an absolute value. The reason I do that equation is that value between 0.0 to 2.0 is where the moon phase is visible. Anything below 0.0 or above 2.0 will bring the entire map to black. The equation also creates linear curve for the value; where the middle point value is 0.0 for 0% and 100% phase, and 1.0 for 50% phase.
You can put Clamp node after 3PointLevels for safety measures.
And that’s the whole function to make curved moon phase. Note that I use UI material domain in the screenshot - it’s just for testing purposes, because UI material domain have faster shader compiling time.
Where can I go from this function?
You can just put the whole function into your skybox material, and adding a MF_SkyboxImage material function for the gradient map’s UV, and you can multiply it with your moon texture in your sky material. Although I have yet to find the best practice for this without masking out the texture details entirely.
You can also put a function in your skysphere BP to update the Phase scalar parameter we exposed earlier, and cycle through 0.0 to 1.0 in 30 in-game days, to create more depth to your day/night cycle.
You can also improve the function, the color level values, and/or the gradient map to get even better looking moon phase!