拾取事件​

拾取事件​

拾取事件 ​在三维应用中时常需要点击场景中的物体,引擎支持射线包围盒拾取和帧缓冲区拾取。

拾取支持的事件类型:

名称解释PICK_OVER当触控点进入碰撞体范围时触发一次PICK_OUT当触控点离开碰撞体范围时触发一次PICK_CLICK当触控点在碰撞体范围内按下并松开,在松开时触发一次PICK_MOVE当触控点在碰撞体范围内移动时触发PICK_UP当触控点在碰撞体范围内松开时触发一次PICK_DOWN当触控点在碰撞体范围内按下时触发一次拾取检测 ​引擎会统一监听鼠标事件,对场景中的所有可点击的 Object3D 进行拾取检测,并触发对应的事件。用户可以给物体添加 ColliderComponent 组件标明物体可被点击,并监听对应的 PointerEvent3D 事件即可。 引擎统一封装了两种拾取类型的用法,可以通过简单配置进行切换

ts//引擎启动前需要配置开启拾取和拾取类型

Engine3D.setting.pick.enable = true;

// Bound: 包围盒拾取, pixel: 帧缓冲区拾取

Engine3D.setting.pick.mode = `bound`; // or 'pixel'

await Engine3D.init()

// 拾取检测依赖 Collider 碰撞组件

let obj = Object3D();

obj.addComponent(ColliderComponent);

// 在节点上添加 PickEvent 事件监听,在回调函数可以获取到对应的事件

obj.addEventListener(PointerEvent3D.PICK_CLICK, onPick, this);

// 或者通过 view.pickFire 监听所有物体点击事件

view.pickFire.addEventListener(PointerEvent3D.PICK_CLICK, onPick, this);

//回调函数中获取到事件信息

function onPick(e: PointerEvent3D) {

e.target // 点击的 Object

e.data.worldPos // 世界坐标下点击的 position

e.data.worldNormal // 世界坐标点击位置的 normal

...

}1234567891011121314151617181920212223包围盒拾取 ​射线包围盒是一种常用的 CPU 计算拾取方法,需要计算 ColliderComponent 组件的 ColliderShape 和鼠标射线的交集,在物体数量不多的场景中性能较好,但是精度较差,因为包围盒往往不能够精准的表达物体的真实形状。

目前引擎提供的基本 ColliderShape 有 BoxColliderShape, SphereColliderShape, CapsuleColliderShape, 也可以通过物体本身 Mesh 形状构建 MeshColliderShape

tsimport {Object3D, Collider, BoxColliderShape, Vector3} from '@orillusion/core';

let box = new Object3D();

let mr = box.addComponent(MeshRenderer);

// 设置 box geometry

mr.geometry = new BoxGeometry(1,1,1);

// 添加碰撞盒检测

let collider = box.addComponent(ColliderComponent);

// bound 模式需要手动设置碰撞盒样式和大小

// 拾取精度取决于 box.geometry 和 collider.shape 的匹配程度

collider.shape = new BoxColliderShape().setFromCenterAndSize(new Vector3(0, 0, 0), new Vector3(1, 1, 1));1234567891011左面的 box 使用同形状的 BoxColliderShape 进行检测,精度较好中间的 sphere 使用 BoxColliderShape,但检测区域就会比实际模型要大,精度较差右边的 sphere 也使用 MeshColliderShape,可以完全贴合模型所有顶点,精度最高,但会消耗更多性能做碰撞检测,对于复杂物体来说,不推荐使用 WebGPU is not supported in your browser Please upgrade to latest Chrome/Edge

import { Graphic3D } from '@orillusion/graphic';

