Browse Source

NodeMaterial: Add Noise2DNode, Noise3DNode, Fractal3DNode. (#21800)

* NodeMaterial: Add Noise2DNode, Noise3DNode, Fractal3DNode. Remove NoiseNode.

* NodeMaterial: Use .includes for noise nodes.

* NodeMaterial: Clean up extra include.
Don McCurdy 4 years ago
parent
commit
9365eaf316

+ 3 - 1
examples/jsm/nodes/Nodes.js

@@ -56,8 +56,10 @@ export { CondNode } from './math/CondNode.js';
 
 // procedural
 
-export { NoiseNode } from './procedural/NoiseNode.js';
+export { Noise2DNode } from './procedural/Noise2DNode.js';
+export { Noise3DNode } from './procedural/Noise3DNode.js';
 export { CheckerNode } from './procedural/CheckerNode.js';
+export { Fractal3DNode } from './procedural/Fractal3DNode.js';
 
 // misc
 

+ 105 - 0
examples/jsm/nodes/procedural/Fractal3DNode.js

@@ -0,0 +1,105 @@
+import { TempNode } from '../core/TempNode.js';
+import { FunctionNode } from '../core/FunctionNode.js';
+import { FunctionCallNode } from '../core/FunctionCallNode.js';
+import { IntNode } from '../inputs/IntNode.js';
+import { FloatNode } from '../inputs/FloatNode.js';
+import { PositionNode } from '../accessors/PositionNode.js';
+import { Noise2DNode } from './Noise2DNode.js';
+import { Noise3DNode } from './Noise3DNode.js';
+
+const FRACTAL3D_SRC = `
+float fractal3d( vec3 p, float amplitude, int octaves, float lacunarity, float diminish ) {
+
+	float result = 0.0;
+
+	for (int i = 0;  i < octaves; ++i) {
+
+		result += noise3d(p, amplitude, 0.0);
+		amplitude *= diminish;
+		p *= lacunarity;
+
+	}
+
+	return result;
+
+}
+`.trim();
+
+/** Fractional Brownian motion. */
+class Fractal3DNode extends TempNode {
+
+	constructor( position = new PositionNode(), amplitude = new FloatNode( 1.0 ), octaves = 3.0, lacunarity = 2.0, diminish = 0.5 ) {
+
+		super( 'f' );
+
+		this.position = position;
+		this.amplitude = amplitude;
+		this.octaves = new IntNode( octaves ).setReadonly( true );
+		this.lacunarity = new FloatNode( lacunarity ).setReadonly( true );
+		this.diminish = new FloatNode( diminish ).setReadonly( true );
+
+	}
+
+	generate( builder, output ) {
+
+		const fractal3d = new FunctionCallNode( Fractal3DNode.Nodes.fractal3d, [
+
+			this.position,
+			this.amplitude,
+			this.octaves,
+			this.lacunarity,
+			this.diminish,
+
+		] );
+
+		return builder.format( fractal3d.generate( builder, output ), this.getType( builder ), output );
+
+	}
+
+	copy( source ) {
+
+		super.copy( source );
+
+		this.position = source.position;
+		this.amplitude = source.amplitude;
+		this.octaves = source.octaves;
+		this.lacunarity = source.lacunarity;
+		this.diminish = source.diminish;
+
+	}
+
+	toJSON( meta ) {
+
+		const data = this.getJSONNode( meta );
+
+		if ( ! data ) {
+
+			data = this.createJSONNode( meta );
+
+			data.position = this.position.toJSON( meta ).uuid;
+			data.amplitude = this.amplitude.toJSON( meta ).uuid;
+			data.octaves = this.octaves.toJSON( meta ).uuid;
+			data.lacunarity = this.lacunarity.toJSON( meta ).uuid;
+			data.diminish = this.diminish.toJSON( meta ).uuid;
+
+		}
+
+		return data;
+
+	}
+
+}
+
+Fractal3DNode.prototype.nodeType = 'Fractal3D';
+
+Fractal3DNode.Nodes = (function () {
+
+	const fractal3d = new FunctionNode( FRACTAL3D_SRC );
+
+	fractal3d.includes = [ Noise3DNode.Nodes.noise3d ];
+
+	return { fractal3d };
+
+})();
+
+export { Fractal3DNode };

+ 150 - 0
examples/jsm/nodes/procedural/Noise2DNode.js

@@ -0,0 +1,150 @@
+import { TempNode } from '../core/TempNode.js';
+import { FunctionNode } from '../core/FunctionNode.js';
+import { FunctionCallNode } from '../core/FunctionCallNode.js';
+import { IntNode } from '../inputs/IntNode.js';
+import { FloatNode } from '../inputs/FloatNode.js';
+import { UVNode } from '../accessors/UVNode.js';
+
+const NOISE_COMMON_SRC = `
+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;
+
+}
+
+vec4 permute( vec4 x ) {
+
+	return mod289( ( ( x * 34.0 ) + 1.0 ) * x );
+
+}
+
+vec4 taylorInvSqrt( vec4 r ) {
+
+	return 1.79284291400159 - 0.85373472095314 * r;
+
+}
+
+vec2 fade( vec2 t ) {
+
+	return t * t * t * ( t * ( t * 6.0 - 15.0 ) + 10.0 );
+
+}
+
+vec3 fade( vec3 t ) {
+
+	return t * t * t * ( t * ( t * 6.0 - 15.0 ) + 10.0 );
+
+}
+`.trim();
+
+const NOISE2D_SRC = `
+float noise2d( vec2 P, float amplitude, float pivot ) {
+
+	vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);
+	vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);
+	Pi = mod289(Pi); // To avoid truncation effects in permutation
+	vec4 ix = Pi.xzxz;
+	vec4 iy = Pi.yyww;
+	vec4 fx = Pf.xzxz;
+	vec4 fy = Pf.yyww;
+
+	vec4 i = permute(permute(ix) + iy);
+
+	vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ;
+	vec4 gy = abs(gx) - 0.5 ;
+	vec4 tx = floor(gx + 0.5);
+	gx = gx - tx;
+
+	vec2 g00 = vec2(gx.x,gy.x);
+	vec2 g10 = vec2(gx.y,gy.y);
+	vec2 g01 = vec2(gx.z,gy.z);
+	vec2 g11 = vec2(gx.w,gy.w);
+
+	vec4 norm = taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)));
+	g00 *= norm.x;
+	g01 *= norm.y;
+	g10 *= norm.z;
+	g11 *= norm.w;
+
+	float n00 = dot(g00, vec2(fx.x, fy.x));
+	float n10 = dot(g10, vec2(fx.y, fy.y));
+	float n01 = dot(g01, vec2(fx.z, fy.z));
+	float n11 = dot(g11, vec2(fx.w, fy.w));
+
+	vec2 fade_xy = fade(Pf.xy);
+	vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);
+	float n_xy = mix(n_x.x, n_x.y, fade_xy.y);
+	return 2.3 * n_xy * amplitude + pivot;
+
+}
+`.trim();
+
+class Noise2DNode extends TempNode {
+
+	constructor( uv = new UVNode(), amplitude = new FloatNode( 1.0 ), pivot = new FloatNode( 0.0 ) ) {
+
+		super( 'f' );
+
+		this.uv = uv;
+		this.amplitude = amplitude;
+		this.pivot = pivot;
+
+	}
+
+	generate(builder, output) {
+
+		const noise2d = new FunctionCallNode( Noise2DNode.Nodes.noise2d, [ this.uv, this.amplitude, this.pivot ] );
+		return builder.format( noise2d.generate( builder, output ), this.getType( builder ), output );
+
+	}
+
+	copy( source ) {
+
+		super.copy( source );
+
+		this.uv = source.uv;
+		this.amplitude = source.amplitude;
+		this.pivot = source.pivot;
+
+	}
+
+	toJSON( meta ) {
+
+		const data = this.getJSONNode( meta );
+
+		if ( ! data ) {
+
+			data = this.createJSONNode( meta );
+
+			data.uv = this.uv.toJSON( meta ).uuid;
+			data.amplitude = this.amplitude.toJSON( meta ).uuid;
+			data.pivot = this.pivot.toJSON( meta ).uuid;
+
+		}
+
+		return data;
+
+	}
+
+}
+
+Noise2DNode.prototype.nodeType = 'Noise2D';
+
+Noise2DNode.Nodes = (function () {
+
+	const noiseCommon = new FunctionNode( NOISE_COMMON_SRC );
+	const noise2d = new FunctionNode( NOISE2D_SRC );
+
+	noise2d.includes = [ noiseCommon ];
+
+	return { noiseCommon, noise2d };
+
+})();
+
+export { Noise2DNode };

+ 142 - 0
examples/jsm/nodes/procedural/Noise3DNode.js

@@ -0,0 +1,142 @@
+import { TempNode } from '../core/TempNode.js';
+import { FunctionNode } from '../core/FunctionNode.js';
+import { FunctionCallNode } from '../core/FunctionCallNode.js';
+import { IntNode } from '../inputs/IntNode.js';
+import { FloatNode } from '../inputs/FloatNode.js';
+import { PositionNode } from '../accessors/PositionNode.js';
+import { Noise2DNode } from './Noise2DNode.js';
+
+const NOISE3D_SRC = `
+float noise3d( vec3 P, float amplitude, float pivot ) {
+
+	vec3 Pi0 = floor(P); // Integer part for indexing
+	vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
+	Pi0 = mod289(Pi0);
+	Pi1 = mod289(Pi1);
+	vec3 Pf0 = fract(P); // Fractional part for interpolation
+	vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
+	vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
+	vec4 iy = vec4(Pi0.yy, Pi1.yy);
+	vec4 iz0 = Pi0.zzzz;
+	vec4 iz1 = Pi1.zzzz;
+
+	vec4 ixy = permute(permute(ix) + iy);
+	vec4 ixy0 = permute(ixy + iz0);
+	vec4 ixy1 = permute(ixy + iz1);
+
+	vec4 gx0 = ixy0 * (1.0 / 7.0);
+	vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5;
+	gx0 = fract(gx0);
+	vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
+	vec4 sz0 = step(gz0, vec4(0.0));
+	gx0 -= sz0 * (step(0.0, gx0) - 0.5);
+	gy0 -= sz0 * (step(0.0, gy0) - 0.5);
+
+	vec4 gx1 = ixy1 * (1.0 / 7.0);
+	vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5;
+	gx1 = fract(gx1);
+	vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
+	vec4 sz1 = step(gz1, vec4(0.0));
+	gx1 -= sz1 * (step(0.0, gx1) - 0.5);
+	gy1 -= sz1 * (step(0.0, gy1) - 0.5);
+
+	vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
+	vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
+	vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
+	vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
+	vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
+	vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
+	vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
+	vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
+
+	vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
+	g000 *= norm0.x;
+	g010 *= norm0.y;
+	g100 *= norm0.z;
+	g110 *= norm0.w;
+	vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
+	g001 *= norm1.x;
+	g011 *= norm1.y;
+	g101 *= norm1.z;
+	g111 *= norm1.w;
+
+	float n000 = dot(g000, Pf0);
+	float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
+	float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
+	float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
+	float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
+	float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
+	float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
+	float n111 = dot(g111, Pf1);
+
+	vec3 fade_xyz = fade(Pf0);
+	vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
+	vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
+	float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
+	return 2.2 * n_xyz * amplitude + pivot;
+
+}
+`.trim();
+
+class Noise3DNode extends TempNode {
+
+	constructor( position = new PositionNode(), amplitude = new FloatNode( 1.0 ), pivot = new FloatNode( 0.0 ) ) {
+
+		super( 'f' );
+
+		this.position = position;
+		this.amplitude = amplitude;
+		this.pivot = pivot;
+
+	}
+
+	generate(builder, output) {
+
+		const noise3d = new FunctionCallNode( Noise3DNode.Nodes.noise3d, [ this.position, this.amplitude, this.pivot ] );
+		return builder.format( noise3d.generate( builder, output ), this.getType( builder ), output );
+
+	}
+
+	copy( source ) {
+
+		super.copy( source );
+
+		this.position = source.position;
+		this.amplitude = source.amplitude;
+		this.pivot = source.pivot;
+
+	}
+
+	toJSON( meta ) {
+
+		const data = this.getJSONNode( meta );
+
+		if ( ! data ) {
+
+			data = this.createJSONNode( meta );
+
+			data.position = this.position.toJSON( meta ).uuid;
+			data.amplitude = this.amplitude.toJSON( meta ).uuid;
+			data.pivot = this.pivot.toJSON( meta ).uuid;
+
+		}
+
+		return data;
+
+	}
+
+}
+
+Noise3DNode.prototype.nodeType = 'Noise3D';
+
+Noise3DNode.Nodes = (function () {
+
+	const noise3d = new FunctionNode( NOISE3D_SRC );
+
+	noise3d.includes = [ Noise2DNode.Nodes.noiseCommon ];
+
+	return { noise3d };
+
+})();
+
+export { Noise3DNode };

+ 0 - 69
examples/jsm/nodes/procedural/NoiseNode.js

@@ -1,69 +0,0 @@
-import { TempNode } from '../core/TempNode.js';
-import { FunctionNode } from '../core/FunctionNode.js';
-import { UVNode } from '../accessors/UVNode.js';
-
-class NoiseNode extends TempNode {
-
-	constructor( uv ) {
-
-		super( 'f' );
-
-		this.uv = uv || new UVNode();
-
-	}
-
-	generate( builder, output ) {
-
-		const snoise = builder.include( NoiseNode.Nodes.snoise );
-
-		return builder.format( snoise + '( ' + this.uv.build( builder, 'v2' ) + ' )', this.getType( builder ), output );
-
-	}
-
-	copy( source ) {
-
-		super.copy( source );
-
-		this.uv = source.uv;
-
-		return this;
-
-	}
-
-	toJSON( meta ) {
-
-		let data = this.getJSONNode( meta );
-
-		if ( ! data ) {
-
-			data = this.createJSONNode( meta );
-
-			data.uv = this.uv.toJSON( meta ).uuid;
-
-		}
-
-		return data;
-
-	}
-
-}
-
-NoiseNode.Nodes = ( function () {
-
-	const snoise = new FunctionNode( [
-		'float snoise(vec2 co) {',
-
-		'	return fract( sin( dot( co.xy, vec2( 12.9898, 78.233 ) ) ) * 43758.5453 );',
-
-		'}'
-	].join( '\n' ) );
-
-	return {
-		snoise: snoise
-	};
-
-} )();
-
-NoiseNode.prototype.nodeType = 'Noise';
-
-export { NoiseNode };