Selaa lähdekoodia

Volumetric NURBS (#27601)

* Update Addons.js

* Update NURBSUtils.js

Fixed inconsistent naming of parameters in comment

* Update NURBSUtils.js

* Create NURBSVolume.js

* Update webgl_geometry_nurbs.html

Expanded NURBS example to NURBSVolume objects.

* Update webgl_geometry_nurbs.html

Added missing include

* Update webgl_geometry_nurbs.html

Improve example.

* Updated E2E screenshot

* Updated E2E screenshot

---------

Co-authored-by: Michael Herzog <[email protected]>
Matthias Möller 1 vuosi sitten
vanhempi
commit
02d41f89d5

+ 1 - 0
examples/jsm/Addons.js

@@ -25,6 +25,7 @@ export * from './csm/CSMShader.js';
 export * as Curves from './curves/CurveExtras.js';
 export * from './curves/NURBSCurve.js';
 export * from './curves/NURBSSurface.js';
+export * from './curves/NURBSVolume.js';
 export * as NURBSUtils from './curves/NURBSUtils.js';
 
 export * from './effects/AnaglyphEffect.js';

+ 59 - 4
examples/jsm/curves/NURBSUtils.js

@@ -429,10 +429,10 @@ function calcNURBSDerivatives( p, U, P, u, nd ) {
 /*
 Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
 
-p1, p2 : degrees of B-Spline surface
-U1, U2 : knot vectors
-P      : control points (x, y, z, w)
-u, v   : parametric values
+p, q : degrees of B-Spline surface
+U, V : knot vectors
+P    : control points (x, y, z, w)
+u, v : parametric values
 
 returns point for given (u, v)
 */
@@ -472,6 +472,60 @@ function calcSurfacePoint( p, q, U, V, P, u, v, target ) {
 
 }
 
+/*
+Calculate rational B-Spline volume point. See The NURBS Book, page 134, algorithm A4.3.
+
+p, q, r   : degrees of B-Splinevolume
+U, V, W   : knot vectors
+P         : control points (x, y, z, w)
+u, v, w   : parametric values
+
+returns point for given (u, v, w)
+*/
+function calcVolumePoint( p, q, r, U, V, W, P, u, v, w, target ) {
+
+	const uspan = findSpan( p, u, U );
+	const vspan = findSpan( q, v, V );
+	const wspan = findSpan( r, w, W );
+	const Nu = calcBasisFunctions( uspan, u, p, U );
+	const Nv = calcBasisFunctions( vspan, v, q, V );
+	const Nw = calcBasisFunctions( wspan, w, r, W );
+	const temp = [];
+
+	for ( let m = 0; m <= r; ++ m ) {
+
+		temp[ m ] = [];
+
+		for ( let l = 0; l <= q; ++ l ) {
+
+			temp[ m ][ l ] = new Vector4( 0, 0, 0, 0 );
+			for ( let k = 0; k <= p; ++ k ) {
+
+				const point = P[ uspan - p + k ][ vspan - q + l ][ wspan - r + m ].clone();
+				const w = point.w;
+				point.x *= w;
+				point.y *= w;
+				point.z *= w;
+				temp[ m ][ l ].add( point.multiplyScalar( Nu[ k ] ) );
+
+			}
+
+		}
+
+	}
+	const Sw = new Vector4( 0, 0, 0, 0 );
+	for ( let m = 0; m <= r; ++ m ) {
+		for ( let l = 0; l <= q; ++ l ) {
+
+			Sw.add( temp[ m ][ l ].multiplyScalar( Nw[ m ] ).multiplyScalar( Nv[ l ] ) );
+
+		}
+	}
+
+	Sw.divideScalar( Sw.w );
+	target.set( Sw.x, Sw.y, Sw.z );
+
+}
 
 
 export {
@@ -484,4 +538,5 @@ export {
 	calcRationalCurveDerivatives,
 	calcNURBSDerivatives,
 	calcSurfacePoint,
+	calcVolumePoint,
 };

+ 62 - 0
examples/jsm/curves/NURBSVolume.js

@@ -0,0 +1,62 @@
+import {
+	Vector4
+} from 'three';
+import * as NURBSUtils from '../curves/NURBSUtils.js';
+
+/**
+ * NURBS volume object
+ *
+ * Implementation is based on (x, y, z [, w=1]]) control points with w=weight.
+ **/
+
+class NURBSVolume {
+
+	constructor( degree1, degree2, degree3, knots1, knots2, knots3 /* arrays of reals */, controlPoints /* array^3 of Vector(2|3|4) */ ) {
+
+		this.degree1 = degree1;
+		this.degree2 = degree2;
+		this.degree3 = degree3;
+		this.knots1 = knots1;
+		this.knots2 = knots2;
+		this.knots3 = knots3;
+		this.controlPoints = [];
+
+		const len1 = knots1.length - degree1 - 1;
+		const len2 = knots2.length - degree2 - 1;
+		const len3 = knots3.length - degree3 - 1;
+
+		// ensure Vector4 for control points
+		for ( let i = 0; i < len1; ++ i ) {
+
+			this.controlPoints[ i ] = [];
+
+			for ( let j = 0; j < len2; ++ j ) {
+
+				this.controlPoints[ i ][ j ] = [];
+
+				for ( let k = 0; k < len3; ++ k ) {
+
+					const point = controlPoints[ i ][ j ][ k ];
+					this.controlPoints[ i ][ j ][ k ] = new Vector4( point.x, point.y, point.z, point.w );
+
+				}
+
+			}
+
+		}
+
+	}
+
+	getPoint( t1, t2, t3, target ) {
+
+		const u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u
+		const v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->v
+		const w = this.knots3[ 0 ] + t3 * ( this.knots3[ this.knots3.length - 1 ] - this.knots3[ 0 ] ); // linear mapping t3->w
+
+		NURBSUtils.calcVolumePoint( this.degree1, this.degree2, this.degree3, this.knots1, this.knots2, this.knots3, this.controlPoints, u, v, w, target );
+
+	}
+
+}
+
+export { NURBSVolume };

BIN
examples/screenshots/webgl_geometry_nurbs.jpg


+ 192 - 45
examples/webgl_geometry_nurbs.html

@@ -37,6 +37,7 @@
 
 			import { NURBSCurve } from 'three/addons/curves/NURBSCurve.js';
 			import { NURBSSurface } from 'three/addons/curves/NURBSSurface.js';
+			import { NURBSVolume } from 'three/addons/curves/NURBSVolume.js';
 			import { ParametricGeometry } from 'three/addons/geometries/ParametricGeometry.js';
 
 			let container, stats;
@@ -112,7 +113,7 @@
 				const nurbsMaterial = new THREE.LineBasicMaterial( { color: 0x333333 } );
 
 				const nurbsLine = new THREE.Line( nurbsGeometry, nurbsMaterial );
-				nurbsLine.position.set( 200, - 100, 0 );
+				nurbsLine.position.set( 0, - 100, 0 );
 				group.add( nurbsLine );
 
 				const nurbsControlPointsGeometry = new THREE.BufferGeometry();
@@ -125,51 +126,197 @@
 				group.add( nurbsControlPointsLine );
 
 				// NURBS surface
-
-				const nsControlPoints = [
-					[
-						new THREE.Vector4( - 200, - 200, 100, 1 ),
-						new THREE.Vector4( - 200, - 100, - 200, 1 ),
-						new THREE.Vector4( - 200, 100, 250, 1 ),
-						new THREE.Vector4( - 200, 200, - 100, 1 )
-					],
-					[
-						new THREE.Vector4( 0, - 200, 0, 1 ),
-						new THREE.Vector4( 0, - 100, - 100, 5 ),
-						new THREE.Vector4( 0, 100, 150, 5 ),
-						new THREE.Vector4( 0, 200, 0, 1 )
-					],
-					[
-						new THREE.Vector4( 200, - 200, - 100, 1 ),
-						new THREE.Vector4( 200, - 100, 200, 1 ),
-						new THREE.Vector4( 200, 100, - 250, 1 ),
-						new THREE.Vector4( 200, 200, 100, 1 )
-					]
-				];
-				const degree1 = 2;
-				const degree2 = 3;
-				const knots1 = [ 0, 0, 0, 1, 1, 1 ];
-				const knots2 = [ 0, 0, 0, 0, 1, 1, 1, 1 ];
-				const nurbsSurface = new NURBSSurface( degree1, degree2, knots1, knots2, nsControlPoints );
-
-				const map = new THREE.TextureLoader().load( 'textures/uv_grid_opengl.jpg' );
-				map.wrapS = map.wrapT = THREE.RepeatWrapping;
-				map.anisotropy = 16;
-				map.colorSpace = THREE.SRGBColorSpace;
-
-				function getSurfacePoint( u, v, target ) {
-
-					return nurbsSurface.getPoint( u, v, target );
-
+				{
+
+					const nsControlPoints = [
+						[
+							new THREE.Vector4( - 200, - 200, 100, 1 ),
+							new THREE.Vector4( - 200, - 100, - 200, 1 ),
+							new THREE.Vector4( - 200, 100, 250, 1 ),
+							new THREE.Vector4( - 200, 200, - 100, 1 )
+						],
+						[
+							new THREE.Vector4( 0, - 200, 0, 1 ),
+							new THREE.Vector4( 0, - 100, - 100, 5 ),
+							new THREE.Vector4( 0, 100, 150, 5 ),
+							new THREE.Vector4( 0, 200, 0, 1 )
+						],
+						[
+							new THREE.Vector4( 200, - 200, - 100, 1 ),
+							new THREE.Vector4( 200, - 100, 200, 1 ),
+							new THREE.Vector4( 200, 100, - 250, 1 ),
+							new THREE.Vector4( 200, 200, 100, 1 )
+						]
+					];
+					const degree1 = 2;
+					const degree2 = 3;
+					const knots1 = [ 0, 0, 0, 1, 1, 1 ];
+					const knots2 = [ 0, 0, 0, 0, 1, 1, 1, 1 ];
+					const nurbsSurface = new NURBSSurface( degree1, degree2, knots1, knots2, nsControlPoints );
+
+					const map = new THREE.TextureLoader().load( 'textures/uv_grid_opengl.jpg' );
+					map.wrapS = map.wrapT = THREE.RepeatWrapping;
+					map.anisotropy = 16;
+					map.colorSpace = THREE.SRGBColorSpace;
+
+					function getSurfacePoint( u, v, target ) {
+
+						return nurbsSurface.getPoint( u, v, target );
+
+					}
+
+					const geometry = new ParametricGeometry( getSurfacePoint, 20, 20 );
+					const material = new THREE.MeshLambertMaterial( { map: map, side: THREE.DoubleSide } );
+					const object = new THREE.Mesh( geometry, material );
+					object.position.set( - 400, 100, 0 );
+					object.scale.multiplyScalar( 1 );
+					group.add( object );
+			
 				}
-
-				const geometry = new ParametricGeometry( getSurfacePoint, 20, 20 );
-				const material = new THREE.MeshLambertMaterial( { map: map, side: THREE.DoubleSide } );
-				const object = new THREE.Mesh( geometry, material );
-				object.position.set( - 200, 100, 0 );
-				object.scale.multiplyScalar( 1 );
-				group.add( object );
-
+			
+				// NURBS volume
+				{
+
+					const nsControlPoints = [
+						[
+							[
+								new THREE.Vector4( - 200, - 200, - 200, 1 ),
+								new THREE.Vector4( - 200, - 200, 200, 1 )
+							],
+							[
+								new THREE.Vector4( - 200, - 100, - 200, 1 ),
+								new THREE.Vector4( - 200, - 100, 200, 1 )
+							],
+							[
+								new THREE.Vector4( - 200, 100, - 200, 1 ),
+								new THREE.Vector4( - 200, 100, 200, 1 )
+							],
+							[
+								new THREE.Vector4( - 200, 200, - 200, 1 ),
+								new THREE.Vector4( - 200, 200, 200, 1 )
+							]
+						],
+						[
+							[
+								new THREE.Vector4( 0, - 200, - 200, 1 ),
+								new THREE.Vector4( 0, - 200, 200, 1 )
+							],
+							[
+								new THREE.Vector4( 0, - 100, - 200, 1 ),
+								new THREE.Vector4( 0, - 100, 200, 1 )
+							],
+							[
+								new THREE.Vector4( 0, 100, - 200, 1 ),
+								new THREE.Vector4( 0, 100, 200, 1 )
+							],
+							[
+								new THREE.Vector4( 0, 200, - 200, 1 ),
+								new THREE.Vector4( 0, 200, 200, 1 )
+							]
+						],
+						[
+							[
+								new THREE.Vector4( 200, - 200, - 200, 1 ),
+								new THREE.Vector4( 200, - 200, 200, 1 )
+							],
+							[
+								new THREE.Vector4( 200, - 100, 0, 1 ),
+								new THREE.Vector4( 200, - 100, 100, 1 )
+							],
+							[
+								new THREE.Vector4( 200, 100, 0, 1 ),
+								new THREE.Vector4( 200, 100, 100, 1 )
+							],
+							[
+								new THREE.Vector4( 200, 200, 0, 1 ),
+								new THREE.Vector4( 200, 200, 100, 1 )
+							]
+						]
+					];
+					const degree1 = 2;
+					const degree2 = 3;
+					const degree3 = 1;
+					const knots1 = [ 0, 0, 0, 1, 1, 1 ];
+					const knots2 = [ 0, 0, 0, 0, 1, 1, 1, 1 ];
+					const knots3 = [ 0, 0, 1, 1 ];
+					const nurbsVolume = new NURBSVolume( degree1, degree2, degree3, knots1, knots2, knots3, nsControlPoints );
+
+					const map = new THREE.TextureLoader().load( 'textures/uv_grid_opengl.jpg' );
+					map.wrapS = map.wrapT = THREE.RepeatWrapping;
+					map.anisotropy = 16;
+					map.colorSpace = THREE.SRGBColorSpace;
+
+					// Since ParametricGeometry() only support bi-variate parametric geometries
+					// we create evaluation functions for different surfaces with one of the three
+					// parameter values (u, v, w) kept constant and create multiple THREE.Mesh
+					// objects one for each surface
+					function getSurfacePointFront( u, v, target ) {
+
+						return nurbsVolume.getPoint( u, v, 0, target );
+
+					}
+
+					function getSurfacePointMiddle( u, v, target ) {
+
+						return nurbsVolume.getPoint( u, v, 0.5, target );
+
+					}
+
+					function getSurfacePointBack( u, v, target ) {
+
+						return nurbsVolume.getPoint( u, v, 1, target );
+
+					}
+
+					function getSurfacePointTop( u, w, target ) {
+
+						return nurbsVolume.getPoint( u, 1, w, target );
+
+					}
+
+					function getSurfacePointSide( v, w, target ) {
+
+						return nurbsVolume.getPoint( 0, v, w, target );
+
+					}
+
+					const geometryFront = new ParametricGeometry( getSurfacePointFront, 20, 20 );
+					const materialFront = new THREE.MeshLambertMaterial( { map: map, side: THREE.DoubleSide } );
+					const objectFront = new THREE.Mesh( geometryFront, materialFront );
+					objectFront.position.set( 400, 100, 0 );
+					objectFront.scale.multiplyScalar( 0.5 );
+					group.add( objectFront );
+
+					const geometryMiddle = new ParametricGeometry( getSurfacePointMiddle, 20, 20 );
+					const materialMiddle = new THREE.MeshLambertMaterial( { map: map, side: THREE.DoubleSide } );
+					const objectMiddle = new THREE.Mesh( geometryMiddle, materialMiddle );
+					objectMiddle.position.set( 400, 100, 0 );
+					objectMiddle.scale.multiplyScalar( 0.5 );
+					group.add( objectMiddle );
+
+					const geometryBack = new ParametricGeometry( getSurfacePointBack, 20, 20 );
+					const materialBack = new THREE.MeshLambertMaterial( { map: map, side: THREE.DoubleSide } );
+					const objectBack = new THREE.Mesh( geometryBack, materialBack );
+					objectBack.position.set( 400, 100, 0 );
+					objectBack.scale.multiplyScalar( 0.5 );
+					group.add( objectBack );
+
+					const geometryTop = new ParametricGeometry( getSurfacePointTop, 20, 20 );
+					const materialTop = new THREE.MeshLambertMaterial( { map: map, side: THREE.DoubleSide } );
+					const objectTop = new THREE.Mesh( geometryTop, materialTop );
+					objectTop.position.set( 400, 100, 0 );
+					objectTop.scale.multiplyScalar( 0.5 );
+					group.add( objectTop );
+
+					const geometrySide = new ParametricGeometry( getSurfacePointSide, 20, 20 );
+					const materialSide = new THREE.MeshLambertMaterial( { map: map, side: THREE.DoubleSide } );
+					const objectSide = new THREE.Mesh( geometrySide, materialSide );
+					objectSide.position.set( 400, 100, 0 );
+					objectSide.scale.multiplyScalar( 0.5 );
+					group.add( objectSide );
+			
+				}
+			
 				//
 
 				renderer = new THREE.WebGLRenderer( { antialias: true } );