Skip to content

Instantly share code, notes, and snippets.

@ItsVerday
Last active April 25, 2020 16:56
Show Gist options
  • Save ItsVerday/256f2b2a9f323263b88a0517bf1372f0 to your computer and use it in GitHub Desktop.
Save ItsVerday/256f2b2a9f323263b88a0517bf1372f0 to your computer and use it in GitHub Desktop.
Clover Noise 2D GLSL (Optimized v3, commented)
// How far points in grid cells can go from the center of the grid cell. Setting this to a value above 0.5 will make the noise discontinuous, and there will be visible seams.
// It is reccomended to leave this at 0.3.
const float CLOVER_NOISE_2D_POINT_SPREAD = .3;
// The value of pi. Used in the offset function.
const float CLOVER_NOISE_2D_PI = radians(180.);
// Hashes a 2D vector into a point. It isn't 100% random, but it's good enough for clover noise to appear random.
float clover_noise_2d_hash(vec2 p) {
return fract(1e4 * sin(17.0 * p.x + p.y * 0.1) * (0.1 + abs(sin(p.y * 13.0 + p.x))));
}
// Hashes a 2D vector into a new 2D vector with coordinates between (0.5 - CLOVER_NOISE_2D_POINT_SPREAD) and (0.5 + CLOVER_NOISE_2D_POINT_SPREAD).
// This vector is then added to the original. The CLOVER_NOISE_2D_POINT_SPREAD constant affects the "spread" of these points.
// This function takes in a pre-hashed float to avoid nested function calls, which can slow compilers down.
vec2 clover_noise_2d_offset(vec2 p, float hash) {
// The offset point has coordinates between 0 and 1. It is generated by finding a point on one of many circles, based on the hash value.
// This is done so that only 1 hash value is required, reducing the amount of hash function calls, which in turn speeds up the algorithm.
vec2 offset = vec2(sin(hash * PI * 100.), cos(hash * PI * 100.)) * floor(hash * 50. + 1.) * .01 + .5;
// Take the offset, scale it, and add it to the original point.
return p + offset * CLOVER_NOISE_2D_POINT_SPREAD * 2. + .5 - CLOVER_NOISE_2D_POINT_SPREAD;
}
// The main clover noise function. All of the main logic, besides hashing, is somewhere in this function.
float clover_noise_2d(vec2 p) {
// Round all coordinates of the point down, in order to find the corners of the quad the input point is in.
vec2 p_floor = floor(p);
// The random point for the grid cell containing the input point.
// This is an important value, as it is garunteed to be a vertex of the quad containing the input point.
vec2 c_11 = clover_noise_2d_offset(p_floor, clover_noise_2d_hash(p_floor));
// The random point for the grid cell to the left of the grid cell containing the input point.
vec2 c_10 = p_floor + vec2(0, -1);
c_10 = clover_noise_2d_offset(c_10, clover_noise_2d_hash(c_10));
// The random point for the grid cell to the right of the grid cell containing the input point.
vec2 c_12 = p_floor + vec2(0, 1);
c_12 = clover_noise_2d_offset(c_12, clover_noise_2d_hash(c_12));
// The random point for the grid cell above the grid cell containing the input point.
vec2 c_01 = p_floor + vec2(-1, 0);
c_01 = clover_noise_2d_offset(c_01, clover_noise_2d_hash(c_01));
// The random point for the grid cell below the grid cell containing the input point.
vec2 c_21 = p_floor + vec2(1, 0);
c_21 = clover_noise_2d_offset(c_21, clover_noise_2d_hash(c_21));
// These values are used to simplify the calculation of the quad boundaries.
vec2 d_p_c11 = vec2(p.y - c_11.y, p.x - c_11.x);
vec2 m_p_c11 = d_p_c11 * c_11;
// Finds the boundary between c_11 (the point in the main grid cell) and c_01 (the point in the left grid cell).
vec2 side_nx = m_p_c11 - d_p_c11 * c_01;
// Finds the boundary between c_11 (the point in the main grid cell) and c_21 (the point in the right grid cell).
vec2 side_px = m_p_c11 - d_p_c11 * c_21;
// Initializes the 3 remaining corners of the quad as empty variables. There is no "b" because it always has the value of c_11.
vec2 a, c, d;
// Checks whether the point lies in a quad above or below the center point. This is calculated with the boundaries above.
if ((side_nx.y - side_nx.x < 0. && p.x < c_11.x) || (side_px.y - side_px.x > 0. && p.x >= c_11.x)) {
// The point is below the center point. We need to calculate the vertical boundary below the center point.
// Finds the boundary between c_11 (the point in the main grid cell) and c_12 (the point in the bottom grid cell).
vec2 side_py = m_p_c11 - d_p_c11 * c_12;
// Are we to the right or left of the boundary?
if (side_py.y - side_py.x > 0.) {
// We are to the right of the boundary. Assign a and c to the correct corner points.
a = c_12;
c = c_01;
// For now, d set to this vector to represent which grid cell corner D is in. This is done to reduce calls to the offset function.
d = vec2(-1, 1);
} else {
// We are to the left of the boundary. Assign a and c to the correct corner points.
a = c_21;
c = c_12;
// d is set to vec2(1, 1) to represent that corner D is the point in the grid cell below and to the right of the main grid cell.
d = vec2(1, 1);
}
} else {
// The point is above the center point. We need to calculate the vertical boundary above the center point.
// Finds the boundary between c_11 (the point in the main grid cell) and c_10 (the point in the top grid cell).
vec2 side_ny = m_p_c11 - d_p_c11 * c_10;
// Are we to the right or left of the boundary?
if (side_ny.y - side_ny.x > 0.) {
// We are to the left of the boundary. Assign a and c to the correct corner points.
a = c_10;
c = c_21;
// d is set to vec2(1, -1) to represent that corner D is the point in the grid cell above and to the right of the main grid cell.
d = vec2(1, -1);
} else {
// We are to the left of the boundary. Assign a and c to the correct corner points.
a = c_01;
c = c_10;
// d is set to vec2(-1, -1) to represent that corner D is the point in the grid cell above and to the left of the main grid cell.
d = vec2(-1, -1);
}
}
// Find corner D from the previous value of d described above.
d = clover_noise_2d_offset(p_floor + d, clover_noise_2d_hash(p_floor + d));
// We now have all 4 corners of the quad containing the input point! We can now move on to finding the triangle containing the input point.
// We will assume that a, c, and d are the corners of the triangle for now. If we are wrong, these values will be updated later on.
vec2 f = a;
vec2 g = c;
vec2 h = d;
// Find the differences between a and c, as well as b and d. Used to figure out the orientation of the triangle.
vec2 ac = a - c;
vec2 bd = c_11 - d;
// Checks which orientation the triangle should be in, based on the distance of opposite corners to each other.
if (ac.x * ac.x + ac.y * ac.y < bd.x * bd.x + bd.y * bd.y) {
// Find the difference between the input point and a, to be used directly below.
vec2 pa = p - a;
// Check if we need to update h to a different corner. If this condition fails, our assumption from earlier is correct, and no values will be updated.
if (pa.x * ac.y - pa.y * ac.x > 0.) {
// If we make it here, we need to update h.
h = c_11;
}
} else {
// Find the difference between the input point and b, to be used directly below
vec2 pb = p - c_11;
// We either need to update f or g, depending on where the point is.
if (pb.x * bd.y - pb.y * bd.x > 0.) {
// Update f to the correct corner.
f = c_11;
} else {
// Update g to the correct corner.
g = c_11;
}
}
// We now have the corners of the triangle containing the input point. We need to calculate where the input point is in the triangle, or the barycentric coordinates, in order to find a smooth value.
// Subtract f from g, h, and the input point, in order to speed up calculations.
vec2 bc_v0 = g - f;
vec2 bc_v1 = h - f;
vec2 bc_v2 = p - f;
// Find the value to multiply v and w by below. Instead of directly dividing by denominator, we take its reciprocal, and multiply it with v and w.
// This is done to reduce the number of time we divide, as division is much slower than multiplication.
float den = 1. / (bc_v0.x * bc_v1.y - bc_v1.x * bc_v0.y);
// Find v, w, and u, the barycentric coordinates of p inside fgh, in order to smoothly transition between the corners.
float v = (bc_v2.x * bc_v1.y - bc_v1.x * bc_v2.y) * den;
float w = (bc_v0.x * bc_v2.y - bc_v2.x * bc_v0.y) * den;
float u = 1. - v - w;
// We cube v, w, and u in order to smooth out the gradient.
v = v * v * v;
w = w * w * w;
u = u * u * u;
// In order for the weighted average to work properly, v, w, and u must add up to 1. Since we just cubed these values, they may not add up to 1 anymore.
// To fix this, we divide all of them by the sum of v, w, and u. We set the value of s to the reciprocal of this sum, in order to reduce the amount of division operations.
// This is because division is much slower than multiplication.
float s = 1. / (u + v + w);
// Multiply all of the values by the reciprocal of their sum, which is the same as dividing by their sum.
v *= s;
w *= s;
u *= s;
// We just need the values at each of the 3 triangle corners. This is done by hashing the coordinates.
float fv = clover_noise_2d_hash(f);
float gv = clover_noise_2d_hash(g);
float hv = clover_noise_2d_hash(h);
// We are almost done! We just need to take the weighted average of the values above, based on u, v, and w. This will be our final noise value!
return u * fv + v * gv + w * hv;
// We are done! The final noise value has been returned.
}
// Fractal clover noise is calculated by layering standard clover noise.
float fractal_clover_noise_2d(vec2 p, int iterations) {
float total = 0.;
float divide = 0.;
float scale = 1.;
float invScale = 1.;
for (int iter = 0; iter < 10; iter++) {
if (iter >= iterations) {
break;
}
total += clover_noise_2d(p * invScale) * scale;
divide += scale;
scale *= .4;
invScale *= 2.5;
}
return total / divide;
}
// Curl clover noise is calculated by finding the value at 3 different points, turning them into a 2D vector, and normalizing.
vec2 curl_clover_noise_2d(vec2 p) {
const float DX = 0.01;
float v = clover_noise_2d(p);
float x = clover_noise_2d(p + vec2(DX, 0.));
float y = clover_noise_2d(p + vec2(0., DX));
return normalize(vec2(v - x, v - y));
}
// Fractal curl clover noise is calculated the same as normal curl clover noise, but we use fractal clover noise instead of standard clover noise.
vec2 curl_fractal_clover_noise_2d(vec2 p, int iterations) {
const float DX = 0.01;
float v = fractal_clover_noise_2d(p, iterations);
float x = fractal_clover_noise_2d(p + vec2(DX, 0.), iterations);
float y = fractal_clover_noise_2d(p + vec2(0., DX), iterations);
return normalize(vec2(v - x, v - y));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment