Skip to content

Commit 9365eaf

Browse files
authored
NodeMaterial: Add Noise2DNode, Noise3DNode, Fractal3DNode. (mrdoob#21800)
* NodeMaterial: Add Noise2DNode, Noise3DNode, Fractal3DNode. Remove NoiseNode. * NodeMaterial: Use .includes for noise nodes. * NodeMaterial: Clean up extra include.
1 parent 7ba9147 commit 9365eaf

File tree

5 files changed

+400
-70
lines changed

5 files changed

+400
-70
lines changed

examples/jsm/nodes/Nodes.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ export { CondNode } from './math/CondNode.js';
5656

5757
// procedural
5858

59-
export { NoiseNode } from './procedural/NoiseNode.js';
59+
export { Noise2DNode } from './procedural/Noise2DNode.js';
60+
export { Noise3DNode } from './procedural/Noise3DNode.js';
6061
export { CheckerNode } from './procedural/CheckerNode.js';
62+
export { Fractal3DNode } from './procedural/Fractal3DNode.js';
6163

6264
// misc
6365

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { TempNode } from '../core/TempNode.js';
2+
import { FunctionNode } from '../core/FunctionNode.js';
3+
import { FunctionCallNode } from '../core/FunctionCallNode.js';
4+
import { IntNode } from '../inputs/IntNode.js';
5+
import { FloatNode } from '../inputs/FloatNode.js';
6+
import { PositionNode } from '../accessors/PositionNode.js';
7+
import { Noise2DNode } from './Noise2DNode.js';
8+
import { Noise3DNode } from './Noise3DNode.js';
9+
10+
const FRACTAL3D_SRC = `
11+
float fractal3d( vec3 p, float amplitude, int octaves, float lacunarity, float diminish ) {
12+
13+
float result = 0.0;
14+
15+
for (int i = 0; i < octaves; ++i) {
16+
17+
result += noise3d(p, amplitude, 0.0);
18+
amplitude *= diminish;
19+
p *= lacunarity;
20+
21+
}
22+
23+
return result;
24+
25+
}
26+
`.trim();
27+
28+
/** Fractional Brownian motion. */
29+
class Fractal3DNode extends TempNode {
30+
31+
constructor( position = new PositionNode(), amplitude = new FloatNode( 1.0 ), octaves = 3.0, lacunarity = 2.0, diminish = 0.5 ) {
32+
33+
super( 'f' );
34+
35+
this.position = position;
36+
this.amplitude = amplitude;
37+
this.octaves = new IntNode( octaves ).setReadonly( true );
38+
this.lacunarity = new FloatNode( lacunarity ).setReadonly( true );
39+
this.diminish = new FloatNode( diminish ).setReadonly( true );
40+
41+
}
42+
43+
generate( builder, output ) {
44+
45+
const fractal3d = new FunctionCallNode( Fractal3DNode.Nodes.fractal3d, [
46+
47+
this.position,
48+
this.amplitude,
49+
this.octaves,
50+
this.lacunarity,
51+
this.diminish,
52+
53+
] );
54+
55+
return builder.format( fractal3d.generate( builder, output ), this.getType( builder ), output );
56+
57+
}
58+
59+
copy( source ) {
60+
61+
super.copy( source );
62+
63+
this.position = source.position;
64+
this.amplitude = source.amplitude;
65+
this.octaves = source.octaves;
66+
this.lacunarity = source.lacunarity;
67+
this.diminish = source.diminish;
68+
69+
}
70+
71+
toJSON( meta ) {
72+
73+
const data = this.getJSONNode( meta );
74+
75+
if ( ! data ) {
76+
77+
data = this.createJSONNode( meta );
78+
79+
data.position = this.position.toJSON( meta ).uuid;
80+
data.amplitude = this.amplitude.toJSON( meta ).uuid;
81+
data.octaves = this.octaves.toJSON( meta ).uuid;
82+
data.lacunarity = this.lacunarity.toJSON( meta ).uuid;
83+
data.diminish = this.diminish.toJSON( meta ).uuid;
84+
85+
}
86+
87+
return data;
88+
89+
}
90+
91+
}
92+
93+
Fractal3DNode.prototype.nodeType = 'Fractal3D';
94+
95+
Fractal3DNode.Nodes = (function () {
96+
97+
const fractal3d = new FunctionNode( FRACTAL3D_SRC );
98+
99+
fractal3d.includes = [ Noise3DNode.Nodes.noise3d ];
100+
101+
return { fractal3d };
102+
103+
})();
104+
105+
export { Fractal3DNode };
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { TempNode } from '../core/TempNode.js';
2+
import { FunctionNode } from '../core/FunctionNode.js';
3+
import { FunctionCallNode } from '../core/FunctionCallNode.js';
4+
import { IntNode } from '../inputs/IntNode.js';
5+
import { FloatNode } from '../inputs/FloatNode.js';
6+
import { UVNode } from '../accessors/UVNode.js';
7+
8+
const NOISE_COMMON_SRC = `
9+
vec3 mod289( vec3 x ) {
10+
11+
return x - floor( x * ( 1.0 / 289.0 ) ) * 289.0;
12+
13+
}
14+
15+
vec4 mod289( vec4 x ) {
16+
17+
return x - floor( x * ( 1.0 / 289.0 ) ) * 289.0;
18+
19+
}
20+
21+
vec4 permute( vec4 x ) {
22+
23+
return mod289( ( ( x * 34.0 ) + 1.0 ) * x );
24+
25+
}
26+
27+
vec4 taylorInvSqrt( vec4 r ) {
28+
29+
return 1.79284291400159 - 0.85373472095314 * r;
30+
31+
}
32+
33+
vec2 fade( vec2 t ) {
34+
35+
return t * t * t * ( t * ( t * 6.0 - 15.0 ) + 10.0 );
36+
37+
}
38+
39+
vec3 fade( vec3 t ) {
40+
41+
return t * t * t * ( t * ( t * 6.0 - 15.0 ) + 10.0 );
42+
43+
}
44+
`.trim();
45+
46+
const NOISE2D_SRC = `
47+
float noise2d( vec2 P, float amplitude, float pivot ) {
48+
49+
vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);
50+
vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);
51+
Pi = mod289(Pi); // To avoid truncation effects in permutation
52+
vec4 ix = Pi.xzxz;
53+
vec4 iy = Pi.yyww;
54+
vec4 fx = Pf.xzxz;
55+
vec4 fy = Pf.yyww;
56+
57+
vec4 i = permute(permute(ix) + iy);
58+
59+
vec4 gx = fract(i * (1.0 / 41.0)) * 2.0 - 1.0 ;
60+
vec4 gy = abs(gx) - 0.5 ;
61+
vec4 tx = floor(gx + 0.5);
62+
gx = gx - tx;
63+
64+
vec2 g00 = vec2(gx.x,gy.x);
65+
vec2 g10 = vec2(gx.y,gy.y);
66+
vec2 g01 = vec2(gx.z,gy.z);
67+
vec2 g11 = vec2(gx.w,gy.w);
68+
69+
vec4 norm = taylorInvSqrt(vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11)));
70+
g00 *= norm.x;
71+
g01 *= norm.y;
72+
g10 *= norm.z;
73+
g11 *= norm.w;
74+
75+
float n00 = dot(g00, vec2(fx.x, fy.x));
76+
float n10 = dot(g10, vec2(fx.y, fy.y));
77+
float n01 = dot(g01, vec2(fx.z, fy.z));
78+
float n11 = dot(g11, vec2(fx.w, fy.w));
79+
80+
vec2 fade_xy = fade(Pf.xy);
81+
vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);
82+
float n_xy = mix(n_x.x, n_x.y, fade_xy.y);
83+
return 2.3 * n_xy * amplitude + pivot;
84+
85+
}
86+
`.trim();
87+
88+
class Noise2DNode extends TempNode {
89+
90+
constructor( uv = new UVNode(), amplitude = new FloatNode( 1.0 ), pivot = new FloatNode( 0.0 ) ) {
91+
92+
super( 'f' );
93+
94+
this.uv = uv;
95+
this.amplitude = amplitude;
96+
this.pivot = pivot;
97+
98+
}
99+
100+
generate(builder, output) {
101+
102+
const noise2d = new FunctionCallNode( Noise2DNode.Nodes.noise2d, [ this.uv, this.amplitude, this.pivot ] );
103+
return builder.format( noise2d.generate( builder, output ), this.getType( builder ), output );
104+
105+
}
106+
107+
copy( source ) {
108+
109+
super.copy( source );
110+
111+
this.uv = source.uv;
112+
this.amplitude = source.amplitude;
113+
this.pivot = source.pivot;
114+
115+
}
116+
117+
toJSON( meta ) {
118+
119+
const data = this.getJSONNode( meta );
120+
121+
if ( ! data ) {
122+
123+
data = this.createJSONNode( meta );
124+
125+
data.uv = this.uv.toJSON( meta ).uuid;
126+
data.amplitude = this.amplitude.toJSON( meta ).uuid;
127+
data.pivot = this.pivot.toJSON( meta ).uuid;
128+
129+
}
130+
131+
return data;
132+
133+
}
134+
135+
}
136+
137+
Noise2DNode.prototype.nodeType = 'Noise2D';
138+
139+
Noise2DNode.Nodes = (function () {
140+
141+
const noiseCommon = new FunctionNode( NOISE_COMMON_SRC );
142+
const noise2d = new FunctionNode( NOISE2D_SRC );
143+
144+
noise2d.includes = [ noiseCommon ];
145+
146+
return { noiseCommon, noise2d };
147+
148+
})();
149+
150+
export { Noise2DNode };

0 commit comments

Comments
 (0)