As mentioned on the wiki page Noise (bandpassed white), value noise is very defective. However defective is not the same as useless. Value noise has an advantage of being very fast in low dimensions. It simple usages its defects can be an advantage of giving a certain “look” and in more complex usages the defeats can be hidden by how the samples are mixed.
The java version is a mixed implementation. It restricts itself to a single cell per evaluation for speed purposes, otherwise the complexity becomes too high for it to be of any potential interest. On the other hand it uses an excellent hashing function and uses a first order continuous weighting function, both of which add in reducing defects. Note that the hashing functions chosen have short dependency chains and probably performs better than one might expect.
public final class ValueNoise
{
private static final int pseudoFloor(float x)
{
return x >= 0 ? (int)x : (int)x-1;
}
private static final float weight(float t)
{
return (t*t*(3.f-(t+t)));
}
private static final float normalizeI(int h)
{
return (h>>>7)*0x1.0p-24f - 1;
}
private static final int M = 0x5bd1e995;
private static final int postHashM2(int h)
{
h ^= h >>> 13; h *= M; h ^= h >>> 15;
return h;
}
private static final int preHashM2(int x)
{
int h = 0x9747b28c;
x *= M; x ^= x >>> 24; x *= M;
h ^= x; h *= M;
return h;
}
private static final int hashM2(int x)
{
return postHashM2(preHashM2(x));
}
private static final int preHashM2(int x, int y)
{
int h = 0x9747b28c;
x *= M; x ^= x >>> 24; x *= M;
y *= M; y ^= y >>> 24; y *= M;
h ^= x; h *= M; h ^= y;
return h;
}
private static final int hashM2(int x, int y)
{
return postHashM2(preHashM2(x,y));
}
private static final int preHashM2(int x, int y, int z)
{
int h = 0x9747b28c;
x *= M; x ^= x >>> 24; x *= M;
y *= M; y ^= y >>> 24; y *= M;
z *= M; z ^= z >>> 24; z *= M;
h ^= x; h *= M;
h ^= y; h *= M;
h ^= z;
return h;
}
private static final int hashM2(int x, int y, int z)
{
return postHashM2(preHashM2(x,y,z));
}
private static final float mix(int x)
{
int h = hashM2(x);
return normalizeI(h);
}
/** Sample 1D function */
public static final float calc(float x)
{
int ix = pseudoFloor(x);
float dx = ease(x-ix);
float r0 = mix(ix);
float r1 = mix(ix+1);
return lerp(dx, r0, r1);
}
private static final float mix(int x, int y)
{
return normalizeI(hashM2(x,y));
}
/** Sample 2D function */
public static final float calc(float x, float y)
{
// get the coordinate of the cell
int ix = pseudoFloor(x);
int iy = pseudoFloor(y);
// compute the offset into the cell, then weight it
x = weight(x-ix);
y = weight(y-iy);
// compute hash values for the four vertices of the cell and
// convert into a uniform float
float r00 = mix(ix, iy);
float r10 = mix(ix+1, iy);
float r01 = mix(ix, iy+1);
float r11 = mix(ix+1, iy+1);
float xb = lerp(x, r00, r10); // lerp bottom edge
float xt = lerp(x, r01, r11); // lerp top edge
return lerp(y, xb, xt); // lerp the two edges into the final result
}
private static final float mix(int x, int y, int z)
{
int h = hashM2(x,y,z);
return normalizeI(h);
}
/** Sample 3D function */
public static final float calc(float x, float y, float z)
{
float t0,t1,t2,t3,t4;
// lower left hand coordinate of cell
int ix = pseudoFloor(x);
int iy = pseudoFloor(y);
int iz = pseudoFloor(z);
// offset into cell, then convert to weighting value
x = weight(x-ix);
y = weight(y-iy);
z = weight(z-iz);
// bottom edge of forward face
t0 = mix(ix, iy, iz);
t1 = mix(ix+1, iy, iz);
t2 = lerp(x, t0, t1);
// top edge of forward face
t0 = mix(ix, iy+1, iz);
t1 = mix(ix+1, iy+1, iz);
t3 = lerp(x, t0, t1);
t3 = lerp(y, t2, t3); // final result in forward face
// bottom edge of back face
t0 = mix(ix, iy, iz+1);
t1 = mix(ix+1, iy, iz+1);
t2 = lerp(x, t0, t1);
// top edge of back face
t0 = mix(ix, iy+1, iz+1);
t1 = mix(ix+1, iy+1, iz+1);
t4 = lerp(x, t0, t1);
t4 = lerp(y, t2, t4);
return lerp(z, t3, t4);
}
}
GLSL version using permutation polynomials for hashing/random number:
#version 150 core
// Permutation polynomial RNG/Hashing from Ashima Arts.
float mod289(float x) { return x - floor(x * (1.0/289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0/289.0)) * 289.0; }
vec3 mod289(vec3 x) { return x - floor(x * (1.0/289.0)) * 289.0; }
vec4 mod289(vec4 x) { return x - floor(x * (1.0/289.0)) * 289.0; }
vec2 permute(vec2 x) { return mod289(((x*34.0)+1.0)*x); }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }
vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
float rng(float x) { return fract(x * (1.f/41.f)); }
vec2 rng(vec2 x) { return fract(x * (1.f/41.f)); }
vec3 rng(vec3 x) { return fract(x * (1.f/41.f)); }
vec4 rng(vec4 x) { return fract(x * (1.f/41.f)); }
float ease(float t) { return (t*t*(3.f-(t+t))); }
vec2 ease(vec2 t) { return (t*t*(3.f-(t+t))); }
vec3 ease(vec3 t) { return (t*t*(3.f-(t+t))); }
vec4 ease(vec4 t) { return (t*t*(3.f-(t+t))); }
// sample 2D noise function
float vnoise(vec2 v)
{
vec4 c, h;
vec2 b, ds;
float r;
c = floor(v.xxyy) + vec4(0,1,0,1); // cell coords: (x0,x1,y0,y1)
ds = ease(fract(v)); // offset into cell -> weighting function
c = mod289(c);
// x=hash(x0,y0) : y=hash(x1,y0) : z=hash(x0,y1) : w=hash(x1,y1)
h = permute(permute(c.xyxy) + c.zzww);
h = rng(h);
b = mix(h.xz, h.yw, ds.x); // lerp top and bottom edges
r = mix(b.x, b.y, ds.y); // lerp in 'y'
return r+r-1;
}
// sample 3D noise function
float vnoise(vec3 v)
{
vec4 c = floor(v.xxyy) + vec4(0,1,0,1); // cell coords: (x0,x1,y0,y1)
vec2 z = floor(v.zz) + vec2(0,1); // cell coords: (z0,z1)
vec3 ds = ease(fract(v)); // offset into cell -> weighting function
c = mod289(c);
// x=hash(x0,y0) : y=hash(x1,y0) : z=hash(x0,y1) : w=hash(x1,y1)
vec4 h = permute(permute(c.xyxy) + c.zzww);
vec4 ff = rng(permute(h + z.xxxx));
vec4 bf = rng(permute(h + z.yyyy));
vec2 t = mix(ff.xz, ff.yw, ds.x); // lerp top and bottom edges (front face)
vec2 s = mix(bf.xz, bf.yw, ds.x); // lerp top and bottom edges (back face)
float a = mix(t.x, t.y, ds.y);
float b = mix(s.x, s.y, ds.y);
float r = mix(a, b, ds.z);
return r+r-1;
}
// EXAMPLE
// Turbulence formulated to insure that cell structures
// are aligned, assuming input coordinates are on 0,1 or
// -1,1, and therefore making defects compound (most
// obvious).
float turb(vec2 v, float s)
{
float n, r;
n = vnoise(s*v.xy); r = abs((1/ 2.f)*(n)); s += s;
n = vnoise(s*v.xy); r += abs((1/ 4.f)*(n)); s += s;
n = vnoise(s*v.xy); r += abs((1/ 8.f)*(n)); s += s;
n = vnoise(s*v.xy); r += abs((1/16.f)*(n));
return r;
}
in vec4 coord;
out vec4 out_Color;
void main(void)
{
vec4 c;
float f;
//f = vnoise(10*coord.xy); f = (f+1)*0.5;
f = turb(coord.xy, 5.f);
c = vec4(f,f,f,1);
out_Color.rgb = c.rgb;
}