aframe摄像机穿墙问题-解决思路

    使用的库:aframe-v0.9.2、aframe-extras.min.js、aframe-physics-system.min.js、CANNON.js(cannon.js前面物理框架引入了不需要再次引入)

    说明点:aframe其实是用了cannon.js来做物理引擎的,因为aframe很多东西都标签化了,代码不方便提取,所以不太方便直接使用cannon的写法,这里急需要曲线救国了。

    一般的情况分为两种:

    第一种:

        手动加入a-box等,使用标签来创建元素

    第二种:

        使用a-obj-model,通过导入模型来实现

    下面分开来讲实现

    第一种:使用a-box等基础元素直接构建

     

<html>
  <head>
    <script src="aframe.min.js"></script>
    <script src="aframe-physics-system.min.js"></script>
  </head>
  <body>
    <a-scene physics>
      <a-box position="-1 4 -3" rotation="0 45 0" color="#4CC3D9" dynamic-body></a-box>
      <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" static-body></a-plane>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>
  </body>
</html>

第二种:

    导入模型有两种做法,一个是代码处理,另外一个就是人力了

    代码处理的思路:把模型里面的mesh变成一个个的a-box然后隐藏起来,变成不可见的entity,,记得墙面模型也需要引入显示的,这样就能直接使用框架本身了,避免很多麻烦。这里需要摄像机有kinema-body组件,就先在开头定义一哈。详细的逻辑暂时不优化,最后的drawWall()里面一定能看得懂。

    人力的思路:在代码里面自己写a-box然后手动去ctrl+i的模式里面一个一个调整大小和位置,和墙面吻合,这个就很累了,所以推荐代码解决。

    

<!DOCTYPE html>

<html>
	<head>
		<script src="aframe.min.js"></script>

		<script src="aframe-extras.min.js"></script>
		<script src="aframe-physics-system.min.js"></script>
		<!-- <script src="//cdn.rawgit.com/donmccurdy/aframe-physics-system/v3.3.0/dist/aframe-physics-system.min.js"></script> -->

		<script>
			/**
			 * Kinema body.
			 *
			 * Based on kinematic-body, from AFrame Extras (for now, basically a copy,
			 *   just because I read it is deprecated in AFrame Extras)
			 *
			 *   https://github.com/donmccurdy/aframe-extras/blob/master/src/misc/kinematic-body.js
			 *
			 * Managed dynamic body, which moves but is not affected (directly) by the
			 * physics engine. This is not a true kinematic body, in the sense that we are
			 * letting the physics engine _compute_ collisions against it and selectively
			 * applying those collisions to the object. The physics engine does not decide
			 * the position/velocity/rotation of the element.
			 *
			 * Used for the camera object, because full physics simulation would create
			 * movement that feels unnatural to the player. Bipedal movement does not
			 * translate nicely to rigid body physics.
			 *
			 * See: http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
			 * And: http://oxleygamedev.blogspot.com/2011/04/player-physics-part-2.html
			 */
			const EPS = 0.000001;

			AFRAME.registerComponent('kinema-body', {
				dependencies: ['velocity'],

				/*******************************************************************
				 * Schema
				 */

				schema: {
					mass: {
						default: 5
					},
					radius: {
						default: 1.3
					},
					linearDamping: {
						default: 0.05
					},
					enableSlopes: {
						default: true
					},
					enableJumps: {
						default: false
					},
				},

				/*******************************************************************
				 * Lifecycle
				 */

				init: function() {
					this.system = this.el.sceneEl.systems.physics;
					this.system.addComponent(this);

					const el = this.el,
						data = this.data,
						position = (new CANNON.Vec3()).copy(el.object3D.getWorldPosition(new THREE.Vector3()));

					this.body = new CANNON.Body({
						material: this.system.getMaterial('staticMaterial'),
						position: position,
						mass: data.mass,
						linearDamping: data.linearDamping,
						fixedRotation: true
					});
					this.body.addShape(
						new CANNON.Sphere(data.radius),
						new CANNON.Vec3(0, data.radius, 0)
					);

					this.body.el = this.el;
					this.el.body = this.body;
					this.system.addBody(this.body);

					if (el.hasAttribute('wasd-controls')) {
						console.warn('[kinema-body] Not compatible with wasd-controls, use movement-controls.');
					}
				},

				remove: function() {
					this.system.removeBody(this.body);
					this.system.removeComponent(this);
					delete this.el.body;
				},

				/*******************************************************************
				 * Update
				 */

				/**
				 * Checks CANNON.World for collisions and attempts to apply them to the
				 * element automatically, in a player-friendly way.
				 *
				 * There's extra logic for horizontal surfaces here. The basic requirements:
				 * (1) Only apply gravity when not in contact with _any_ horizontal surface.
				 * (2) When moving, project the velocity against exactly one ground surface.
				 *     If in contact with two ground surfaces (e.g. ground + ramp), choose
				 *     the one that collides with current velocity, if any.
				 */
				beforeStep: function(t, dt) {
					if (!dt) return;

					const el = this.el;
					const data = this.data
					const body = this.body;

					if (!data.enableJumps) body.velocity.set(0, 0, 0);
					body.position.copy(el.getAttribute('position'));
				},

				step: (function() {
					const velocity = new THREE.Vector3(),
						normalizedVelocity = new THREE.Vector3(),
						currentSurfaceNormal = new THREE.Vector3(),
						groundNormal = new THREE.Vector3();

					return function(t, dt) {
						if (!dt) return;

						let body = this.body,
							data = this.data,
							didCollide = false,
							height, groundHeight = -Infinity,
							groundBody,
							contacts = this.system.getContacts();

						dt = Math.min(dt, this.system.data.maxInterval * 1000);

						groundNormal.set(0, 0, 0);
						velocity.copy(this.el.getAttribute('velocity'));
						body.velocity.copy(velocity);

						for (var i = 0, contact; contact = contacts[i]; i++) {
							// 1. Find any collisions involving this element. Get the contact
							// normal, and make sure it's oriented _out_ of the other object and
							// enabled (body.collisionReponse is true for both bodies)
							if (!contact.enabled) {
								continue;
							}
							if (body.id === contact.bi.id) {
								contact.ni.negate(currentSurfaceNormal);
							} else if (body.id === contact.bj.id) {
								currentSurfaceNormal.copy(contact.ni);
							} else {
								continue;
							}

							didCollide = body.velocity.dot(currentSurfaceNormal) < -EPS;
							if (didCollide && currentSurfaceNormal.y <= 0.5) {
								// 2. If current trajectory attempts to move _through_ another
								// object, project the velocity against the collision plane to
								// prevent passing through.
								velocity.projectOnPlane(currentSurfaceNormal);
							} else if (currentSurfaceNormal.y > 0.5) {
								// 3. If in contact with something roughly horizontal (+/- 45º) then
								// consider that the current ground. Only the highest qualifying
								// ground is retained.
								height = body.id === contact.bi.id ?
									Math.abs(contact.rj.y + contact.bj.position.y) :
									Math.abs(contact.ri.y + contact.bi.position.y);
								if (height > groundHeight) {
									groundHeight = height;
									groundNormal.copy(currentSurfaceNormal);
									groundBody = body.id === contact.bi.id ? contact.bj : contact.bi;
								}
							}
						}

						normalizedVelocity.copy(velocity).normalize();
						if (groundBody && (!data.enableJumps || normalizedVelocity.y < 0.5)) {
							if (!data.enableSlopes) {
								groundNormal.set(0, 1, 0);
							} else if (groundNormal.y < 1 - EPS) {
								groundNormal.copy(this.raycastToGround(groundBody, groundNormal));
							}

							// 4. Project trajectory onto the top-most ground object, unless
							// trajectory is > 45º.
							velocity.projectOnPlane(groundNormal);

						} else if (this.system.driver.world) {
							// 5. If not in contact with anything horizontal, apply world gravity.
							// TODO - Why is the 4x scalar necessary.
							// NOTE: Does not work if physics runs on a worker.
							velocity.add(this.system.driver.world.gravity.scale(dt * 4.0 / 1000));
						}

						body.velocity.copy(velocity);
						this.el.setAttribute('velocity', body.velocity);
						this.el.setAttribute('position', body.position);
					};
				}()),

				/**
				 * When walking on complex surfaces (trimeshes, borders between two shapes),
				 * the collision normals returned for the player sphere can be very
				 * inconsistent. To address this, raycast straight down, find the collision
				 * normal, and return whichever normal is more vertical.
				 * @param  {CANNON.Body} groundBody
				 * @param  {CANNON.Vec3} groundNormal
				 * @return {CANNON.Vec3}
				 */
				raycastToGround: function(groundBody, groundNormal) {
					let ray,
						hitNormal,
						vFrom = this.body.position,
						vTo = this.body.position.clone();

					ray = new CANNON.Ray(vFrom, vTo);
					ray._updateDirection(); // TODO - Report bug.
					ray.intersectBody(groundBody);

					if (!ray.hasHit) return groundNormal;

					// Compare ABS, in case we're projecting against the inside of the face.
					hitNormal = ray.result.hitNormalWorld;
					return Math.abs(hitNormal.y) > Math.abs(groundNormal.y) ? hitNormal : groundNormal;
				}
			});
		</script>




	<body>
		<a-scene physics="debug: false;" vr-mode-ui="enabled: false" antialias="true">
			<a-assets>
				<a-asset-item id="wall3-obj" src="wall3.obj"></a-asset-item>
				<a-asset-item id="0-mtl" src="0.mtl"></a-asset-item>
				<img id="wood5" src="k.png">
				<a-asset-item id="sopraporta-obj" src="sopraporta.obj"></a-asset-item>
				<a-asset-item id="0-mtl" src="0.mtl"></a-asset-item>
				<img id="wood5" src="k.png">
				<a-asset-item id="jgz-obj" src="jgz.obj"></a-asset-item>
				<a-asset-item id="jgz-mtl" src="jgz.mtl"></a-asset-item>
				<img id="wood111" src="d.png">
				<a-asset-item id="wljg-obj" src="wljg.obj"></a-asset-item>
				<a-asset-item id="wljg-mtl" src="wljg.mtl"></a-asset-item>
				<img id="wood111" src="d.png">
				<a-asset-item id="zw2-obj" src="zw2.obj"></a-asset-item>
				<a-asset-item id="zw2-mtl" src="zw2.mtl"></a-asset-item>
				<img id="wood111" src="d.png">
				<a-asset-item id="wire frame2-obj" src="wire frame2.obj"></a-asset-item>
				<img id="wood1" src="kk.jpg">
				<a-asset-item id="logo-obj" src="logo.obj"></a-asset-item>
				<a-asset-item id="logo-mtl" src="logo.mtl"></a-asset-item>
				<img id="wood1" src="kk.jpg">
				<a-asset-item id="sopraporta wire-obj" src="sopraporta wire.obj"></a-asset-item>
				<a-asset-item id="sopraporta wire" src="sopraporta wire.mtl"></a-asset-item>
				<img id="wood1" src="kk.jpg">
				<a-asset-item id="name baseplate-obj" src="name baseplate.obj"></a-asset-item>
				<a-asset-item id="name baseplate" src="name baseplate.mtl"></a-asset-item>
				<img id="wood1" src="kk.jpg">
				<a-asset-item id="walls-dae" src="wall3.dae"></a-asset-item>
				<a-asset-item id="walls-glb" src="wall3.glb"></a-asset-item>
			</a-assets>

			<!-- <a-entity id="wall_obj" obj-model="obj: url(wall3.obj)" material="src: #wood5; repeat: 1 1.0" opacity="0.4" scale="0.01 0.01 0.01"
			 position="0 0 0" static-body></a-entity> -->
			<!-- <a-obj-model id="wall_obj" src="#wall3-obj" material="src: #wood5; repeat: 1 1.0" opacity="0.4" scale="0.01 0.01 0.01"
			 position="0 0 0" visible="false"></a-obj-model> -->

			<!-- <a-box id="box" static-body position="0 0 0" height="3" width="4" color="red"></a-box> -->

			<a-sky color="#ECECEC"></a-sky>

			<a-entity kinema-body="mass:1;radius:0.3;" movement-controls="fly: false;speed:0.2;" position="0 0 4">
				<a-entity camera position="0 1 0" look-controls="reverseMouseDrag:false;pointerLockEnabled:true;">
					<a-entity cursor
					            position="0 0 -0.1"
					            geometry="primitive: ring; radiusInner: 0.001; radiusOuter: 0.0015"
					            material="color: black; shader: flat">
					</a-entity>
				</a-entity>
				<!-- <a-camera id="c1" look-controls="enabled:true;reverseMouseDrag:true;reverseTouchDrag:false;" zoom="1"></a-camera> -->
			</a-entity>
			<a-plane height="100" width="100" rotation="-90 0 0" src="bd.jpg" static-body></a-plane>
			<a-obj-model id="wall_obj" src="#wall3-obj"  material="src: #wood5; repeat: 1 1.0" opacity="0.4" scale="0.01 0.01 0.01" position="0 0 0" class="clickable"></a-obj-model>
			<a-obj-model src="#wire frame2-obj" scale="0.01 0.015 0.01" opacity="0.99" position="0 0.01 0" color="#21c7e1" class="clickable"></a-obj-model>
			<!-- <a-entity id="wall_obj" gltf-model="#walls-glb" scale="0.01 0.01 0.01" position="0 0 0" visible="true"></a-entity> -->
		</a-scene>
	</body>
	<script>
		var sceneEl = document.querySelector('a-scene');
		var wall = document.querySelector('#wall_obj');
		wall.addEventListener('model-loaded', function(e) {
			drawWall(wall);
		});

		function drawWall(wall) {
			wall_mesh = wall.object3D.children[0].children;
			for (var i = 0; i < wall_mesh.length; i++) {
				if (wall_mesh[i].isMesh) {
					var object = wall_mesh[i];
					var geometry = object.geometry;
					var geometry_s_p = createBoundingBoxShape(object);
					var box_size = geometry_s_p.box;
					var scale = wall.object3D.scale;
					var depth = "2",
						height = "4",
						width = "0.5";
					width = (box_size.max.x - box_size.min.x) * scale.x;
					height = (box_size.max.y - box_size.min.y) * scale.y * 2;
					depth = (box_size.max.z - box_size.min.z) * scale.z;

					var position = {
						x: (box_size.min.x + (width / 2)) * scale.x,
						y: (box_size.min.y + (height / 2)) * scale.y,
						z: (box_size.min.z + (depth / 2)) * scale.z
					};
					position = geometry_s_p.position;
					
					position = {
						x: position.x * scale.x,
						y: position.y * scale.y,
						z: position.z * scale.z
					};
					//创建entity
					var boxEl = document.createElement('a-box');
					boxEl.setAttribute('material', {
						color: '#ffffff'
					});
					boxEl.setAttribute('width', width);
					boxEl.setAttribute('height', height);
					boxEl.setAttribute('depth', depth);
					boxEl.setAttribute('position', position);
					boxEl.setAttribute('static-body', {mass:1});
					// boxEl.setAttribute('scale', scale);
					boxEl.setAttribute('visible', false);
					sceneEl.appendChild(boxEl);
				}

			}


		}

		/**
		 * Bounding box needs to be computed with the entire mesh, not just geometry.
		 * @param  {THREE.Object3D} mesh
		 * @return {CANNON.Shape}
		 */
		function createBoundingBoxShape(object) {
			var shape, localPosition, worldPosition,
				box = new THREE.Box3();

			box.setFromObject(object);

			if (!isFinite(box.min.lengthSq())) return null;

			shape = new CANNON.Box(new CANNON.Vec3(
				(box.max.x - box.min.x) / 2,
				(box.max.y - box.min.y) / 2,
				(box.max.z - box.min.z) / 2
			));

			object.updateMatrixWorld();
			worldPosition = new THREE.Vector3();
			worldPosition.setFromMatrixPosition(object.matrixWorld);
			localPosition = box.translate(worldPosition.negate()).getCenter();
			if (localPosition.lengthSq()) {
				shape.offset = localPosition;
			}
			return {box:box, position:localPosition};
		}
	</script>
