I am participating in the Mid-Autumn Festival Creative Submission contest, please see: Mid-Autumn Festival Creative Submission Contest for details

Effect drawing (still drawing + DYNAMIC drawing)

Note: It is recommended to watch the GIF on the app side of the mobile phone, which is too compressed to be viewed on the PC browser

Although people have joys and sorrows, the moon waxes and wanes, this matter is always difficult to complete. I hope you will have loved ones around you when the Mid-Autumn Festival comes. Even if you are alone in a strange land, you should be safe and healthy after study and work, so that you can share this beautiful moonlight with him/her.

Ideas and corresponding shader code

First, the construction of the sphere

First we construct three-dimensional coordinates with the center of the screen as the origin

vec3 pos = vec3(fragCoord.xy - iResolution.xy / 2.0.0.0);
Copy the code

Then follow the formula for the sphere

float z = r * r - dot(pos, pos);
Copy the code

You get the result shown below

We need to distinguish the z coordinates of the inner and outer layers of the sphere in order to do the effect on the inner and outer layers of the sphere

    float z_in = z > 0. ? sqrt(z) : eps;
    float z_out = z < 0. ? sqrt(-z) : -eps;
Copy the code

The inner sphere looks like this (with some manipulation of the values for easy visualization) :

The schematic diagram of the function is as follows:

The outer effect of the ball is as follows:

The schematic diagram of the function is as follows:

Two, create the moon surface concave convex feeling

Given the z-coordinate range of the inner sphere, we can construct its normal vector

vec3 norm = vec3(0.);

/ / / @ note normals
if (z_in > eps)
    norm = normalize(vec3(pos.x, pos.y, z_in)); ///< the inner normal of the ball
Copy the code

The visualized effect is shown below

And then we’re going to further perturb it to create that “concave and convex” feeling of the lunar surface.

    float nx = fbm(vec3(norm.x + e, norm.y,   norm.z  )) * 0.5 + 0.5; // Normal permutation in the x direction
    float ny = fbm(vec3(norm.x,   norm.y + e, norm.z  )) * 0.5 + 0.5; // Normal permutation in the y direction
    float nz = fbm(vec3(norm.x,   norm.y,   norm.z + e)) * 0.5 + 0.5; // Normal permutation in the z direction
    norm = normalize(norm * vec3(nx, ny, nz));
Copy the code

Then we can get the normal effect as shown below

This may not be obvious, so let’s look at the effect of the difference between the normal vectors before and after the transformation (to visualize proper magnification) :

The effect is more obvious with lighting (still + GIF)

Clever use of FBM noise

In the section of creating “concave and convex feeling”, FBM superposition of Simplex noise was used to create concave and convex feeling on the lunar surface. Relevant functions are defined as follows:

float noise3D(vec3 p)
{
    ...
}

float simplex3D(vec3 p)
{
    ...
}

float fbm(vec3 p)
{
    ...
}
Copy the code

Due to its large amount of code and limited space, the detailed introduction of noise can be referred to my other article “ShaderJoy — The Beauty of Noise, Let’s Make Noise together [GLSL]”.

In addition to the above, this noise is also used to make the inherent color of the lunar surface

    /// @note further gets the texture of the lunar surface from the (disturbed) normals
    float n = 1.0 - (fbm(norm) * 0.5 + 0.5); 
Copy the code

The effects are as follows:

The atmosphere enshrouds the halo

This effect is achieved by inverting the z-coordinate range of the inner and outer spheres

    / / / @ note atmosphere
    float z_in_atm  = (r * in_outer)  / z_in - in_inner;   / / / < the inner layer
    float z_out_atm = (r * out_inner) / z_out - out_outer; / / / < outer layer
    z_in_atm = max(0.0, z_in_atm);
    z_out_atm = max(0.0, z_out_atm);
Copy the code

The intensity of atmospheric dense halo is defined as follows

// @note inner and outer atmosphere halo intensity
float in_inner = 0.2;
float in_outer = 0.2;
float out_inner = 0.2;
float out_outer = 0.4;
Copy the code

The inner and outer halo effects are as follows:

The schematic diagram of the function is as follows:

The schematic diagram of the function is as follows:

Illumination calculation

