Previous history repost about Perlin noise was so inspiring that it was hard to do it in a proper way, because all I was thinking was to try to use that knowledge to make my old solar system better.
In this article I would try to make a texture for a Jupiter!
Setting up glsl sandbox
It looks like I would play a lot with shaders, so it is a wise idea to generalize the code.
gl_FragColor = vec4(uv.y/2.0+0.5, sin(uTime)/2.0+0.5, uv.x/2.0+0.5, 1.0);
Yep, it's handy now, just like adding a pixelart.
Let's make the basic noise. Perlin is not a great option for GPU, because it requires some pre-computation and sending that Vectors to the shader. There is also a simplex
noise, but it is patented, and now Nokia (microsoft) holds the patent. Community made openSimplexNoise to overcome mentioned pitfalls.
// CC0 license https://creativecommons.org/share-your-work/public-domain/cc0/
/////////////// K.jpg's OpenSimplex2, domain-warping vector-output variant ///////////////
////////////////////// Output: vec4(warpX, warpY, warpZ, fullValue) //////////////////////
vec4 permute(vec4 t) {
return t * (t * 34.0 + 257.0);
}
// Gradient set is a cuboctahedron
vec3 grad(float hash) {
vec2 g2 = mod(trunc(hash * vec2(1.0, 0.5)), 2.0) - 0.5;
vec2 gMove = trunc(hash * 0.125 + vec2(0.0, 0.5)) * g2;
vec3 grad = vec3(g2, 0.0) + vec3(-1.0, 1.0, 0.0) * gMove.x + vec3(0.0, -1.0, 1.0) * gMove.y;
return grad;
}
// BCC lattice split up into 2 cube lattices
vec4 openSimplex2_UnrotatedBase(vec3 X) {
// First half-lattice, closest edge
vec3 v1 = round(X);
vec3 d1 = X - v1;
vec3 score1 = abs(d1);
vec3 dir1 = step(max(score1.yzx, score1.zxy), score1);
vec3 v2 = v1 + dir1 * sign(d1);
vec3 d2 = X - v2;
// Second half-lattice, closest edge
vec3 X2 = X + 144.5;
vec3 v3 = round(X2);
vec3 d3 = X2 - v3;
vec3 score2 = abs(d3);
vec3 dir2 = step(max(score2.yzx, score2.zxy), score2);
vec3 v4 = v3 + dir2 * sign(d3);
vec3 d4 = X2 - v4;
// Gradient hashes for the four points, two from each half-lattice
vec4 hashes = permute(mod(vec4(v1.x, v2.x, v3.x, v4.x), 289.0));
hashes = permute(mod(hashes + vec4(v1.y, v2.y, v3.y, v4.y), 289.0));
hashes = permute(mod(hashes + vec4(v1.z, v2.z, v3.z, v4.z), 289.0));
hashes = mod(hashes, 289.0);
vec4 whashes = mod(hashes / 12.0, 12.0);
hashes = mod(hashes, 12.0);
// Gradient extrapolations & kernel function
vec4 a = max(0.6 - vec4(dot(d1, d1), dot(d2, d2), dot(d3, d3), dot(d4, d4)), 0.0);
vec4 aa = a * a; vec4 aaaa = aa * aa;
vec3 g1 = grad(hashes.x); vec3 g2 = grad(hashes.y);
vec3 g3 = grad(hashes.z); vec3 g4 = grad(hashes.w);
vec4 extrapolations = vec4(dot(d1, g1), dot(d2, g2), dot(d3, g3), dot(d4, g4));
vec3 og1 = grad(whashes.x); vec3 og2 = grad(whashes.y);
vec3 og3 = grad(whashes.z); vec3 og4 = grad(whashes.w);
vec4 extrapolationsX = extrapolations * vec4(og1.x, og2.x, og3.x, og4.x);
vec4 extrapolationsY = extrapolations * vec4(og1.y, og2.y, og3.y, og4.y);
vec4 extrapolationsZ = extrapolations * vec4(og1.z, og2.z, og3.z, og4.z);
// Return it all as a vec4
return vec4(dot(aaaa, extrapolationsX), dot(aaaa, extrapolationsY), dot(aaaa, extrapolationsZ), dot(aaaa, extrapolations))*vec4(32.69428253173828125*2.);
}
// Use this if X and Y are horizontal, and Z is vertical or time (or unused).
vec4 openSimplex2_ImproveXYPlanes(vec3 X) {
// Rotate so Z points down the main diagonal. Not a skew transform.
mat3 orthonormalMap = mat3(
0.788675134594813, -0.211324865405187, -0.577350269189626,
-0.211324865405187, 0.788675134594813, -0.577350269189626,
0.577350269189626, 0.577350269189626, 0.577350269189626);
vec4 result = openSimplex2_UnrotatedBase(orthonormalMap * X);
return vec4(result.xyz * orthonormalMap, result.w);
}
// Use this if X and Z are horizontal, and Y is vertical or time (or unused).
vec4 openSimplex2_ImproveXZPlanes(vec3 X) {
// Rotate so Z points down the main diagonal. Not a skew transform.
mat3 orthonormalMap = mat3(
0.788675134594813, -0.577350269189626, -0.211324865405187,
0.577350269189626 , 0.577350269189626, 0.577350269189626,
-0.211324865405187, -0.577350269189626, 0.788675134594813);
vec4 result = openSimplex2_UnrotatedBase(orthonormalMap * X);
return vec4(result.xyz * orthonormalMap, result.w);
}
// Use this if neither of the above make sense.
vec4 openSimplex2_Classical(vec3 X) {
// Rotate around the main diagonal. Not a skew transform.
vec4 result = openSimplex2_UnrotatedBase(dot(X, vec3(2.0/3.0)) - X);
return vec4(dot(result.xyz, vec3(2.0/3.0)) - result.xyz, result.w);
}
//////////////////////////////// End noise code ////////////////////////////////
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main() {
float time = iTime/2.0;
vec3 X = vec3(uv*10.0+vec2(cos(time), sin(time*1.3+2.7)), sin(time/4.0));
vec3 col = openSimplex2_ImproveXZPlanes( X ).www;
gl_FragColor = vec4(col *0.5+0.5 , 1.0);
}
With simplex noise stored to the library we can finally try to add this noise iteratively!
Got some colored fluid that looks like a lava lamp or an oil:
#include('simplex')
void main() {
float time = iTime * 0.5;
vec3 X = vec3(uv*2.0+vec2(0.0, sin(time/3.0+2.3)/3.0), time/7.0+sin(time/5.0));
vec3 noise = vec3(0);
float scale = 1.0;
for(int i = 0; i < 4; i++){
noise += simplex(X) * scale;
X += noise/scale;
X.z += scale*scale;
X *= scale;
scale *= 0.8;
}
vec3 normalize = noise * 0.5;
gl_FragColor = vec4(pow(normalize.r,0.2), normalize.r, 0.0, 1.0);
}
Add some color:
#include('simplex')
void main() {
float time = iTime * 0.5;
vec3 X = vec3(uv*4.0+vec2(0.0, sin(time/3.0)/3.0), time/7.0+sin(time/5.0));
vec3 noise = vec3(0);
float scale = 1.0;
for(int i = 0; i < 7; i++){
noise += simplex(
X + noise * (1.3 / sqrt(scale))
).xyz * scale;
X *= 1.3;
scale *= 0.75;
}
noise.g /= 3.0;
gl_FragColor = vec4(noise *0.5+0.5, 1.0);
}
This looks more like some planet atmosphere
#include('simplex')
void main() {
vec3 X = vec3(uv*1.5, iTime * 0.09);
vec3 noise = vec3(0);
float scale = 1.0;
float z = 0.0;
for(int i = 0; i < 5; i++){
noise += simplex(X-noise / (2.3/sqrt(scale))) * scale;
X = X*1.1 + vec3(X+noise / sqrt(scale));
scale *= 0.7;
z += 11.0*scale;
}
noise = noise*0.5 + 0.5;
gl_FragColor = vec4( noise.x,noise.y-noise.z,noise.x/2.0, 1.0);
}
Now I want to draw it on a sphere!
#include('simplex')
void main() {
vec3 pos = uvu/30.0;
//vec3 X = vec3(uv*.15+313.0, 119.0+iTime * 0.09);
//vec3 X = vec3(pos.xy*1.15+313.0, 119.0+iTime * 0.09);
vec3 X = pos*3.0+sin(pos.z/2.0)*pos.x+iTime * .12;
vec3 noise = vec3(0);
float scale = 1.0;
float z = 0.0;
for(int i = 0; i < 5; i++){
noise += simplex(X-noise / (2.3/sqrt(scale))) * scale;
X = X*1.1 + vec3(X+noise / sqrt(scale));
scale *= 0.7;
z += 11.0*scale;
}
noise = noise*0.5 + 0.5;
float r = noise.x;
float g = noise.y-noise.z;
r+=g/1.5;
g/=1.0;
r*=0.7;
float b = (r-g)/5.0+0.1;
r+=0.4;
g+=0.3;
gl_FragColor = vec4( r,g,b, 1.0);
}
It looks like a star and now let's take some jupiter reference:
#include('simplex')
float PI = 3.141592;
vec3 grayColor = vec3( 125.0/256.0, 125.0/256.0, 125.0/256.0);
vec3 baseColor = vec3( 119.0/256.0, 103.0/256.0, 71.0/256.0);
vec3 lightColor = vec3( 221.0/256.0, 225.0/256.0, 230.0/256.0);
vec3 orangeColor = vec3( 209.0/256.0, 141.0/256.0, 65.0/256.0);
vec3 orangeDarkColor = vec3( 164.0/256.0, 88.0/256.0, 25.0/256.0);
vec3 redColor = vec3( 150.0/256.0, 67.0/256.0, 54.0/256.0);
vec3 redDarkColor = vec3( 110.0/256.0, 27.0/256.0, 44.0/256.0);
float mix(float x, float y, bool a) {
return a ? y : x;
}
float atan2(float y, float x){
bool s = (abs(x) > abs(y));
return mix(PI/2.0 - atan(x,y), atan(y,x), s);
}
vec3 colorFromPoint(vec3 pos){
float amount = pow(abs(cos((pos.y+1.2+sin(pos.x*6.28*4.0)*0.005)*6.28)),0.8)*0.5;
amount = smoothstep(0.0,.8, amount);
/* float rad = asin(pos.x);
//mix(pos.z, pos.x , acos(pos.x));
rad = acos(pos.x*0.5+0.5)/3.14;
float mixPercent = abs(0.5-(rad*1.56));
rad = mix(pos.x, 1.0-pos.x*pos.x*pos.z, mixPercent);
*/
float rad = atan2(pos.x,pos.z);
//float rad = abs(pos.x+pos.z-pos.y/3.0);
vec3 color = mix(lightColor, baseColor, amount-sin(pos.y*6.28*1.0)*0.2);
color = mix(color, orangeColor, smoothstep(0.3, 1.0, 1.0-abs(pos.y+sin(pos.x*6.28*3.0+2.0)*0.01)));
color = mix(color, orangeDarkColor, smoothstep(0.9, 1.0, 1.0-abs(pos.y+sin(pos.x*6.28*3.0+2.0)*0.02-0.05)));
color = mix(color, redColor, smoothstep(0.80, 1.0, 1.0-abs(pos.y+0.1+sin(rad*6.28*5.0)*0.01+0.05)));
color = mix(color, redDarkColor, smoothstep(0.93, 1.0, 1.0-abs(pos.y+0.1+sin(rad*6.28*5.0+0.3)*0.01+0.1)));
color = mix(color, orangeColor,
smoothstep(0.86, 1.0, 1.1-max(
abs(sin(pos.y*6.28*3.0+sin(2.0+rad*6.28*5.0+pos.y*6.28*4.0)*0.2)+0.3),
sin(rad*6.28*5.0+cos(sin(rad*33.0)*7.7+pos.y*6.28*19.0))*1.7
)));
color = mix(color, redColor,
smoothstep(0.80, 1.0, 1.1-max(
abs(sin(pos.y*6.28*2.0+sin(rad*6.28*7.0+pos.y*6.28*7.0)*0.1)+0.2),
sin(rad*6.28*8.0+cos(pos.y*6.28*17.0))*0.25
)));
color = mix(color, mix(color,grayColor, abs(sin(pos.y*2.0))),
smoothstep(sin((pos.y*0.5)*6.28)*0.1,1.0,1.0-max(
abs(sin(pos.y*6.28*4.7+sin(rad*6.28*5.0+pos.y*6.28*7.0)*0.01)+0.7),
sin(rad*6.28*18.0+cos(pos.y*6.28*27.0+sin(rad*14.0+cos(rad*55.0+17.0)*13.0)*1.5))*0.45
)));
float redSpotDistance = length(vec2(0.4-rad, 0.1-pos.y));
float R = 0.25;
vec3 spotColor = mix(color,
mix(orangeDarkColor, redDarkColor, cos(redSpotDistance*54.2+cos(pos.y*28.1)+sin(pos.x*18.2))*0.5+0.8),
cos(redSpotDistance*64.2+cos(pos.y*23.1)+sin(pos.x*14.2))*0.5+1.4);
/**spotColor = mix(spotColor, orangeDarkColor, cos(redSpotDistance*94.2+cos(pos.y*23.1)+sin(pos.x*54.2))*0.9+1.2);
*/
//spotColor = mix(spotColor, redDarkColor, cos(redSpotDistance*84.2+cos(pos.y*13.1)+sin(pos.x*24.2))*0.8+1.2);
//color = mix(color,spotColor, smoothstep(0.4,1.0,(R-redSpotDistance*redSpotDistance)/R));
//smoothstep(0.80, 1.0, );
//pos.x -= pos.z;
//color.b = rad;
//color.g = rad;
//color.r = rad;
return color;
}
void main() {
vec3 pos = uvu/30.0;
//vec3 X = vec3(uv*.15+313.0, 119.0+iTime * 0.09);
//vec3 X = vec3(pos.xy*1.15+313.0, 119.0+iTime * 0.09);
vec3 X = pos;//*3.0+sin(pos.z/2.0)*pos.x+iTime * .12;
X.x = X.x + iTime*0.017;
vec3 noise = vec3(0);
float scale = 1.0;
float z = 0.0;
for(int i = 0; i < 8; i++){
vec3 c = colorFromPoint(X);
noise += colorFromPoint(X-noise / (2.3/sqrt(scale))+c) * scale;
X= X*(1.0+sin(X.y)*0.05) +simplex(X*scale*13.0 + iTime*0.011)*scale*.03 ;
X.z += z;
scale *= 0.8;
z += 11.0*scale;
}
noise /= 3.5;
//noise = noise*0.5 + 0.5;
gl_FragColor = vec4( noise, 1.0);
//gl_FragColor = vec4( colorFromPoint(pos), 1.0);
}