webxr_xr_dragging_custom_depth.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js xr - dragging with custom depth shader</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  7. <meta property="og:title" content="three.js xr - dragging with custom depth shader">
  8. <meta property="og:type" content="website">
  9. <meta property="og:url" content="https://threejs.org/examples/webxr_xr_dragging_custom_depth.html">
  10. <meta property="og:image" content="https://threejs.org/examples/screenshots/webxr_xr_dragging_custom_depth.jpg">
  11. <link type="text/css" rel="stylesheet" href="main.css">
  12. </head>
  13. <body>
  14. <div id="info">
  15. <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> xr - dragging
  16. </div>
  17. <script type="importmap">
  18. {
  19. "imports": {
  20. "three": "../build/three.module.js",
  21. "three/addons/": "./jsm/"
  22. }
  23. }
  24. </script>
  25. <script type="module">
  26. import * as THREE from 'three';
  27. import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  28. import { XRButton } from 'three/addons/webxr/XRButton.js';
  29. import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js';
  30. let container;
  31. let camera, scene, renderer;
  32. let controller1, controller2;
  33. let controllerGrip1, controllerGrip2;
  34. let isDepthSupplied = false;
  35. let raycaster;
  36. const intersected = [];
  37. const tempMatrix = new THREE.Matrix4();
  38. let controls, group;
  39. init();
  40. animate();
  41. function init() {
  42. container = document.createElement( 'div' );
  43. document.body.appendChild( container );
  44. scene = new THREE.Scene();
  45. scene.background = new THREE.Color( 0x808080 );
  46. camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 10 );
  47. camera.position.set( 0, 1.6, 3 );
  48. controls = new OrbitControls( camera, container );
  49. controls.target.set( 0, 1.6, 0 );
  50. controls.update();
  51. const floorGeometry = new THREE.PlaneGeometry( 6, 6 );
  52. const floorMaterial = new THREE.ShadowMaterial( { opacity: 0.25, blending: THREE.CustomBlending, transparent: false } );
  53. const floor = new THREE.Mesh( floorGeometry, floorMaterial );
  54. floor.rotation.x = - Math.PI / 2;
  55. floor.receiveShadow = true;
  56. scene.add( floor );
  57. scene.add( new THREE.HemisphereLight( 0xbcbcbc, 0xa5a5a5, 3 ) );
  58. const light = new THREE.DirectionalLight( 0xffffff, 3 );
  59. light.position.set( 0, 6, 0 );
  60. light.castShadow = true;
  61. light.shadow.camera.top = 3;
  62. light.shadow.camera.bottom = - 3;
  63. light.shadow.camera.right = 3;
  64. light.shadow.camera.left = - 3;
  65. light.shadow.mapSize.set( 4096, 4096 );
  66. scene.add( light );
  67. group = new THREE.Group();
  68. scene.add( group );
  69. const geometries = [
  70. new THREE.BoxGeometry( 0.2, 0.2, 0.2 ),
  71. new THREE.ConeGeometry( 0.2, 0.2, 64 ),
  72. new THREE.CylinderGeometry( 0.2, 0.2, 0.2, 64 ),
  73. new THREE.IcosahedronGeometry( 0.2, 8 ),
  74. new THREE.TorusGeometry( 0.2, 0.04, 64, 32 )
  75. ];
  76. for ( let i = 0; i < 50; i ++ ) {
  77. const geometry = geometries[ Math.floor( Math.random() * geometries.length ) ];
  78. const material = new THREE.ShaderMaterial( {
  79. vertexShader: /* glsl */`
  80. varying vec3 vNormal;
  81. varying vec2 vUv;
  82. void main() {
  83. vNormal = normalize(normalMatrix * normal);
  84. vUv = uv;
  85. gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  86. }
  87. `,
  88. fragmentShader: /* glsl */`
  89. uniform vec3 diffuseColor;
  90. uniform float roughness;
  91. uniform float metalness;
  92. uniform float emissive;
  93. varying vec3 vNormal;
  94. varying vec2 vUv;
  95. uniform sampler2DArray depthColor;
  96. uniform float depthWidth;
  97. uniform float depthHeight;
  98. #define saturate( a ) clamp( a, 0.0, 1.0 )
  99. float Depth_GetCameraDepthInMeters(const sampler2DArray depthTexture,
  100. const vec2 depthUv, int arrayIndex) {
  101. return texture(depthColor, vec3(depthUv.x, depthUv.y, arrayIndex)).r;
  102. }
  103. float Depth_GetOcclusion(const sampler2DArray depthTexture, const vec2 depthUv, float assetDepthM, int arrayIndex) {
  104. float depthMm = Depth_GetCameraDepthInMeters(depthTexture, depthUv, arrayIndex);
  105. const float kDepthTolerancePerM = 0.001;
  106. return clamp(1.0 -
  107. 0.5 * (depthMm - assetDepthM) /
  108. (kDepthTolerancePerM * assetDepthM) +
  109. 0.5, 0.0, 1.0);
  110. }
  111. float Depth_GetBlurredOcclusionAroundUV(const sampler2DArray depthTexture, const vec2 uv, float assetDepthM, int arrayIndex) {
  112. // Kernel used:
  113. // 0 4 7 4 0
  114. // 4 16 26 16 4
  115. // 7 26 41 26 7
  116. // 4 16 26 16 4
  117. // 0 4 7 4 0
  118. const float kKernelTotalWeights = 269.0;
  119. float sum = 0.0;
  120. const float kOcclusionBlurAmount = 0.0005;
  121. vec2 blurriness =
  122. vec2(kOcclusionBlurAmount, kOcclusionBlurAmount /** u_DepthAspectRatio*/);
  123. float current = 0.0;
  124. current += Depth_GetOcclusion(
  125. depthTexture, uv + vec2(-1.0, -2.0) * blurriness, assetDepthM, arrayIndex);
  126. current += Depth_GetOcclusion(
  127. depthTexture, uv + vec2(+1.0, -2.0) * blurriness, assetDepthM, arrayIndex);
  128. current += Depth_GetOcclusion(
  129. depthTexture, uv + vec2(-1.0, +2.0) * blurriness, assetDepthM, arrayIndex);
  130. current += Depth_GetOcclusion(
  131. depthTexture, uv + vec2(+1.0, +2.0) * blurriness, assetDepthM, arrayIndex);
  132. current += Depth_GetOcclusion(
  133. depthTexture, uv + vec2(-2.0, +1.0) * blurriness, assetDepthM, arrayIndex);
  134. current += Depth_GetOcclusion(
  135. depthTexture, uv + vec2(+2.0, +1.0) * blurriness, assetDepthM, arrayIndex);
  136. current += Depth_GetOcclusion(
  137. depthTexture, uv + vec2(-2.0, -1.0) * blurriness, assetDepthM, arrayIndex);
  138. current += Depth_GetOcclusion(
  139. depthTexture, uv + vec2(+2.0, -1.0) * blurriness, assetDepthM, arrayIndex);
  140. sum += current * 4.0;
  141. current = 0.0;
  142. current += Depth_GetOcclusion(
  143. depthTexture, uv + vec2(-2.0, -0.0) * blurriness, assetDepthM, arrayIndex);
  144. current += Depth_GetOcclusion(
  145. depthTexture, uv + vec2(+2.0, +0.0) * blurriness, assetDepthM, arrayIndex);
  146. current += Depth_GetOcclusion(
  147. depthTexture, uv + vec2(+0.0, +2.0) * blurriness, assetDepthM, arrayIndex);
  148. current += Depth_GetOcclusion(
  149. depthTexture, uv + vec2(-0.0, -2.0) * blurriness, assetDepthM, arrayIndex);
  150. sum += current * 7.0;
  151. current = 0.0;
  152. current += Depth_GetOcclusion(
  153. depthTexture, uv + vec2(-1.0, -1.0) * blurriness, assetDepthM, arrayIndex);
  154. current += Depth_GetOcclusion(
  155. depthTexture, uv + vec2(+1.0, -1.0) * blurriness, assetDepthM, arrayIndex);
  156. current += Depth_GetOcclusion(
  157. depthTexture, uv + vec2(-1.0, +1.0) * blurriness, assetDepthM, arrayIndex);
  158. current += Depth_GetOcclusion(
  159. depthTexture, uv + vec2(+1.0, +1.0) * blurriness, assetDepthM, arrayIndex);
  160. sum += current * 16.0;
  161. current = 0.0;
  162. current += Depth_GetOcclusion(
  163. depthTexture, uv + vec2(+0.0, +1.0) * blurriness, assetDepthM, arrayIndex);
  164. current += Depth_GetOcclusion(
  165. depthTexture, uv + vec2(-0.0, -1.0) * blurriness, assetDepthM, arrayIndex);
  166. current += Depth_GetOcclusion(
  167. depthTexture, uv + vec2(-1.0, -0.0) * blurriness, assetDepthM, arrayIndex);
  168. current += Depth_GetOcclusion(
  169. depthTexture, uv + vec2(+1.0, +0.0) * blurriness, assetDepthM, arrayIndex);
  170. sum += current * 26.0;
  171. sum += Depth_GetOcclusion(depthTexture, uv, assetDepthM, arrayIndex) * 41.0;
  172. return sum / kKernelTotalWeights;
  173. }
  174. void main() {
  175. vec3 normal = normalize(vNormal);
  176. vec3 diffuse = diffuseColor;
  177. float specularIntensity = pow(max(dot(normal, normalize(vec3(0, 0, 1))), 0.0), 64.0);
  178. vec3 specular = vec3(specularIntensity) * mix(vec3(0.04), diffuse, metalness);
  179. gl_FragColor = vec4(diffuse * (1.0 - specular) + specular, 1.0) * (1.0 + emissive);
  180. if (depthWidth > 0.0) {
  181. int arrayIndex = 0;
  182. vec2 depthUv;
  183. if (gl_FragCoord.x>=depthWidth) {
  184. arrayIndex = 1;
  185. depthUv = vec2((gl_FragCoord.x-depthWidth)/depthWidth, gl_FragCoord.y/depthHeight);
  186. } else {
  187. depthUv = vec2(gl_FragCoord.x/depthWidth, gl_FragCoord.y/depthHeight);
  188. }
  189. float assetDepthM = gl_FragCoord.z;
  190. float occlusion = Depth_GetBlurredOcclusionAroundUV(depthColor, depthUv, assetDepthM, arrayIndex);
  191. float depthMm = Depth_GetCameraDepthInMeters(depthColor, depthUv, arrayIndex);
  192. float absDistance = abs(assetDepthM - depthMm);
  193. float v = 0.0025;
  194. absDistance = saturate(v - absDistance) / v;
  195. gl_FragColor.rgb += vec3(absDistance * 2.0, absDistance * 2.0, absDistance * 12.0);
  196. gl_FragColor = mix(gl_FragColor, vec4(0.0, 0.0, 0.0, 0.0), occlusion * 0.7);
  197. }
  198. }
  199. `,
  200. uniforms: {
  201. diffuseColor: { value: new THREE.Color( Math.random() * 0xffffff ) },
  202. roughness: { value: 0.7 },
  203. metalness: { value: 0.0 },
  204. emissive: { value: 0.0 },
  205. depthWidth: { value: 0.0 },
  206. depthHeight: { value: 0.0 },
  207. depthColor: { value: new THREE.Texture() }
  208. }
  209. } );
  210. const object = new THREE.Mesh( geometry, material );
  211. object.position.x = Math.random() * 4 - 2;
  212. object.position.y = Math.random() * 2;
  213. object.position.z = Math.random() * 4 - 2;
  214. object.rotation.x = Math.random() * 2 * Math.PI;
  215. object.rotation.y = Math.random() * 2 * Math.PI;
  216. object.rotation.z = Math.random() * 2 * Math.PI;
  217. object.scale.setScalar( Math.random() + 0.5 );
  218. group.add( object );
  219. }
  220. //
  221. renderer = new THREE.WebGLRenderer( { antialias: true } );
  222. renderer.setPixelRatio( window.devicePixelRatio );
  223. renderer.setSize( window.innerWidth, window.innerHeight );
  224. renderer.shadowMap.enabled = true;
  225. renderer.xr.enabled = true;
  226. container.appendChild( renderer.domElement );
  227. document.body.appendChild( XRButton.createButton( renderer, {
  228. 'optionalFeatures': [ 'depth-sensing' ],
  229. 'depthSensing': { 'usagePreference': [ 'gpu-optimized' ], 'dataFormatPreference': [] }
  230. } ) );
  231. // controllers
  232. controller1 = renderer.xr.getController( 0 );
  233. controller1.addEventListener( 'selectstart', onSelectStart );
  234. controller1.addEventListener( 'selectend', onSelectEnd );
  235. scene.add( controller1 );
  236. controller2 = renderer.xr.getController( 1 );
  237. controller2.addEventListener( 'selectstart', onSelectStart );
  238. controller2.addEventListener( 'selectend', onSelectEnd );
  239. scene.add( controller2 );
  240. const controllerModelFactory = new XRControllerModelFactory();
  241. controllerGrip1 = renderer.xr.getControllerGrip( 0 );
  242. controllerGrip1.add( controllerModelFactory.createControllerModel( controllerGrip1 ) );
  243. scene.add( controllerGrip1 );
  244. controllerGrip2 = renderer.xr.getControllerGrip( 1 );
  245. controllerGrip2.add( controllerModelFactory.createControllerModel( controllerGrip2 ) );
  246. scene.add( controllerGrip2 );
  247. //
  248. const geometry = new THREE.BufferGeometry().setFromPoints( [ new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, - 1 ) ] );
  249. const line = new THREE.Line( geometry );
  250. line.name = 'line';
  251. line.scale.z = 5;
  252. controller1.add( line.clone() );
  253. controller2.add( line.clone() );
  254. raycaster = new THREE.Raycaster();
  255. //
  256. window.addEventListener( 'resize', onWindowResize );
  257. }
  258. function onWindowResize() {
  259. camera.aspect = window.innerWidth / window.innerHeight;
  260. camera.updateProjectionMatrix();
  261. renderer.setSize( window.innerWidth, window.innerHeight );
  262. }
  263. function onSelectStart( event ) {
  264. const controller = event.target;
  265. const intersections = getIntersections( controller );
  266. if ( intersections.length > 0 ) {
  267. const intersection = intersections[ 0 ];
  268. const object = intersection.object;
  269. object.material.uniforms.emissive.value = 1;
  270. controller.attach( object );
  271. controller.userData.selected = object;
  272. }
  273. controller.userData.targetRayMode = event.data.targetRayMode;
  274. }
  275. function onSelectEnd( event ) {
  276. const controller = event.target;
  277. if ( controller.userData.selected !== undefined ) {
  278. const object = controller.userData.selected;
  279. object.material.uniforms.emissive.value = 0;
  280. group.attach( object );
  281. controller.userData.selected = undefined;
  282. }
  283. }
  284. function getIntersections( controller ) {
  285. controller.updateMatrixWorld();
  286. tempMatrix.identity().extractRotation( controller.matrixWorld );
  287. raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
  288. raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
  289. return raycaster.intersectObjects( group.children, false );
  290. }
  291. function intersectObjects( controller ) {
  292. // Do not highlight in mobile-ar
  293. if ( controller.userData.targetRayMode === 'screen' ) return;
  294. // Do not highlight when already selected
  295. if ( controller.userData.selected !== undefined ) return;
  296. const line = controller.getObjectByName( 'line' );
  297. const intersections = getIntersections( controller );
  298. if ( intersections.length > 0 ) {
  299. const intersection = intersections[ 0 ];
  300. const object = intersection.object;
  301. object.material.uniforms.emissive.value = 1;
  302. intersected.push( object );
  303. line.scale.z = intersection.distance;
  304. } else {
  305. line.scale.z = 5;
  306. }
  307. }
  308. function cleanIntersected() {
  309. while ( intersected.length ) {
  310. const object = intersected.pop();
  311. object.material.uniforms.emissive.value = 0;
  312. }
  313. }
  314. //
  315. function animate() {
  316. renderer.setAnimationLoop( render );
  317. }
  318. function render() {
  319. if ( renderer.xr.hasDepthSensing() && ! isDepthSupplied ) {
  320. group.children.forEach( child => {
  321. child.material.uniforms.depthColor.value = renderer.xr.getDepthTexture();
  322. child.material.uniforms.depthWidth.value = 1680;
  323. child.material.uniforms.depthHeight.value = 1760;
  324. isDepthSupplied = true;
  325. } );
  326. } else if ( ! renderer.xr.hasDepthSensing() && isDepthSupplied ) {
  327. group.children.forEach( child => {
  328. child.material.uniforms.depthWidth.value = 0;
  329. child.material.uniforms.depthHeight.value = 0;
  330. isDepthSupplied = false;
  331. } );
  332. }
  333. cleanIntersected();
  334. intersectObjects( controller1 );
  335. intersectObjects( controller2 );
  336. renderer.render( scene, camera );
  337. }
  338. </script>
  339. </body>
  340. </html>
粤ICP备19079148号