The illumination calculation here is quite simple, just a dot product of the normal line and the illumination direction to calculate the scattered light, but the inner and outer layers of the atmosphere need to be calculated once, and once is the illumination calculation of the sphere itself.

 /// @note Diffuse light calculation
 float diffuse = max(0.0.dot(norm, l));
 float diffuse_out = max(0.0.dot(norm_out, l) + 0.3); . . = n * diffuse ...Copy the code

To give the moon a “waxing and waxing” effect, we make the direction of the light change with time, i.e

    // @note ray 3D direction vector
    vec3 l = normalize(vec3(sin(time), sin(time * 0.5), (cos(time))));
Copy the code

Together, the illumination calculation code is as follows

    fragColor = vec4(vec3(n * diffuse +
                          z_in_atm * diffuse +
                          z_out_atm * diffuse_out), 1.0);
Copy the code

At this point, we have the initial effect

The complete code

#define time iTime

// @note inner and outer atmosphere halo intensity
float in_inner = 0.2;
float in_outer = 0.2;
float out_inner = 0.2;
float out_outer = 0.4;

/ / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- FBM noise related -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
float noise3D(vec3 p)
{
    return fract(sin(dot(p, vec3(12.9898.78.233.128.852))) * 43758.5453) * 2.0 - 1.0;
}

float simplex3D(vec3 p)
{
    float f3 = 1.0 / 3.0;
    float s = (p.x + p.y + p.z) * f3;
    int i = int(floor(p.x + s));
    int j = int(floor(p.y + s));
    int k = int(floor(p.z + s));

    float g3 = 1.0 / 6.0;
    float t = float((i + j + k)) * g3;
    float x0 = float(i) - t;
    float y0 = float(j) - t;
    float z0 = float(k) - t;
    x0 = p.x - x0;
    y0 = p.y - y0;
    z0 = p.z - z0;
    int i1, j1, k1;
    int i2, j2, k2;
    if (x0 >= y0)
    {
        if      (y0 >= z0)
        {
            i1 = 1;    // X Y Z order
            j1 = 0;
            k1 = 0;
            i2 = 1;
            j2 = 1;
            k2 = 0;
        }
        else if (x0 >= z0)
        {
            i1 = 1;    // X Z Y order
            j1 = 0;
            k1 = 0;
            i2 = 1;
            j2 = 0;
            k2 = 1;
        }
        else
        {
            i1 = 0;    // Z X Z order
            j1 = 0;
            k1 = 1;
            i2 = 1;
            j2 = 0;
            k2 = 1; }}else
    {
        if      (y0 < z0)
        {
            i1 = 0;    // Z Y X order
            j1 = 0;
            k1 = 1;
            i2 = 0;
            j2 = 1;
            k2 = 1;
        }
        else if (x0 < z0)
        {
            i1 = 0;    // Y Z X order
            j1 = 1;
            k1 = 0;
            i2 = 0;
            j2 = 1;
            k2 = 1;
        }
        else
        {
            i1 = 0;    // Y X Z order
            j1 = 1;
            k1 = 0;
            i2 = 1;
            j2 = 1;
            k2 = 0; }}float x1 = x0 - float(i1) + g3;
    float y1 = y0 - float(j1) + g3;
    float z1 = z0 - float(k1) + g3;
    float x2 = x0 - float(i2) + 2.0 * g3;
    float y2 = y0 - float(j2) + 2.0 * g3;
    float z2 = z0 - float(k2) + 2.0 * g3;
    float x3 = x0 - 1.0 + 3.0 * g3;
    float y3 = y0 - 1.0 + 3.0 * g3;
    float z3 = z0 - 1.0 + 3.0 * g3;
    vec3 ijk0 = vec3(i, j, k);
    vec3 ijk1 = vec3(i + i1, j + j1, k + k1);
    vec3 ijk2 = vec3(i + i2, j + j2, k + k2);
    vec3 ijk3 = vec3(i + 1, j + 1, k + 1);
    vec3 gr0 = normalize(vec3(noise3D(ijk0), noise3D(ijk0 * 2.01), noise3D(ijk0 * 2.02)));
    vec3 gr1 = normalize(vec3(noise3D(ijk1), noise3D(ijk1 * 2.01), noise3D(ijk1 * 2.02)));
    vec3 gr2 = normalize(vec3(noise3D(ijk2), noise3D(ijk2 * 2.01), noise3D(ijk2 * 2.02)));
    vec3 gr3 = normalize(vec3(noise3D(ijk3), noise3D(ijk3 * 2.01), noise3D(ijk3 * 2.02)));
    float n0 = 0.0;
    float n1 = 0.0;
    float n2 = 0.0;
    float n3 = 0.0;
    float t0 = 0.5 - x0 * x0 - y0 * y0 - z0 * z0;
    if (t0 >= 0.0)
    {
        t0 *= t0;
        n0 = t0 * t0 * dot(gr0, vec3(x0, y0, z0));
    }
    float t1 = 0.5 - x1 * x1 - y1 * y1 - z1 * z1;
    if (t1 >= 0.0)
    {
        t1 *= t1;
        n1 = t1 * t1 * dot(gr1, vec3(x1, y1, z1));
    }
    float t2 = 0.5 - x2 * x2 - y2 * y2 - z2 * z2;
    if (t2 >= 0.0)
    {
        t2 *= t2;
        n2 = t2 * t2 * dot(gr2, vec3(x2, y2, z2));
    }
    float t3 = 0.5 - x3 * x3 - y3 * y3 - z3 * z3;
    if (t3 >= 0.0)
    {
        t3 *= t3;
        n3 = t3 * t3 * dot(gr3, vec3(x3, y3, z3));
    }
    return 96.0 * (n0 + n1 + n2 + n3);
}

float fbm(vec3 p)
{
    float f;
    f  = 0.50000 * (simplex3D( p )); p = p * 2.01;
    f += 0.25000 * (simplex3D( p )); p = p * 2.02;
    f += 0.12500 * (simplex3D( p )); p = p * 2.03;
    f += 0.06250 * (simplex3D( p )); p = p * 2.04;
    f += 0.03125 * (simplex3D( p )); p = p * 2.05;
    f += 0.015625 * (simplex3D( p ));
    return f;
}
/ / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- FBM noise related -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

/// @note Lunar radius
/// r The default value is iResolution. Y / 3.0
#iUniform float r = 170. in {0., 512.}
/// @note controls the concave and convex sensation of the lunar surface
#iUniform float E = 0.05 in {0.,.6}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec3 pos = vec3(fragCoord.xy - iResolution.xy / 2.0.0.0); // Start at the center of the screen

    // @note ray 3D direction vector
    vec3 l = normalize(vec3(sin(time), sin(time * 0.5), (cos(time))));

    /// @note sphere and distinguish between inside and outside
    float eps = 1e-3;
    float z = r * r - dot(pos, pos);
    float z_in = z > 0. ? sqrt(z) : eps;
    float z_out = -z > 0. ? sqrt(-z) : -eps;

    / / / @ note normals
    vec3 norm = vec3(0.);
    vec3 norm_out = vec3(0.);
    if (z_in > eps)
        norm = normalize(vec3(pos.x, pos.y, z_in)); ///< normals on the ball

    // if (z_out > sqrt(e))
    norm_out = normalize(vec3(pos.x, pos.y, z_out)); ///< normal outside the ball

    /// @note creates a concave and convex impression
    float nx = fbm(vec3(norm.x + e, norm.y,   norm.z  )) * 0.5 + 0.5; // Normal permutation in the x direction
    float ny = fbm(vec3(norm.x,   norm.y + e, norm.z  )) * 0.5 + 0.5; // Normal permutation in the y direction
    float nz = fbm(vec3(norm.x,   norm.y,   norm.z + e)) * 0.5 + 0.5; // Normal permutation in the z direction
    norm = normalize(norm * vec3(nx, ny, nz));

    /// @note Noise texture based on normals
    float n = 1.0 - (fbm(norm) * 0.5 + 0.5); // noise for every pixel in planet

    / / / @ note atmosphere
    float z_in_atm  = (r * in_outer)  / z_in - in_inner;   // inner atmos
    float z_out_atm = (r * out_inner) / z_out - out_outer; // outer atmos
    z_in_atm = max(0.0, z_in_atm);
    z_out_atm = max(0.0, z_out_atm);

    // @note Diffuse calculation
    float diffuse = max(0.0.dot(norm, l));
    float diffuse_out = max(0.0.dot(norm_out, l) + 0.3); 

    /// @note overall illumination
    fragColor = vec4(vec3(n * diffuse +
                          z_in_atm * diffuse +
                          z_out_atm * diffuse_out), 1.0);

}
Copy the code