소스 검색

Examples: Add external three-mesh-bvh library demo (#23907)

* add external bvh demo

* update new example

* use bunny model

* screenshot

* final updates

* speed improvements

* wording update

* Update external_bvh_raycasting.html

* fixes

* fix links in iframe

* Change ray speed

* Update files.json

* change file name

* Update tags.json
Garrett Johnson 3 년 전
부모
커밋
d7452b3db4
4개의 변경된 파일305개의 추가작업 그리고 1개의 파일을 삭제
  1. 1 0
      examples/files.json
  2. BIN
      examples/screenshots/webgl_raycasting_bvh.jpg
  3. 2 1
      examples/tags.json
  4. 302 0
      examples/webgl_raycasting_bvh.html

+ 1 - 0
examples/files.json

@@ -198,6 +198,7 @@
 		"webgl_portal",
 		"webgl_raycast_sprite",
 		"webgl_raycast_texture",
+		"webgl_raycasting_bvh",
 		"webgl_read_float_buffer",
 		"webgl_refraction",
 		"webgl_rtt",

BIN
examples/screenshots/webgl_raycasting_bvh.jpg


+ 2 - 1
examples/tags.json

@@ -100,5 +100,6 @@
 	"misc_controls_map": [ "drag" ],
 	"misc_controls_orbit": [ "rotation" ],
 	"misc_controls_trackball": [ "rotation" ],
-	"misc_controls_transform": [ "scale", "rotate", "translate" ]
+	"misc_controls_transform": [ "scale", "rotate", "translate" ],
+	"webgl_raycasting_bvh": [ "three-mesh-bvh", "query", "bounds", "tree", "accelerate", "performance", "community", "extension", "plugin", "library" ]
 }

+ 302 - 0
examples/webgl_raycasting_bvh.html

