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); }