2
0
Эх сурвалжийг харах

Merge pull request #14957 from WestLangley/dev-tex3d_volume

Volume rendering example: refactor and clean up
Mr.doob 6 жил өмнө
parent
commit
c01d68976b

+ 2 - 2
examples/js/shaders/VolumeShader.js

@@ -8,10 +8,10 @@
 
 THREE.VolumeRenderShader1 = {
 	uniforms: {
-        "u_size": { value: [1, 1, 1] },
+        "u_size": { value: new THREE.Vector3( 1, 1, 1 ) },
         "u_renderstyle": { value: 0 },
         "u_renderthreshold": { value: 0.5 },
-        "u_clim": { value: [0.0, 1.0] },
+        "u_clim": { value: new THREE.Vector2( 1, 1 ) },
         "u_data": { value: null },
         "u_cmdata": { value: null }
     },

+ 59 - 69
examples/webgl_materials_texture3d_volume.html

@@ -42,8 +42,7 @@
 
 <body>
 	<div id="info">
-		<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> -
-		Float volume render test (mip / isosurface)
+		<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - Float volume render test (mip / isosurface)
 	</div>
 	<div id="inset"></div>
 
@@ -61,18 +60,16 @@
 
 	<script>
 
-		if ( ! Detector.webgl ) { Detector.addGetWebGLMessage(); }
+		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
 		var container,
 			renderer,
 			scene,
 			camera,
 			controls,
-			volmaterial,
+			material,
 			volconfig,
-			cmtextures,
-			gui,
-			drawrequested;
+			cmtextures;
 
 		init();
 
@@ -84,28 +81,28 @@
 			var canvas = document.createElement( 'canvas' );
 			var context = canvas.getContext( 'webgl2' );
 			renderer = new THREE.WebGLRenderer( { canvas: canvas, context: context } );
-
-			// Attach renderer to DOM
-			container = document.createElement( 'div' );
-			document.body.appendChild( container );
-			container.appendChild( renderer.domElement );
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			document.body.appendChild( renderer.domElement );
 
 			// Create camera (The volume renderer does not work very well with perspective yet)
-			camera = new THREE.OrthographicCamera()
-			camera.position.z = 1000;
-			camera.near = 0.1;
-			camera.far = 5000;
-			camera.up = new THREE.Vector3( 0, 0, 1 );  // In our data, z is the up/down axis !!
-			scene.add( camera );
+			var h = 512; // frustum height
+			var aspect = window.innerWidth / window.innerHeight;
+			camera = new THREE.OrthographicCamera( - h * aspect / 2, h * aspect / 2, h / 2, - h / 2, 1, 1000 );
+			camera.position.set( 0, 0, 128 );
+			camera.up.set( 0, 0, 1 ); // In our data, z is up
 
 			// Create controls
 			controls = new THREE.OrbitControls( camera, renderer.domElement );
-			controls.addEventListener( 'change', animate );
+			controls.addEventListener( 'change', render );
 			controls.target.set( 64, 64, 128 );
-			controls.enableDamping  = false;
+			controls.minZoom = 0.5;
+			controls.maxZoom = 4;
 			controls.update();
 
-			// Lighting is backed into the shader a.t.m.
+			// scene.add( new THREE.AxesHelper( 128 ) );
+
+			// Lighting is baked into the shader a.t.m.
 			// var dirLight = new THREE.DirectionalLight( 0xffffff );
 
 			// The gui for interaction
@@ -117,14 +114,9 @@
 			gui.add( volconfig, 'renderstyle', { mip: 'mip', iso: 'iso' } ).onChange( updateUniforms );
 			gui.add( volconfig, 'isothreshold', 0, 1, 0.01 ).onChange( updateUniforms );
 
-			// Keep track of window size (and initialize now)
-			window.addEventListener( 'resize', onWindowResize, false );
-			onWindowResize();
-
 			// Load the data ...
-			var loader = new THREE.NRRDLoader();
-			loader.load( "models/nrrd/stent.nrrd", function ( volume ) {
-				window.volume = volume;
+			new THREE.NRRDLoader().load( "models/nrrd/stent.nrrd", function ( volume ) {
+
 				// Texture to hold the volume. We have scalars, so we put our data in the red channel.
 				// THREEJS will select R32F (33326) based on the RedFormat and FloatType.
 				// Also see https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE
@@ -138,74 +130,72 @@
 
 				// Colormap textures
 				cmtextures = {
-					viridis: new THREE.TextureLoader().load( 'textures/cm_viridis.png' ),
-					gray: new THREE.TextureLoader().load( 'textures/cm_gray.png' )
+					viridis: new THREE.TextureLoader().load( 'textures/cm_viridis.png', render ),
+					gray: new THREE.TextureLoader().load( 'textures/cm_gray.png', render )
 				};
 
-				// Material (shaders) to render the volume using raycasting
-				volmaterial = new THREE.ShaderMaterial( THREE.VolumeRenderShader1 );
-				volmaterial.side = THREE.BackSide;  // The volume shader uses the backface as its "reference point"
+				// Material
+				var shader = THREE.VolumeRenderShader1;
+
+				var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+				uniforms.u_data.value = texture;
+				uniforms.u_size.value.set( volume.xLength, volume.yLength, volume.zLength );
+				uniforms.u_clim.value.set( volconfig.clim1, volconfig.clim2 );
+				uniforms.u_renderstyle.value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO
+				uniforms.u_renderthreshold.value = volconfig.isothreshold; // For ISO renderstyle
+				uniforms.u_cmdata.value = cmtextures[ volconfig.colormap ];
 
-				// Apply standard volume material uniform info
-				volmaterial.uniforms.u_data.value = texture;
-				volmaterial.uniforms.u_size.value = [ volume.xLength, volume.yLength, volume.zLength ];
+				material = new THREE.ShaderMaterial( {
+					uniforms        : uniforms,
+					vertexShader    : shader.vertexShader,
+					fragmentShader  : shader.fragmentShader,
+					side: THREE.BackSide // The volume shader uses the backface as its "reference point"
+				} );
 
-				// Geometry for the volume
-				var volgeometry = new THREE.BoxGeometry( volume.xLength, volume.yLength, volume.zLength );
-				volgeometry = volgeometry.translate( volume.xLength / 2 - 0.5, volume.yLength / 2 - 0.5, volume.zLength / 2 - 0.5 );
-				var volcube = new THREE.Mesh( volgeometry, volmaterial );
-				scene.add( volcube );
+				// Mesh
+				var geometry = new THREE.BoxGeometry( volume.xLength, volume.yLength, volume.zLength );
+				geometry.translate( volume.xLength / 2 - 0.5, volume.yLength / 2 - 0.5, volume.zLength / 2 - 0.5 );
 
-				// Apply uniforms now, this will call animate(), but apparently that's not enough sometimes
-				this.updateUniforms();
-				window.setTimeout(animate, 100);
-				window.setTimeout(animate, 1000);
+				var mesh = new THREE.Mesh( geometry, material );
+				scene.add( mesh );
 
 			} );
 
+			window.addEventListener( 'resize', onWindowResize, false );
+
 		}
 
 		function updateUniforms() {
 
-			volmaterial.uniforms.u_clim.value = [ volconfig.clim1, volconfig.clim2 ];
-			volmaterial.uniforms.u_renderstyle.value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO
-			volmaterial.uniforms.u_renderthreshold.value = volconfig.isothreshold;  // For ISO renderstyle
-			volmaterial.uniforms.u_cmdata.value = cmtextures[volconfig.colormap];
+			material.uniforms.u_clim.value.set( volconfig.clim1, volconfig.clim2 );
+			material.uniforms.u_renderstyle.value = volconfig.renderstyle == 'mip' ? 0 : 1; // 0: MIP, 1: ISO
+			material.uniforms.u_renderthreshold.value = volconfig.isothreshold; // For ISO renderstyle
+			material.uniforms.u_cmdata.value = cmtextures[ volconfig.colormap ];
 
-			volmaterial.needsUpdate = true;
-			animate();
+			render();
 
 		}
 
 		function onWindowResize() {
 
-			camera.right = window.innerWidth / 5.0;
-			camera.left = -camera.right;
-			camera.top = window.innerHeight / 5.0;
-			camera.bottom = -camera.top;
-			camera.updateProjectionMatrix();
-
-			renderer.setPixelRatio( window.devicePixelRatio );
 			renderer.setSize( window.innerWidth, window.innerHeight );
 
-			if (controls.handleResize) { controls.handleResize(); }
+			var aspect = window.innerWidth / window.innerHeight;
 
-			animate();
-		}
+			var frustumHeight = camera.top - camera.bottom;
 
-		function animate() {
+			camera.left = - frustumHeight * aspect / 2;
+			camera.right = frustumHeight * aspect / 2;
+
+			camera.updateProjectionMatrix();
 
-			// throttled animate function
-			if ( !drawrequested ) {
-				drawrequested = true;
-				window.requestAnimationFrame( _animate );
-			}
+			render();
 
 		}
 
-		function _animate() {
+		function render() {
 
-			drawrequested = false;
 			renderer.render( scene, camera );
 
 		}