@@ -0,0 +1,302 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three-mesh-bvh - raycasting - bvh</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">
+		<style>
+			body {
+				color: #333;
+			}
+
+			a {
+				color: #E91E63;
+				text-decoration: underline;
+			}
+		</style>
+	</head>
+
+	<body>
+		<div id="info">
+			<a href="https://github.com/gkjohnson/three-mesh-bvh" target="_blank" rel="noopener">three-mesh-bvh</a> accelerated raycasting demo<br/>
+			See <a href="https://github.com/gkjohnson/three-mesh-bvh" target="_blank" rel="noopener">main project repository</a> for more information and examples on high performance spatial queries.
+		</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-mesh-bvh": "https://unpkg.com/three-mesh-bvh@^0.5.10/build/index.module.js"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+			import { computeBoundsTree, disposeBoundsTree, acceleratedRaycast, MeshBVHVisualizer } from 'three-mesh-bvh';
+			import Stats from './jsm/libs/stats.module.js';
+			import { FBXLoader } from './jsm/loaders/FBXLoader.js';
+			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { GUI } from './jsm/libs/lil-gui.module.min.js';
+
+			// Add the extension functions
+			THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
+			THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;
+			THREE.Mesh.prototype.raycast = acceleratedRaycast;
+
+			let stats;
+			let camera, scene, renderer, controls;
+			let mesh, helper, bvh;
+			let sphereInstance, lineSegments;
+
+			// reusable variables
+			const _raycaster = new THREE.Raycaster();
+			const _position = new THREE.Vector3();
+			const _quaternion = new THREE.Quaternion();
+			const _scale = new THREE.Vector3( 1, 1, 1 );
+			const _matrix = new THREE.Matrix4();
+			const _axis = new THREE.Vector3();
+			const MAX_RAYS = 3000;
+			const RAY_COLOR = 0x444444;
+
+			const params = {
+
+				count: 150,
+				firstHitOnly: true,
+				useBVH: true,
+
+				displayHelper: false,
+				helperDepth: 10,
+
+			};
+
+			init();
+			animate();
+
+			function init() {
+
+				// environment
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 100 );
+				camera.position.z = 10;
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0xeeeeee );
+
+				const ambient = new THREE.HemisphereLight( 0xffffff, 0x999999 );
+				scene.add( ambient );
+
+				// renderer
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				stats = new Stats();
+				document.body.appendChild( stats.dom );
+
+				// raycast visualizations
+				const lineGeometry = new THREE.BufferGeometry();
+				lineGeometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( MAX_RAYS * 2 * 3 ), 3 ) );
+				lineSegments = new THREE.LineSegments( lineGeometry, new THREE.LineBasicMaterial( {
+					color: RAY_COLOR,
+					transparent: true,
+					opacity: 0.25,
+					depthWrite: false
+				} ) );
+
+				sphereInstance = new THREE.InstancedMesh(
+					new THREE.SphereBufferGeometry(),
+					new THREE.MeshBasicMaterial( { color: RAY_COLOR } ),
+					2 * MAX_RAYS
+				);
+				sphereInstance.instanceMatrix.setUsage( THREE.DynamicDrawUsage );
+				sphereInstance.count = 0;
+
+				scene.add( sphereInstance, lineSegments );
+
+				// load the bunny
+				const loader = new FBXLoader();
+				loader.load( 'models/fbx/stanford-bunny.fbx', object => {
+
+					mesh = object.children[ 0 ];
+
+					const geometry = mesh.geometry;
+					geometry.translate( 0, 0.5 / 0.0075, 0 );
+					geometry.computeBoundsTree();
+					bvh = geometry.boundsTree;
+
+					if ( ! params.useBVH ) {
+
+						geometry.boundsTree = null;
+
+					}
+
+					scene.add( mesh );
+					mesh.scale.setScalar( 0.0075 );
+
+					helper = new MeshBVHVisualizer( mesh );
+					helper.color.set( 0xE91E63 );
+					scene.add( helper );
+					
+				} );
+
+				controls = new OrbitControls( camera, renderer.domElement );
+
+				// set up gui
+				const gui = new GUI();
+				const rayFolder = gui.addFolder( 'Raycasting' );
+				rayFolder.add( params, 'count', 1, MAX_RAYS, 1 );
+				rayFolder.add( params, 'firstHitOnly' );
+				rayFolder.add( params, 'useBVH' ).onChange( v => {
+
+					mesh.geometry.boundsTree = v ? bvh : null;
+
+				} );
+				
+				const helperFolder = gui.addFolder( 'BVH Helper' );
+				helperFolder.add( params, 'displayHelper' );
+				helperFolder.add( params, 'helperDepth', 1, 20, 1 ).onChange( v => {
+
+					helper.depth = v;
+					helper.update();
+
+				} );
+
+				window.addEventListener( 'resize', onWindowResize );
+				onWindowResize();
+
+				initRays();
+
+			}
+
+			function initRays() {
+
+				const position = new THREE.Vector3();
+				const quaternion = new THREE.Quaternion();
+				const scale = new THREE.Vector3( 1, 1, 1 );
+				const matrix = new THREE.Matrix4();
+
+				for ( let i = 0; i < MAX_RAYS * 2; i ++ ) {
+
+					position.randomDirection().multiplyScalar( 3.75 );
+					matrix.compose( position, quaternion, scale );
+					sphereInstance.setMatrixAt( i, matrix );
+
+				}
+
+			}
+
+			function updateRays() {
+
+				if ( ! mesh ) return;
+
+				_raycaster.firstHitOnly = params.firstHitOnly;
+				const rayCount = params.count;
+
+				let lineNum = 0;
+				for ( let i = 0; i < rayCount; i ++ ) {
+
+					// get the current ray origin
+					sphereInstance.getMatrixAt( i * 2, _matrix );
+					_matrix.decompose( _position, _quaternion, _scale );
+
+					// rotate it about the origin
+					const offset = 1e-4 * window.performance.now();
+					_axis.set(
+						Math.sin( i * 100 + offset ),
+						Math.cos( - i * 10 + offset ),
+						Math.sin( i * 1 + offset ),
+					).normalize();
+					_position.applyAxisAngle( _axis, 0.001 );
+					
+					// update the position
+					_scale.setScalar( 0.02 );
+					_matrix.compose( _position, _quaternion, _scale );
+					sphereInstance.setMatrixAt( i * 2, _matrix );
+
+					// raycast
+					_raycaster.ray.origin.copy( _position );
+					_raycaster.ray.direction.copy( _position ).multiplyScalar( - 1).normalize();
+
+					// update hits points and lines
+					const hits = _raycaster.intersectObject( mesh );
+					if ( hits.length !== 0 ) {
+
+						const hit = hits[ 0 ];
+						const point = hit.point;
+						_scale.setScalar( 0.01 );
+						_matrix.compose( point, _quaternion, _scale );
+						sphereInstance.setMatrixAt( i * 2 + 1, _matrix );
+
+						lineSegments.geometry.attributes.position.setXYZ( lineNum ++, _position.x, _position.y, _position.z );
+						lineSegments.geometry.attributes.position.setXYZ( lineNum ++, point.x, point.y, point.z );
+
+					} else {
+
+						sphereInstance.setMatrixAt( i * 2 + 1, _matrix );
+						lineSegments.geometry.attributes.position.setXYZ( lineNum ++, _position.x, _position.y, _position.z );
+						lineSegments.geometry.attributes.position.setXYZ( lineNum ++, 0, 0, 0 );
+
+					}
+					
+				}
+
+				sphereInstance.count = rayCount * 2;
+				sphereInstance.instanceMatrix.needsUpdate = true;
+
+				lineSegments.geometry.setDrawRange( 0, lineNum );
+				lineSegments.geometry.attributes.position.needsUpdate = true;
+
+			}
+
+			function onWindowResize() {
+
+				const hx = window.innerWidth / 2;
+				const hy = window.innerHeight / 2;
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				if ( helper ) {
+
+					helper.visible = params.displayHelper;
+
+				}
+
+				if ( mesh ) {
+
+					mesh.rotation.y += 0.002;
+					mesh.updateMatrixWorld();
+
+				}
+				updateRays();
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>