</html>

 

下载地址:https://download.csdn.net/download/lz610756247/11868604

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: aframe-ar-nft.js是一个基于WebAR技术的开源JavaScript库,它可以使开发者快速构建AR应用程序。该库主要针对基于NFT(Natrual Feature Tracking)技术的AR应用开发,NFT是一种AR技术,可以通过识别真实世界中的物体并与虚拟信息相结合来增强增强现实体验。该库与Three.js进行了整合,开发者可以使用Three.js中已有的功能和API完成AR应用程序的开发。同时,该库还支持在模型和纹理之间进行切换,可以根据需要在AR场景中加入图像、视频、3D模型等虚拟元素。 使用aframe-ar-nft.js开发AR应用程序非常方便,只需要在HTML文件中引入需要加载的模型和纹理文件,然后实例化NFTMarker类,即可开始AR应用的开发。开发者可以根据需要配置AR场景中的元素,例如相机、灯光、模型等,使用该库还可以实现手势控制和声音效果。 总的来说,aframe-ar-nft.js可以让开发者轻松地创建基于NFT技术的AR应用程序,通过与Three.js的整合,可以很容易地进行二次开发和定制。这为AR应用程序的开发提供了更加便利和高效的解决方案。 ### 回答2: aframe-ar-nft.js是一个能够让用户在webAR中使用NFT(可识别图像)来构建虚拟现实场景的javascript库。该库的主要优势是,与传统的AR技术不同,它基于NFT技术,不需要使用额外的标记或器具来触发虚拟现实效果,简单易用。aframe-ar-nft.js提供了完整的API文档,使得开发人员可以快速轻松地在自己的网站或应用中使用AR技术。它具有高度的灵活性,可以应用于各种网站和应用场景,例如:游戏、虚拟演示、教育、医疗等等。该库的使用方法也非常简单,只需几行代码即可实现AR效果。总之,aframe-ar-nft.js是一款易用、灵活、高性能的javascript库,为开发人员提供了一种强大的AR开发工具。 ### 回答3: aframe-ar-nft.js是一款基于WebAR技术的扩展库,它提供了简单易用的NFT(Natural Feature Tracking,自然特征跟踪)功能,可以实现在现实场景中识别NFT图像,从而在图像上展示虚拟3D模型。 通过在A-Frame中使用aframe-ar-nft.js,用户可以很方便地创建AR体验,比如将虚拟模型置于某个位置,或者通过手势交互与用户互动,展示出身临其境的AR效果。 此外,aframe-ar-nft.js还提供了丰富的配置选项,可以针对不同的NFT图像进行自定义设置,例如调整图像的大小、定义图像的旋转方向等等,从而获得更出色的AR效果。 总的来说,aframe-ar-nft.js是一款非常实用的WebAR库,它通过简洁易用的接口和多样化的配置选项,为WebAR开发者提供了便捷、高效、灵活的开发体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值