浏览代码

Nodes: MaterialX Noise Functions Lib (#24504)

* add includes for constructor argument

* add support to array for function call

* add function interface

* convert to nodeObjects function call parameters and includes

* add fn

* add materialx noise functions example

* adjust scale to better visual
sunag 2 年之前
父节点
当前提交
0fd7bb6f5d

+ 1 - 0
examples/files.json

@@ -235,6 +235,7 @@
 		"webgl_nodes_materials_instance_uniform",
 		"webgl_nodes_materials_instance_uniform",
 		"webgl_nodes_materials_physical_clearcoat",
 		"webgl_nodes_materials_physical_clearcoat",
 		"webgl_nodes_materials_standard",
 		"webgl_nodes_materials_standard",
+		"webgl_nodes_materialx_noise",
 		"webgl_nodes_playground",
 		"webgl_nodes_playground",
 		"webgl_nodes_points"
 		"webgl_nodes_points"
 	],
 	],

+ 3 - 3
examples/jsm/nodes/core/CodeNode.js

@@ -2,15 +2,15 @@ import Node from './Node.js';
 
 
 class CodeNode extends Node {
 class CodeNode extends Node {
 
 
-	constructor( code = '', nodeType = 'code' ) {
+	constructor( code = '', includes = [] ) {
 
 
-		super( nodeType );
+		super( 'code' );
 
 
 		this.isCodeNode = true;
 		this.isCodeNode = true;
 
 
 		this.code = code;
 		this.code = code;
 
 
-		this._includes = [];
+		this._includes = includes;
 
 
 	}
 	}
 
 

+ 20 - 5
examples/jsm/nodes/core/FunctionCallNode.js

@@ -40,17 +40,32 @@ class FunctionCallNode extends TempNode {
 		const inputs = functionNode.getInputs( builder );
 		const inputs = functionNode.getInputs( builder );
 		const parameters = this.parameters;
 		const parameters = this.parameters;
 
 
-		for ( const inputNode of inputs ) {
+		if ( Array.isArray( parameters ) ) {
 
 
-			const node = parameters[ inputNode.name ];
+			for ( let i = 0; i < parameters.length; i ++ ) {
 
 
-			if ( node !== undefined ) {
+				const inputNode = inputs[ i ];
+				const node = parameters[ i ];
 
 
 				params.push( node.build( builder, inputNode.type ) );
 				params.push( node.build( builder, inputNode.type ) );
 
 
-			} else {
+			}
+
+		} else {
+
+			for ( const inputNode of inputs ) {
+
+				const node = parameters[ inputNode.name ];
+
+				if ( node !== undefined ) {
+
+					params.push( node.build( builder, inputNode.type ) );
+
+				} else {
+
+					throw new Error( `FunctionCallNode: Input '${inputNode.name}' not found in FunctionNode.` );
 
 
-				throw new Error( `FunctionCallNode: Input '${inputNode.name}' not found in FunctionNode.` );
+				}
 
 
 			}
 			}
 
 

+ 2 - 2
examples/jsm/nodes/core/FunctionNode.js

@@ -3,9 +3,9 @@ import FunctionCallNode from './FunctionCallNode.js';
 
 
 class FunctionNode extends CodeNode {
 class FunctionNode extends CodeNode {
 
 
-	constructor( code = '' ) {
+	constructor( code = '', includes = [] ) {
 
 
-		super( code );
+		super( code, includes );
 
 
 		this.keywords = {};
 		this.keywords = {};
 
 

+ 199 - 0
examples/jsm/nodes/materialx/Disclaimer.md

@@ -0,0 +1,199 @@
+## MaterialX
+
+MaterialX is a project of the
+[Academy Software Foundation](https://www.aswf.io/) and relies on the ASWF
+governance policies, supported by the Linux Foundation.
+<https://github.com/AcademySoftwareFoundation/MaterialX>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+<http://www.apache.org/licenses/LICENSE-2.0>
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+```
+-------------------------------------------------------------------------
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+```

+ 56 - 0
examples/jsm/nodes/materialx/functions/lib/mx_hsv.js

@@ -0,0 +1,56 @@
+import { fn } from '../../../Nodes.js';
+
+// Original shader code from:
+// https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_hsv.glsl
+
+export const mx_hsvtorgb = fn( `vec3 mx_hsvtorgb(vec3 hsv)
+{
+    // Reference for this technique: Foley & van Dam
+    float h = hsv.x; float s = hsv.y; float v = hsv.z;
+    if (s < 0.0001f) {
+      return vec3 (v, v, v);
+    } else {
+        h = 6.0f * (h - floor(h));  // expand to [0..6)
+        int hi = int(trunc(h));
+        float f = h - float(hi);
+        float p = v * (1.0f-s);
+        float q = v * (1.0f-s*f);
+        float t = v * (1.0f-s*(1.0f-f));
+        if (hi == 0)
+            return vec3 (v, t, p);
+        else if (hi == 1)
+            return vec3 (q, v, p);
+        else if (hi == 2)
+            return vec3 (p, v, t);
+        else if (hi == 3)
+            return vec3 (p, q, v);
+        else if (hi == 4)
+            return vec3 (t, p, v);
+        return vec3 (v, p, q);
+    }
+}` );
+
+export const mx_rgbtohsv = fn( `vec3 mx_rgbtohsv(vec3 c)
+{
+    // See Foley & van Dam
+    float r = c.x; float g = c.y; float b = c.z;
+    float mincomp = min (r, min(g, b));
+    float maxcomp = max (r, max(g, b));
+    float delta = maxcomp - mincomp;  // chroma
+    float h, s, v;
+    v = maxcomp;
+    if (maxcomp > 0.0f)
+        s = delta / maxcomp;
+    else s = 0.0f;
+    if (s <= 0.0f)
+        h = 0.0f;
+    else {
+        if      (r >= maxcomp) h = (g-b) / delta;
+        else if (g >= maxcomp) h = 2.0f + (b-r) / delta;
+        else                   h = 4.0f + (r-g) / delta;
+        h *= (1.0f/6.0f);
+        if (h < 0.0f)
+            h += 1.0f;
+    }
+    return vec3(h, s, v);
+}` );

+ 607 - 0
examples/jsm/nodes/materialx/functions/lib/mx_noise.js

@@ -0,0 +1,607 @@
+import { code, fn } from '../../../Nodes.js';
+
+// Original shader code from:
+// https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_noise.glsl
+
+export const mx_noise = code( `float mx_select(bool b, float t, float f)
+{
+    return b ? t : f;
+}
+
+float mx_negate_if(float val, bool b)
+{
+    return b ? -val : val;
+}
+
+int mx_floor(float x)
+{
+    return int(floor(x));
+}
+
+// return mx_floor as well as the fractional remainder
+float mx_floorfrac(float x, out int i)
+{
+    i = mx_floor(x);
+    return x - float(i);
+}
+
+float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
+{
+    float s1 = 1.0 - s;
+    return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
+}
+vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
+{
+    float s1 = 1.0 - s;
+    return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
+}
+float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
+{
+    float s1 = 1.0 - s;
+    float t1 = 1.0 - t;
+    float r1 = 1.0 - r;
+    return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
+            r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
+}
+vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
+{
+    float s1 = 1.0 - s;
+    float t1 = 1.0 - t;
+    float r1 = 1.0 - r;
+    return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
+            r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
+}
+
+// 2 and 3 dimensional gradient functions - perform a dot product against a
+// randomly chosen vector. Note that the gradient vector is not normalized, but
+// this only affects the overal "scale" of the result, so we simply account for
+// the scale by multiplying in the corresponding "perlin" function.
+float mx_gradient_float(uint hash, float x, float y)
+{
+    // 8 possible directions (+-1,+-2) and (+-2,+-1)
+    uint h = hash & 7u;
+    float u = mx_select(h<4u, x, y);
+    float v = 2.0 * mx_select(h<4u, y, x);
+    // compute the dot product with (x,y).
+    return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
+}
+float mx_gradient_float(uint hash, float x, float y, float z)
+{
+    // use vectors pointing to the edges of the cube
+    uint h = hash & 15u;
+    float u = mx_select(h<8u, x, y);
+    float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
+    return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
+}
+vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
+{
+    return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
+}
+vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
+{
+    return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
+}
+// Scaling factors to normalize the result of gradients above.
+// These factors were experimentally calculated to be:
+//    2D:   0.6616
+//    3D:   0.9820
+float mx_gradient_scale2d(float v) { return 0.6616 * v; }
+float mx_gradient_scale3d(float v) { return 0.9820 * v; }
+vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
+vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
+
+/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
+uint mx_rotl32(uint x, int k)
+{
+    return (x<<k) | (x>>(32-k));
+}
+
+void mx_bjmix(inout uint a, inout uint b, inout uint c)
+{
+    a -= c; a ^= mx_rotl32(c, 4); c += b;
+    b -= a; b ^= mx_rotl32(a, 6); a += c;
+    c -= b; c ^= mx_rotl32(b, 8); b += a;
+    a -= c; a ^= mx_rotl32(c,16); c += b;
+    b -= a; b ^= mx_rotl32(a,19); a += c;
+    c -= b; c ^= mx_rotl32(b, 4); b += a;
+}
+
+// Mix up and combine the bits of a, b, and c (doesn't change them, but
+// returns a hash of those three original values).
+uint mx_bjfinal(uint a, uint b, uint c)
+{
+    c ^= b; c -= mx_rotl32(b,14);
+    a ^= c; a -= mx_rotl32(c,11);
+    b ^= a; b -= mx_rotl32(a,25);
+    c ^= b; c -= mx_rotl32(b,16);
+    a ^= c; a -= mx_rotl32(c,4);
+    b ^= a; b -= mx_rotl32(a,14);
+    c ^= b; c -= mx_rotl32(b,24);
+    return c;
+}
+
+// Convert a 32 bit integer into a floating point number in [0,1]
+float mx_bits_to_01(uint bits)
+{
+    return float(bits) / float(uint(0xffffffff));
+}
+
+float mx_fade(float t)
+{
+   return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
+}
+
+uint mx_hash_int(int x)
+{
+    uint len = 1u;
+    uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
+    return mx_bjfinal(seed+uint(x), seed, seed);
+}
+
+uint mx_hash_int(int x, int y)
+{
+    uint len = 2u;
+    uint a, b, c;
+    a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
+    a += uint(x);
+    b += uint(y);
+    return mx_bjfinal(a, b, c);
+}
+
+uint mx_hash_int(int x, int y, int z)
+{
+    uint len = 3u;
+    uint a, b, c;
+    a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
+    a += uint(x);
+    b += uint(y);
+    c += uint(z);
+    return mx_bjfinal(a, b, c);
+}
+
+uint mx_hash_int(int x, int y, int z, int xx)
+{
+    uint len = 4u;
+    uint a, b, c;
+    a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
+    a += uint(x);
+    b += uint(y);
+    c += uint(z);
+    mx_bjmix(a, b, c);
+    a += uint(xx);
+    return mx_bjfinal(a, b, c);
+}
+
+uint mx_hash_int(int x, int y, int z, int xx, int yy)
+{
+    uint len = 5u;
+    uint a, b, c;
+    a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
+    a += uint(x);
+    b += uint(y);
+    c += uint(z);
+    mx_bjmix(a, b, c);
+    a += uint(xx);
+    b += uint(yy);
+    return mx_bjfinal(a, b, c);
+}
+
+uvec3 mx_hash_vec3(int x, int y)
+{
+    uint h = mx_hash_int(x, y);
+    // we only need the low-order bits to be random, so split out
+    // the 32 bit result into 3 parts for each channel
+    uvec3 result;
+    result.x = (h      ) & 0xFFu;
+    result.y = (h >> 8 ) & 0xFFu;
+    result.z = (h >> 16) & 0xFFu;
+    return result;
+}
+
+uvec3 mx_hash_vec3(int x, int y, int z)
+{
+    uint h = mx_hash_int(x, y, z);
+    // we only need the low-order bits to be random, so split out
+    // the 32 bit result into 3 parts for each channel
+    uvec3 result;
+    result.x = (h      ) & 0xFFu;
+    result.y = (h >> 8 ) & 0xFFu;
+    result.z = (h >> 16) & 0xFFu;
+    return result;
+}
+
+float mx_perlin_noise_float(vec2 p)
+{
+    int X, Y;
+    float fx = mx_floorfrac(p.x, X);
+    float fy = mx_floorfrac(p.y, Y);
+    float u = mx_fade(fx);
+    float v = mx_fade(fy);
+    float result = mx_bilerp(
+        mx_gradient_float(mx_hash_int(X  , Y  ), fx    , fy     ),
+        mx_gradient_float(mx_hash_int(X+1, Y  ), fx-1.0, fy     ),
+        mx_gradient_float(mx_hash_int(X  , Y+1), fx    , fy-1.0),
+        mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
+        u, v);
+    return mx_gradient_scale2d(result);
+}
+
+float mx_perlin_noise_float(vec3 p)
+{
+    int X, Y, Z;
+    float fx = mx_floorfrac(p.x, X);
+    float fy = mx_floorfrac(p.y, Y);
+    float fz = mx_floorfrac(p.z, Z);
+    float u = mx_fade(fx);
+    float v = mx_fade(fy);
+    float w = mx_fade(fz);
+    float result = mx_trilerp(
+        mx_gradient_float(mx_hash_int(X  , Y  , Z  ), fx    , fy    , fz     ),
+        mx_gradient_float(mx_hash_int(X+1, Y  , Z  ), fx-1.0, fy    , fz     ),
+        mx_gradient_float(mx_hash_int(X  , Y+1, Z  ), fx    , fy-1.0, fz     ),
+        mx_gradient_float(mx_hash_int(X+1, Y+1, Z  ), fx-1.0, fy-1.0, fz     ),
+        mx_gradient_float(mx_hash_int(X  , Y  , Z+1), fx    , fy    , fz-1.0),
+        mx_gradient_float(mx_hash_int(X+1, Y  , Z+1), fx-1.0, fy    , fz-1.0),
+        mx_gradient_float(mx_hash_int(X  , Y+1, Z+1), fx    , fy-1.0, fz-1.0),
+        mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
+        u, v, w);
+    return mx_gradient_scale3d(result);
+}
+
+vec3 mx_perlin_noise_vec3(vec2 p)
+{
+    int X, Y;
+    float fx = mx_floorfrac(p.x, X);
+    float fy = mx_floorfrac(p.y, Y);
+    float u = mx_fade(fx);
+    float v = mx_fade(fy);
+    vec3 result = mx_bilerp(
+        mx_gradient_vec3(mx_hash_vec3(X  , Y  ), fx    , fy     ),
+        mx_gradient_vec3(mx_hash_vec3(X+1, Y  ), fx-1.0, fy     ),
+        mx_gradient_vec3(mx_hash_vec3(X  , Y+1), fx    , fy-1.0),
+        mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
+        u, v);
+    return mx_gradient_scale2d(result);
+}
+
+vec3 mx_perlin_noise_vec3(vec3 p)
+{
+    int X, Y, Z;
+    float fx = mx_floorfrac(p.x, X);
+    float fy = mx_floorfrac(p.y, Y);
+    float fz = mx_floorfrac(p.z, Z);
+    float u = mx_fade(fx);
+    float v = mx_fade(fy);
+    float w = mx_fade(fz);
+    vec3 result = mx_trilerp(
+        mx_gradient_vec3(mx_hash_vec3(X  , Y  , Z  ), fx    , fy    , fz     ),
+        mx_gradient_vec3(mx_hash_vec3(X+1, Y  , Z  ), fx-1.0, fy    , fz     ),
+        mx_gradient_vec3(mx_hash_vec3(X  , Y+1, Z  ), fx    , fy-1.0, fz     ),
+        mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z  ), fx-1.0, fy-1.0, fz     ),
+        mx_gradient_vec3(mx_hash_vec3(X  , Y  , Z+1), fx    , fy    , fz-1.0),
+        mx_gradient_vec3(mx_hash_vec3(X+1, Y  , Z+1), fx-1.0, fy    , fz-1.0),
+        mx_gradient_vec3(mx_hash_vec3(X  , Y+1, Z+1), fx    , fy-1.0, fz-1.0),
+        mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
+        u, v, w);
+    return mx_gradient_scale3d(result);
+}
+
+float mx_cell_noise_float(float p)
+{
+    int ix = mx_floor(p);
+    return mx_bits_to_01(mx_hash_int(ix));
+}
+
+float mx_cell_noise_float(vec2 p)
+{
+    int ix = mx_floor(p.x);
+    int iy = mx_floor(p.y);
+    return mx_bits_to_01(mx_hash_int(ix, iy));
+}
+
+float mx_cell_noise_float(vec3 p)
+{
+    int ix = mx_floor(p.x);
+    int iy = mx_floor(p.y);
+    int iz = mx_floor(p.z);
+    return mx_bits_to_01(mx_hash_int(ix, iy, iz));
+}
+
+float mx_cell_noise_float(vec4 p)
+{
+    int ix = mx_floor(p.x);
+    int iy = mx_floor(p.y);
+    int iz = mx_floor(p.z);
+    int iw = mx_floor(p.w);
+    return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
+}
+
+vec3 mx_cell_noise_vec3(float p)
+{
+    int ix = mx_floor(p);
+    return vec3(
+            mx_bits_to_01(mx_hash_int(ix, 0)),
+            mx_bits_to_01(mx_hash_int(ix, 1)),
+            mx_bits_to_01(mx_hash_int(ix, 2))
+    );
+}
+
+vec3 mx_cell_noise_vec3(vec2 p)
+{
+    int ix = mx_floor(p.x);
+    int iy = mx_floor(p.y);
+    return vec3(
+            mx_bits_to_01(mx_hash_int(ix, iy, 0)),
+            mx_bits_to_01(mx_hash_int(ix, iy, 1)),
+            mx_bits_to_01(mx_hash_int(ix, iy, 2))
+    );
+}
+
+vec3 mx_cell_noise_vec3(vec3 p)
+{
+    int ix = mx_floor(p.x);
+    int iy = mx_floor(p.y);
+    int iz = mx_floor(p.z);
+    return vec3(
+            mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
+            mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
+            mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
+    );
+}
+
+vec3 mx_cell_noise_vec3(vec4 p)
+{
+    int ix = mx_floor(p.x);
+    int iy = mx_floor(p.y);
+    int iz = mx_floor(p.z);
+    int iw = mx_floor(p.w);
+    return vec3(
+            mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
+            mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
+            mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
+    );
+}
+
+float mx_fractal_noise_float(vec3 p, int octaves, float lacunarity, float diminish)
+{
+    float result = 0.0;
+    float amplitude = 1.0;
+    for (int i = 0;  i < octaves; ++i)
+    {
+        result += amplitude * mx_perlin_noise_float(p);
+        amplitude *= diminish;
+        p *= lacunarity;
+    }
+    return result;
+}
+
+vec3 mx_fractal_noise_vec3(vec3 p, int octaves, float lacunarity, float diminish)
+{
+    vec3 result = vec3(0.0);
+    float amplitude = 1.0;
+    for (int i = 0;  i < octaves; ++i)
+    {
+        result += amplitude * mx_perlin_noise_vec3(p);
+        amplitude *= diminish;
+        p *= lacunarity;
+    }
+    return result;
+}
+
+vec2 mx_fractal_noise_vec2(vec3 p, int octaves, float lacunarity, float diminish)
+{
+    return vec2(mx_fractal_noise_float(p, octaves, lacunarity, diminish),
+                mx_fractal_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish));
+}
+
+vec4 mx_fractal_noise_vec4(vec3 p, int octaves, float lacunarity, float diminish)
+{
+    vec3  c = mx_fractal_noise_vec3(p, octaves, lacunarity, diminish);
+    float f = mx_fractal_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish);
+    return vec4(c, f);
+}
+
+float mx_worley_distance(vec2 p, int x, int y, int xoff, int yoff, float jitter, int metric)
+{
+    vec3  tmp = mx_cell_noise_vec3(vec2(x+xoff, y+yoff));
+    vec2  off = vec2(tmp.x, tmp.y);
+
+    off -= 0.5f;
+    off *= jitter;
+    off += 0.5f;
+
+    vec2 cellpos = vec2(float(x), float(y)) + off;
+    vec2 diff = cellpos - p;
+    if (metric == 2)
+        return abs(diff.x) + abs(diff.y);       // Manhattan distance
+    if (metric == 3)
+        return max(abs(diff.x), abs(diff.y));   // Chebyshev distance
+    // Either Euclidian or Distance^2
+    return dot(diff, diff);
+}
+
+float mx_worley_distance(vec3 p, int x, int y, int z, int xoff, int yoff, int zoff, float jitter, int metric)
+{
+    vec3  off = mx_cell_noise_vec3(vec3(x+xoff, y+yoff, z+zoff));
+
+    off -= 0.5f;
+    off *= jitter;
+    off += 0.5f;
+
+    vec3 cellpos = vec3(float(x), float(y), float(z)) + off;
+    vec3 diff = cellpos - p;
+    if (metric == 2)
+        return abs(diff.x) + abs(diff.y) + abs(diff.z); // Manhattan distance
+    if (metric == 3)
+        return max(max(abs(diff.x), abs(diff.y)), abs(diff.z)); // Chebyshev distance
+    // Either Euclidian or Distance^2
+    return dot(diff, diff);
+}
+
+float mx_worley_noise_float(vec2 p, float jitter, int metric)
+{
+    int X, Y;
+    vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+    float sqdist = 1e6f;        // Some big number for jitter > 1 (not all GPUs may be IEEE)
+    for (int x = -1; x <= 1; ++x)
+    {
+        for (int y = -1; y <= 1; ++y)
+        {
+            float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+            sqdist = min(sqdist, dist);
+        }
+    }
+    if (metric == 0)
+        sqdist = sqrt(sqdist);
+    return sqdist;
+}
+
+vec2 mx_worley_noise_vec2(vec2 p, float jitter, int metric)
+{
+    int X, Y;
+    vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+    vec2 sqdist = vec2(1e6f, 1e6f);
+    for (int x = -1; x <= 1; ++x)
+    {
+        for (int y = -1; y <= 1; ++y)
+        {
+            float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+            if (dist < sqdist.x)
+            {
+                sqdist.y = sqdist.x;
+                sqdist.x = dist;
+            }
+            else if (dist < sqdist.y)
+            {
+                sqdist.y = dist;
+            }
+        }
+    }
+    if (metric == 0)
+        sqdist = sqrt(sqdist);
+    return sqdist;
+}
+
+vec3 mx_worley_noise_vec3(vec2 p, float jitter, int metric)
+{
+    int X, Y;
+    vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+    vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
+    for (int x = -1; x <= 1; ++x)
+    {
+        for (int y = -1; y <= 1; ++y)
+        {
+            float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+            if (dist < sqdist.x)
+            {
+                sqdist.z = sqdist.y;
+                sqdist.y = sqdist.x;
+                sqdist.x = dist;
+            }
+            else if (dist < sqdist.y)
+            {
+                sqdist.z = sqdist.y;
+                sqdist.y = dist;
+            }
+            else if (dist < sqdist.z)
+            {
+                sqdist.z = dist;
+            }
+        }
+    }
+    if (metric == 0)
+        sqdist = sqrt(sqdist);
+    return sqdist;
+}
+
+float mx_worley_noise_float(vec3 p, float jitter, int metric)
+{
+    int X, Y, Z;
+    vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+    float sqdist = 1e6f;
+    for (int x = -1; x <= 1; ++x)
+    {
+        for (int y = -1; y <= 1; ++y)
+        {
+            for (int z = -1; z <= 1; ++z)
+            {
+                float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+                sqdist = min(sqdist, dist);
+            }
+        }
+    }
+    if (metric == 0)
+        sqdist = sqrt(sqdist);
+    return sqdist;
+}
+
+vec2 mx_worley_noise_vec2(vec3 p, float jitter, int metric)
+{
+    int X, Y, Z;
+    vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+    vec2 sqdist = vec2(1e6f, 1e6f);
+    for (int x = -1; x <= 1; ++x)
+    {
+        for (int y = -1; y <= 1; ++y)
+        {
+            for (int z = -1; z <= 1; ++z)
+            {
+                float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+                if (dist < sqdist.x)
+                {
+                    sqdist.y = sqdist.x;
+                    sqdist.x = dist;
+                }
+                else if (dist < sqdist.y)
+                {
+                    sqdist.y = dist;
+                }
+            }
+        }
+    }
+    if (metric == 0)
+        sqdist = sqrt(sqdist);
+    return sqdist;
+}
+
+vec3 mx_worley_noise_vec3(vec3 p, float jitter, int metric)
+{
+    int X, Y, Z;
+    vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+    vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
+    for (int x = -1; x <= 1; ++x)
+    {
+        for (int y = -1; y <= 1; ++y)
+        {
+            for (int z = -1; z <= 1; ++z)
+            {
+                float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+                if (dist < sqdist.x)
+                {
+                    sqdist.z = sqdist.y;
+                    sqdist.y = sqdist.x;
+                    sqdist.x = dist;
+                }
+                else if (dist < sqdist.y)
+                {
+                    sqdist.z = sqdist.y;
+                    sqdist.y = dist;
+                }
+                else if (dist < sqdist.z)
+                {
+                    sqdist.z = dist;
+                }
+            }
+        }
+    }
+    if (metric == 0)
+        sqdist = sqrt(sqdist);
+    return sqdist;
+}` );
+
+const includes = [ mx_noise ];
+
+export const mx_perlin_noise_float = fn( 'float mx_perlin_noise_float( vec3 p )', includes );
+export const mx_cell_noise_float = fn( 'float mx_cell_noise_float( vec3 p )', includes );
+export const mx_worley_noise_float = fn( 'float mx_worley_noise_float( vec3 p, float jitter, int metric )', includes );
+export const mx_fractal_noise_float = fn( 'float mx_fractal_noise_float( vec3 p, int octaves, float lacunarity, float diminish )', includes );

+ 21 - 6
examples/jsm/nodes/parsers/GLSLNodeFunction.js

@@ -117,18 +117,33 @@ class GLSLNodeFunction extends NodeFunction {
 
 
 	getCode( name = this.name ) {
 	getCode( name = this.name ) {
 
 
-		const headerCode = this.headerCode;
-		const presicion = this.presicion;
+		let code;
 
 
-		let declarationCode = `${ this.type } ${ name } ( ${ this.inputsCode.trim() } )`;
+		const blockCode = this.blockCode;
 
 
-		if ( presicion !== '' ) {
+		if ( blockCode !== '' ) {
 
 
-			declarationCode = `${ presicion } ${ declarationCode }`;
+			const { type, inputsCode, headerCode, presicion } = this;
+
+			let declarationCode = `${ type } ${ name } ( ${ inputsCode.trim() } )`;
+
+			if ( presicion !== '' ) {
+
+				declarationCode = `${ presicion } ${ declarationCode }`;
+
+			}
+
+			code = headerCode + declarationCode + blockCode;
+
+		} else {
+
+			// interface function
+
+			code = '';
 
 
 		}
 		}
 
 
-		return headerCode + declarationCode + this.blockCode;
+		return code;
 
 
 	}
 	}
 
 

+ 5 - 3
examples/jsm/nodes/shadernode/ShaderNodeBaseElements.js

@@ -87,12 +87,12 @@ export const bmat4 = new ConvertType( 'bmat4' );
 
 
 // @TODO: ArrayUniformNode
 // @TODO: ArrayUniformNode
 
 
-export const func = ( code ) => {
+export const func = ( code, includes ) => {
 
 
-	const node = nodeObject( new FunctionNode( code ) );
+	const node = nodeObject( new FunctionNode( code, includes ) );
 
 
 	const call = node.call.bind( node );
 	const call = node.call.bind( node );
-	node.call = ( params ) => nodeObject( call( params ) );
+	node.call = ( ...params ) => nodeObject( call( params.length > 1 || params[ 0 ]?.isNode === true ? nodeArray( params ) : nodeObjects( params[ 0 ] ) ) );
 
 
 	return node;
 	return node;
 
 
@@ -109,6 +109,8 @@ export const uniform = ( nodeOrType ) => {
 
 
 };
 };
 
 
+export const fn = ( code, includes ) => func( code, includes ).call;
+
 export const attribute = ( name, nodeType ) => nodeObject( new AttributeNode( name, nodeType ) );
 export const attribute = ( name, nodeType ) => nodeObject( new AttributeNode( name, nodeType ) );
 export const property = ( name, nodeOrType ) => nodeObject( new PropertyNode( name, getConstNodeType( nodeOrType ) ) );
 export const property = ( name, nodeOrType ) => nodeObject( new PropertyNode( name, getConstNodeType( nodeOrType ) ) );
 
 

二进制
examples/screenshots/webgl_nodes_materialx_noise.jpg


+ 214 - 0
examples/webgl_nodes_materialx_noise.html

@@ -0,0 +1,214 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - materials - materialx nodes</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - MaterialX - Noise
+		</div>
+
+		<!-- Import maps polyfill -->
+		<!-- Remove this when import maps will be widely supported -->
+		<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three-nodes/": "./jsm/nodes/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+			import { MeshPhysicalNodeMaterial, add, mul, normalWorld, saturate, timerLocal } from 'three-nodes/Nodes.js';
+
+			import {
+				mx_perlin_noise_float,
+				mx_cell_noise_float,
+				mx_worley_noise_float,
+				mx_fractal_noise_float
+			} from 'three-nodes/materialx/functions/lib/mx_noise.js';
+
+			import { nodeFrame } from './jsm/renderers/webgl/nodes/WebGLNodes.js';
+
+			import Stats from './jsm/libs/stats.module.js';
+
+			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { HDRCubeTextureLoader } from './jsm/loaders/HDRCubeTextureLoader.js';
+
+			let container, stats;
+
+			let camera, scene, renderer;
+
+			let particleLight;
+			let group;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.position.z = 1000;
+
+				scene = new THREE.Scene();
+
+				group = new THREE.Group();
+				scene.add( group );
+
+				new HDRCubeTextureLoader()
+					.setPath( 'textures/cube/pisaHDR/' )
+					.load( [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ],
+						  function ( hdrTexture ) {
+
+							const geometry = new THREE.SphereGeometry( 80, 64, 32 );
+
+							const offsetNode = timerLocal();
+							const customUV = add( mul( normalWorld, 10 ), offsetNode );
+
+							// left top
+
+							let material = new MeshPhysicalNodeMaterial();
+							material.colorNode = mx_perlin_noise_float( customUV );
+
+							let mesh = new THREE.Mesh( geometry, material );
+							mesh.position.x = - 100;
+							mesh.position.y = 100;
+							group.add( mesh );
+
+							// right top
+
+							material = new MeshPhysicalNodeMaterial();
+							material.colorNode = mx_cell_noise_float( customUV );
+
+							mesh = new THREE.Mesh( geometry, material );
+							mesh.position.x = 100;
+							mesh.position.y = 100;
+							group.add( mesh );
+
+							// left bottom
+
+							material = new MeshPhysicalNodeMaterial();
+							material.colorNode = mx_worley_noise_float( customUV, 1, 1 );
+
+							mesh = new THREE.Mesh( geometry, material );
+							mesh.position.x = - 100;
+							mesh.position.y = - 100;
+							group.add( mesh );
+
+							// right bottom
+
+							material = new MeshPhysicalNodeMaterial();
+							material.colorNode = saturate( mul( add( mx_fractal_noise_float( mul( customUV, .2 ), 7, 2, .7 ), 1 ), .5 ) );
+
+							mesh = new THREE.Mesh( geometry, material );
+							mesh.position.x = 100;
+							mesh.position.y = - 100;
+							group.add( mesh );
+
+							//
+
+							scene.background = hdrTexture;
+							scene.environment = hdrTexture;
+
+						}
+
+						 );
+
+				// LIGHTS
+
+				particleLight = new THREE.Mesh(
+					new THREE.SphereGeometry( 4, 8, 8 ),
+					new THREE.MeshBasicMaterial( { color: 0xffffff } )
+				);
+				scene.add( particleLight );
+
+				particleLight.add( new THREE.PointLight( 0xffffff, 1 ) );
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				//
+
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.toneMappingExposure = 1.25;
+
+				//
+
+				renderer.outputEncoding = THREE.sRGBEncoding;
+
+				//
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				// EVENTS
+
+				new OrbitControls( camera, renderer.domElement );
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			//
+
+			function onWindowResize() {
+
+				const width = window.innerWidth;
+				const height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				nodeFrame.update();
+
+				render();
+
+				stats.update();
+
+			}
+
+			function render() {
+
+				const timer = Date.now() * 0.00025;
+
+				particleLight.position.x = Math.sin( timer * 7 ) * 300;
+				particleLight.position.y = Math.cos( timer * 5 ) * 400;
+				particleLight.position.z = Math.cos( timer * 3 ) * 300;
+
+				for ( let i = 0; i < group.children.length; i ++ ) {
+
+					const child = group.children[ i ];
+					child.rotation.y += 0.005;
+
+				}
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>