Simple procedural skybox with a moon
We’re making a game and were looking for some references. This one by Mark Kirkpatrick seemed very interesting
I have tried to recreate that in Unity and here’s what we’ve got:
I used standard procedural skybox shader and this set of shaders (https://github.com/keijiro/UnitySkyboxShaders) as a reference, but simplified it a lot. Here’s how it works.
We have top and bottom colors and we need to blend them together. Skybox shaders are interesting, there’s a set of vertices that are rendered using texture coordinates and positions. We will use texture coordinates in our case. Here’s a vertex shader:
We just pass texture coordinates without any changes. As you can see, those texture coordinates are stored in a 3D vector. That vector represents a unit vector of direction towards the point on a skybox (much like a directional light rotation _WorldSpaceLightPos0 used in shaders. So Y component of that vector represents vertical direction of that point and we can use that to blend two colors together. All components change from -1 to 1 (Y component is -1 at the bottom and 1 at the top). If we normalize the Y component and blend our two colors together, we get a very smooth transition:
So we need a way to move the border closer to the center and squish it a bit.
We subtract current Y component from 1 and take a minimum of the subtraction and 1.0. As values are less than 0 after we cross a horizontal plane, subtraction absolute values will be more than 1, and this is where we will blend the Ground color. _Exponent parameter allows to tweak “hardness” of the gradient transition. This is how it looks with _Exponent = 15:
The moon is a bit tricky and got me digging a lot of Unity shader sources. The idea itself is simple though.
- Set our moon center as a directional unit vector (just like the directional _WorldSpaceLightPos0 is stored)
- Find distance between the moon center directional vector and a skybox directional vector (our float3 textureCoords)
- Use that distance to calculate falloff of the moon circle using some function that allows to tweak the hardness of the moon edges.
Here’s a modified calcSunSpot function from the standard procedural skybox:
5 and 6 lines calculate distance between two directional vectors (how far is the fragment from the center of the moon)
7 line calculates the circular form of our moon. We take advantage of the smoothstep function clamping abilities outside of the interpolation range. Subtracting it from 1 gives us bigger values in the center (whites)
The 8 line squishes our gradient transition. You can imitate it’s mathematical logic in Photoshop using Curves adjustments:
We need to blend our gradient with the moon now. We will also need our ground fog to affect the moon.
Line 9: calculating and coloring our moon (sun) spot
Line 11: blending the ground color with the moon
Line 13: adding the top (sky) color
This is what it looks like in the end (I changed the color of the moon from white to light cyan):
One thing to mention — _SunPosition should be a unit vector (with a magnitude of 1). It’s tricky to set it by hand, so you can either use some helper methods to calculate a unit vector from rotation, or use one directional light on the scene just for its rotation (in this case either your lighting will depend on it’s rotation or you’ll need to use unlit shaders everywhere).
Here’s a full shader source. Create a new material and set it as skybox inside Unity’s lighting preferences window — https://github.com/KumoKairo/Procedural-Skybox/blob/master/GradientMoonSkybox.shader