Explorar o código

Renamed material class and reduced allocations.

Mr.doob hai 3 meses
pai
achega
c4c8657a47

+ 33 - 26
examples/jsm/utils/VolumeMesh.js

@@ -1,12 +1,12 @@
 import { Mesh, BoxGeometry, Data3DTexture, RGBAFormat, FloatType, LinearFilter, Matrix4, Vector3, Vector2, Quaternion, Ray, DoubleSide, Triangle } from 'three';
-import { RayMarchSDFMaterial } from './RayMarchSDFMaterial.js';
+import { VolumeStandardMaterial } from './VolumeStandardMaterial.js';
 
 export class VolumeMesh extends Mesh {
 
 	constructor( params = {} ) {
 
 		const geometry = new BoxGeometry( 1, 1, 1 );
-		const material = new RayMarchSDFMaterial( {
+		const material = new VolumeStandardMaterial( {
 			roughness: params.roughness !== undefined ? params.roughness : 1.0,
 			metalness: params.metalness !== undefined ? params.metalness : 1.0
 		} );
@@ -79,6 +79,24 @@ export class VolumeMesh extends Mesh {
 		};
 		const uvAttr = geometry.attributes.uv;
 
+		// Reusable objects to avoid allocations in the loop
+		const ray = new Ray();
+		const directions = [
+			new Vector3( 1, 0, 0 ),
+			new Vector3( - 1, 0, 0 ),
+			new Vector3( 0, 1, 0 ),
+			new Vector3( 0, - 1, 0 ),
+			new Vector3( 0, 0, 1 ),
+			new Vector3( 0, 0, - 1 )
+		];
+		const v0 = new Vector3();
+		const v1 = new Vector3();
+		const v2 = new Vector3();
+		const barycoord = new Vector3();
+		const uv0 = new Vector2();
+		const uv1 = new Vector2();
+		const uv2 = new Vector2();
+
 		// Iterate over all pixels and check distance
 		for ( let x = 0; x < dim; x ++ ) {
 
@@ -109,22 +127,18 @@ export class VolumeMesh extends Mesh {
 					// Check if the point is inside or outside by raycasting
 					// If we hit a back face then we're inside
 					let insideCount = 0;
-					const ray = new Ray( point );
-					const directions = [
-						new Vector3( 1, 0, 0 ),
-						new Vector3( -1, 0, 0 ),
-						new Vector3( 0, 1, 0 ),
-						new Vector3( 0, -1, 0 ),
-						new Vector3( 0, 0, 1 ),
-						new Vector3( 0, 0, -1 )
-					];
-
-					for( let i = 0; i < 6; i ++ ) {
+					ray.origin.copy( point );
+
+					for ( let i = 0; i < 6; i ++ ) {
+
 						ray.direction.copy( directions[ i ] );
 						const hit = bvh.raycastFirst( ray, DoubleSide );
 						if ( hit && hit.face.normal.dot( ray.direction ) > 0.0 ) {
+
 							insideCount ++;
+
 						}
+
 					}
 
 					const isInside = insideCount > 3;
@@ -143,16 +157,15 @@ export class VolumeMesh extends Mesh {
 						const i1 = indexAttr.getX( faceIndex * 3 + 1 );
 						const i2 = indexAttr.getX( faceIndex * 3 + 2 );
 
-						const v0 = new Vector3().fromBufferAttribute( geometry.attributes.position, i0 );
-						const v1 = new Vector3().fromBufferAttribute( geometry.attributes.position, i1 );
-						const v2 = new Vector3().fromBufferAttribute( geometry.attributes.position, i2 );
+						v0.fromBufferAttribute( geometry.attributes.position, i0 );
+						v1.fromBufferAttribute( geometry.attributes.position, i1 );
+						v2.fromBufferAttribute( geometry.attributes.position, i2 );
 
-						const barycoord = new Vector3();
 						Triangle.getBarycoord( target.point, v0, v1, v2, barycoord );
 
-						const uv0 = new Vector2().fromBufferAttribute( uvAttr, i0 );
-						const uv1 = new Vector2().fromBufferAttribute( uvAttr, i1 );
-						const uv2 = new Vector2().fromBufferAttribute( uvAttr, i2 );
+						uv0.fromBufferAttribute( uvAttr, i0 );
+						uv1.fromBufferAttribute( uvAttr, i1 );
+						uv2.fromBufferAttribute( uvAttr, i2 );
 
 						u = uv0.x * barycoord.x + uv1.x * barycoord.y + uv2.x * barycoord.z;
 						v = uv0.y * barycoord.x + uv1.y * barycoord.y + uv2.y * barycoord.z;
@@ -225,12 +238,6 @@ export class VolumeMesh extends Mesh {
 
 		}
 
-		// Compute normal matrix: normalMatrix = transpose(inverse(modelViewMatrix))
-		// For transforming normals from local space to view space
-		const normalMatrix = new Matrix4();
-		normalMatrix.copy( this.modelViewMatrix ).invert().transpose();
-		this.material.uniforms.sdfNormalMatrix.value.copy( normalMatrix );
-
 	}
 
 	dispose() {

+ 40 - 20
examples/jsm/utils/RayMarchSDFMaterial.js → examples/jsm/utils/VolumeStandardMaterial.js

@@ -1,21 +1,22 @@
-import { MeshStandardMaterial, Matrix4, Vector3 } from 'three';
+import { MeshStandardMaterial, Vector3, BackSide } from 'three';
 
-export class RayMarchSDFMaterial extends MeshStandardMaterial {
+export class VolumeStandardMaterial extends MeshStandardMaterial {
 
 	constructor( params ) {
 
 		super( params );
 
+		this.side = BackSide;
+
 		this.uniforms = {
 			sdfTex: { value: null },
 			normalStep: { value: new Vector3() },
-			sdfNormalMatrix: { value: new Matrix4() },
 			surface: { value: 0 }
 		};
 
 		this.defines = {
-			MAX_STEPS: 500,
-			SURFACE_EPSILON: 0.001
+			MAX_STEPS: 50,
+			SURFACE_EPSILON: 0.0001
 		};
 
 		this.onBeforeCompile = ( shader ) => {
@@ -23,7 +24,6 @@ export class RayMarchSDFMaterial extends MeshStandardMaterial {
 			// Add our custom uniforms
 			shader.uniforms.sdfTex = this.uniforms.sdfTex;
 			shader.uniforms.normalStep = this.uniforms.normalStep;
-			shader.uniforms.sdfNormalMatrix = this.uniforms.sdfNormalMatrix;
 			shader.uniforms.surface = this.uniforms.surface;
 
 			// Add our defines
@@ -54,7 +54,9 @@ export class RayMarchSDFMaterial extends MeshStandardMaterial {
 
 				uniform sampler3D sdfTex;
 				uniform vec3 normalStep;
-				uniform mat4 sdfNormalMatrix;
+				uniform mat3 normalMatrix;
+				uniform mat4 modelViewMatrix;
+				uniform mat4 projectionMatrix;
 				uniform float surface;
 
 				varying vec3 vLocalPosition;
@@ -77,41 +79,59 @@ export class RayMarchSDFMaterial extends MeshStandardMaterial {
 			shader.fragmentShader = shader.fragmentShader.replace(
 				'void main() {',
 				`void main() {
-				// Raymarch from camera through the box in local space
+				// Raymarch from entry point to back face (current fragment) in local space
 				vec3 rayOrigin = vLocalRayOrigin;
 				vec3 rayDirection = normalize( vLocalPosition - vLocalRayOrigin );
-				
+
 				// Find intersection with SDF bounds [-0.5, 0.5]
 				vec2 boxIntersectionInfo = rayBoxDist( vec3( - 0.5 ), vec3( 0.5 ), rayOrigin, rayDirection );
 				float distToBox = boxIntersectionInfo.x;
 				float distInsideBox = boxIntersectionInfo.y;
-				bool intersectsBox = distInsideBox > 0.0;
-				
-				if ( !intersectsBox ) {
-					discard;
-				}
-				
-				// Raymarch to find surface in SDF local space
+
+				// Start from the entry point (or camera if inside)
+				distToBox = max( distToBox, 0.0 );
+
+				// Compute distance to back face (current fragment position)
+				float distToBackFace = length( vLocalPosition - rayOrigin );
+
+				// Raymarch from entry to back face to find surface in SDF
 				bool intersectsSurface = false;
-				vec3 localPoint = rayOrigin + rayDirection * ( distToBox + 1e-5 );
-				
+				vec3 localPoint = rayOrigin + rayDirection * distToBox;
+				float marchDist = distToBox;
+
 				for ( int i = 0; i < MAX_STEPS; i ++ ) {
+
+					// Stop if we've reached the back face
+					if ( marchDist >= distToBackFace ) {
+						break;
+					}
+
 					vec3 sdfUV = localPoint + vec3( 0.5 );
 					if ( sdfUV.x < 0.0 || sdfUV.x > 1.0 || sdfUV.y < 0.0 || sdfUV.y > 1.0 || sdfUV.z < 0.0 || sdfUV.z > 1.0 ) {
 						break;
 					}
+
 					float distanceToSurface = texture( sdfTex, sdfUV ).r - surface;
 					if ( abs( distanceToSurface ) < SURFACE_EPSILON ) {
 						intersectsSurface = true;
 						break;
 					}
-					localPoint += rayDirection * distanceToSurface * 0.5;
+
+					float stepSize = distanceToSurface * 0.5;
+					localPoint += rayDirection * stepSize;
+					marchDist += stepSize;
 				}
 
 				if ( !intersectsSurface ) {
 					discard;
 				}
 
+				// Write correct depth for the raymarched surface
+				vec4 viewPos = modelViewMatrix * vec4( localPoint, 1.0 );
+				vec4 clipPos = projectionMatrix * viewPos;
+				float ndcDepth = clipPos.z / clipPos.w;
+				gl_FragDepth = ndcDepth * 0.5 + 0.5;
+
 				// Compute UV and normal from SDF
 				vec3 sdfUV = localPoint + vec3( 0.5 );
 				vec4 sdfData = texture( sdfTex, sdfUV );
@@ -124,7 +144,7 @@ export class RayMarchSDFMaterial extends MeshStandardMaterial {
 				vec3 sdfNormalLocal = normalize( vec3( dx, dy, dz ) );
 
 				// Transform normal from SDF local space to view space
-				vec3 sdfNormal = normalize( ( sdfNormalMatrix * vec4( sdfNormalLocal, 0.0 ) ).xyz );
+				vec3 sdfNormal = normalize( normalMatrix * sdfNormalLocal );
 				`
 			);
 

+ 8 - 13
examples/webgl_volume_mesh.html

@@ -219,10 +219,10 @@
 				if ( volumeMeshes.length === 0 || ! sourceMesh ) return;
 
 				// Position the first one
-				volumeMeshes[ 0 ].position.x = - 1.5;
+				volumeMeshes[ 0 ].position.x = 0;
 
 				// Create 2 more volumes
-				for ( let i = 1; i < 3; i ++ ) {
+				for ( let i = 1; i < 100; i ++ ) {
 
 					const volume = new VolumeMesh( {
 						resolution: params.resolution,
@@ -249,17 +249,12 @@
 
 					}
 
-					// Set scale and position
-					const sdfBoundsMatrix = volume.inverseBoundsMatrix.clone().invert();
-					const boundsCenter = new THREE.Vector3();
-					const boundsQuat = new THREE.Quaternion();
-					const boundsScale = new THREE.Vector3();
-					sdfBoundsMatrix.decompose( boundsCenter, boundsQuat, boundsScale );
-
-					volume.scale.copy( boundsScale );
-					volume.position.copy( boundsCenter );
-					volume.position.x = i * 1.5;
-					volume.updateMatrixWorld();
+					volume.position.set(
+						( Math.random() - 0.5 ) * 8,
+						( Math.random() - 0.5 ) * 8,
+						( Math.random() - 0.5 ) * 8
+					);
+					volume.rotation.set( Math.random() * Math.PI, Math.random() * Math.PI, Math.random() * Math.PI );
 
 					scene.add( volume );
 					volumeMeshes.push( volume );

粤ICP备19079148号