class TouchDemo {

scene: Scene3D;

cameraObj: Object3D;

camera: Camera3D;

graphic3D: Graphic3D;

constructor() {}

async run() {

console.log('start demo');

// enable pick and use bound mode

Engine3D.setting.pick.enable = true;

Engine3D.setting.pick.mode = `bound`;

await Engine3D.init();

this.scene = new Scene3D();

this.scene.addComponent(AtmosphericComponent);

this.cameraObj = new Object3D();

this.camera = this.cameraObj.addComponent(Camera3D);

this.scene.addChild(this.cameraObj);

this.camera.lookAt(new Vector3(0, 0, 10), new Vector3(0, 0, 0));

this.camera.perspective(60, Engine3D.aspect, 1, 10000.0);

// add a base light

let lightObj = new Object3D();

lightObj.addComponent(DirectLight);

this.scene.addChild(lightObj);

let box = this.createBox(-4, 0, 0);

let sphere = this.createSphere(0, 0, 0);

let sphere2 = this.createSphere2(4, 0, 0);

this.graphic3D = new Graphic3D();

this.scene.addChild(this.graphic3D);

this.graphic3D.drawBoundingBox(box.instanceID, box.bound as BoundingBox, Color.COLOR_GREEN);

this.graphic3D.drawBoundingBox(sphere.instanceID, sphere.bound as BoundingBox, Color.COLOR_GREEN);

this.graphic3D.drawMeshWireframe(sphere2.instanceID, new SphereGeometry(1.01, 8, 8), sphere2.transform, Color.COLOR_GREEN)

let view = new View3D();

view.scene = this.scene;

view.camera = this.camera;

// start render

Engine3D.startRenderView(view);

// listen all pick_click events

view.pickFire.addEventListener(PointerEvent3D.PICK_CLICK, this.onPick, this);

}

createBox(x: number, y: number, z: number) {

let boxObj = new Object3D();

boxObj.transform.localPosition = new Vector3(x, y, z);

let size: number = 2;

let shape: BoxColliderShape = new BoxColliderShape().setFromCenterAndSize(new Vector3(0, 0, 0), new Vector3(size, size, size));

// add a box collider

let collider = boxObj.addComponent(ColliderComponent);

collider.shape = shape;

let mr: MeshRenderer = boxObj.addComponent(MeshRenderer);

mr.geometry = new BoxGeometry(size, size, size);

mr.material = new LitMaterial();

this.scene.addChild(boxObj);

return boxObj;

}

createSphere(x: number, y: number, z: number) {

let sphereObj = new Object3D();

sphereObj.transform.localPosition = new Vector3(x, y, z);

let size: number = 2;

let shape: BoxColliderShape = new BoxColliderShape().setFromCenterAndSize(new Vector3(0, 0, 0), new Vector3(size, size, size));

// add a box collider

let collider = sphereObj.addComponent(ColliderComponent);

collider.shape = shape;

let mr: MeshRenderer = sphereObj.addComponent(MeshRenderer);

mr.geometry = new SphereGeometry(size / 2, 8, 8);

mr.material = new LitMaterial();

this.scene.addChild(sphereObj);

return sphereObj;

}

createSphere2(x: number, y: number, z: number) {

let sphereObj = new Object3D();

sphereObj.transform.localPosition = new Vector3(x, y, z);

let size: number = 2;

let shape: MeshColliderShape = new MeshColliderShape()

// add a box collider

let collider = sphereObj.addComponent(ColliderComponent);

collider.shape = shape;

let mr: MeshRenderer = sphereObj.addComponent(MeshRenderer);

mr.geometry = shape.mesh = new SphereGeometry(size / 2, 8, 8);

mr.material = new LitMaterial();

this.scene.addChild(sphereObj);

return sphereObj;

}

onPick(e: PointerEvent3D) {

console.log('onClick:', e);

let mr: MeshRenderer = e.target.getComponent(MeshRenderer);

mr.material.baseColor = Color.random();

}

}

new TouchDemo().run();123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108帧缓冲拾取 ​pixel 模式和 bound 模式不同,帧缓冲拾取 利用的是 GPU 的像素检测,几乎不消耗 CPU 性能,可以无视场景中交互对象的数量和形状复杂度,支持所有触控事件。当场景模型形状复杂或物体数量众多的时候,我们推荐使用 pixel 模式进行拾取检测。

