I found a cool kickstarter project called "Skywanders". It's an minecraft like space game with a lego like building system and pretty cool graphics.
One thing I noticed are the "nebula clouds". They are procedurally generated 3D Objects and they look amazing.
How do I generate such nebulas? I bet there's a way to do that. And is it possible to convert a 2D nebula into a 3D one? I didn't found any algorithm yet or other sources.
Answer
I've done something similar in the past through glsl's Fragment Shaders:
https://www.shadertoy.com/view/lsyfWy
And a Processing version:
https://github.com/felipunky/Stars
It is basically a Fractional Brownian Motion or several layers of noise at different frequencies and amplitudes stacked together:
#define HASHSCALE .1031
// We create the pseudo-random number generator.
// https://www.shadertoy.com/view/4djSRW
float hash(float p)
{
vec3 p3 = fract(vec3(p) * HASHSCALE);
p3 += dot(p3, p3.yzx + 19.19);
return fract((p3.x + p3.y) * p3.z);
}
// This function is by @Inigo Quilez.
// We create the 3D noise by generating pseudo-random numbers in the x, y and z directions and then interpolating between them.
float noise( in vec3 x )
{
vec3 p = floor( x );
vec3 k = fract( x );
k *= k * k * ( 3.0 - 2.0 * k );
float n = p.x + p.y * 57.0 + p.z * 113.0;
float a = hash( n );
float b = hash( n + 1.0 );
float c = hash( n + 57.0 );
float d = hash( n + 58.0 );
float e = hash( n + 113.0 );
float f = hash( n + 114.0 );
float g = hash( n + 170.0 );
float h = hash( n + 171.0 );
float res = mix( mix( mix ( a, b, k.x ), mix( c, d, k.x ), k.y ),
mix( mix ( e, f, k.x ), mix( g, h, k.x ), k.y ),
k.z
);
return res;
}
// Here we do the stacking of noise at different octaves.
float fbm( in vec3 p )
{
float f = 0.0;
f += 0.5000 * noise( p ); p *= 2.02; p -= iTime * 0.5;
f += 0.2500 * noise( p ); p *= 2.03; p += iTime * 0.4;
f += 0.1250 * noise( p ); p *= 2.01; p -= iTime * 0.5;
f += 0.0625 * noise( p );
f += 0.0125 * noise( p );
return f / 0.9375;
}
I find this fbm function more readable and easier to tweak:
float fbm( in vec3 p )
{
float res = 0.0, fre = 1.0, amp = 1.0, div = 0.0;
for( int i = 0; i < 5; ++i )
{
res += amp * noise( p * fre );
div += amp;
amp *= 0.7;
fre *= 1.7;
}
res /= div;
return res;
}
Rendered through a Sphere Tracing Algorithm that accumulates for a volumetric look:
// This is our ray marching algorithm.
float ray( vec3 ro, vec3 rd, out float den )
{
float t = 0.0, maxD = 0.0, d = 1.0; den = 0.0;
// The more STEPS the more accurate the marching.
for( int i = 0; i < STEPS; ++i )
{
// Here we compute our Position p by the formula RayOrigin + RayDirection * our RayMarchStep.
vec3 p = ro + rd * t;
// This is our density, it is simply calling the Fractional Brownian Motion fbm function.
den = fbm( p );
// This allows us to put a limit in our accumulation of density.
maxD = maxD < den ? den : maxD;
// Here we bail on our marching according to MaximumDensity or our FAR threshold.
if( maxD > 1.0 || t > FAR ) break;
// We increment our RayMarchingSteps.
t += 0.05;
}
den = maxD;
return t;
}
No comments:
Post a Comment