WebGPU is not supported in your browser Please upgrade to latest Chrome/Edge

class Sample_MousePick {

lightObj: Object3D;

cameraObj: Camera3D;

scene: Scene3D;

hover: HoverCameraController;

constructor() {}

async run() {

// enable pick and use pixel mode

Engine3D.setting.pick.enable = true;

Engine3D.setting.pick.mode = `pixel`;

await Engine3D.init({});

this.scene = new Scene3D();

this.scene.addComponent(AtmosphericComponent);

let camera = CameraUtil.createCamera3DObject(this.scene);

camera.perspective(60, Engine3D.aspect, 1, 5000.0);

this.hover = camera.object3D.addComponent(HoverCameraController);

this.hover.setCamera(-30, -15, 120);

let wukong = await Engine3D.res.loadGltf('https://cdn.orillusion.com/gltfs/wukong/wukong.gltf');

wukong.transform.y = 30;

wukong.transform.scaleX = 20;

wukong.transform.scaleY = 20;

wukong.transform.scaleZ = 20;

wukong.forChild((node) => {

if (node.hasComponent(MeshRenderer)) {

node.addComponent(ColliderComponent);

}

});

this.scene.addChild(wukong);

this.initPickObject(this.scene);

let view = new View3D();

view.scene = this.scene;

view.camera = camera;

// start render

Engine3D.startRenderView(view);

// listen all mouse events

view.pickFire.addEventListener(PointerEvent3D.PICK_UP, this.onPick, this);

view.pickFire.addEventListener(PointerEvent3D.PICK_DOWN, this.onPick, this);

view.pickFire.addEventListener(PointerEvent3D.PICK_CLICK, this.onPick, this);

view.pickFire.addEventListener(PointerEvent3D.PICK_OVER, this.onPick, this);

view.pickFire.addEventListener(PointerEvent3D.PICK_OUT, this.onPick, this);

view.pickFire.addEventListener(PointerEvent3D.PICK_MOVE, this.onPick, this);

}

private initPickObject(scene: Scene3D): void {

/******** light *******/

{

this.lightObj = new Object3D();

this.lightObj.rotationX = 125;

this.lightObj.rotationY = 0;

this.lightObj.rotationZ = 40;

let lc = this.lightObj.addComponent(DirectLight);

lc.lightColor = KelvinUtil.color_temperature_to_rgb(5355);

lc.castShadow = true;

lc.intensity = 5;

scene.addChild(this.lightObj);

}

let size: number = 9;

let shape = new BoxColliderShape();

shape.setFromCenterAndSize(new Vector3(), new Vector3(size, size, size));

let geometry = new SphereGeometry(size / 2, 20, 20);

for (let i = 0; i < 10; i++) {

let obj = new Object3D();

obj.name = 'sphere ' + i;

scene.addChild(obj);

obj.x = (i - 5) * 10;

let mat = new LitMaterial();

mat.emissiveMap = Engine3D.res.grayTexture;

mat.emissiveIntensity = 0.0;

let renderer = obj.addComponent(MeshRenderer);

renderer.geometry = geometry;

renderer.material = mat;

obj.addComponent(ColliderComponent);

}

}

private onPick(e: PointerEvent3D) {

console.log(e.type, e.target.name, e.data);

if(e.type !== 'onPickMove'){

let obj = e.target as Object3D;

let mr = obj.getComponent(MeshRenderer);

mr.material.baseColor = Color.random();

}

}

}

new Sample_MousePick().run();123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100

相关推荐

帅康热水器全面操作指南
bat365官方登录中文

帅康热水器全面操作指南

📅 08-02 👁️ 6987
微信小信号咋才能发,微信发送小信号是什么意思
bat365官方登录中文

微信小信号咋才能发,微信发送小信号是什么意思

📅 07-13 👁️ 9319
百度云盘种子下载及文件打开方法详解
365体育竞彩足球

百度云盘种子下载及文件打开方法详解

📅 08-04 👁️ 3379