three.js学习笔记(九)——光线投射

光线投射(RayCaster)可以向特定方向投射光线,并测试哪些对象与其相交。光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。
在这里插入图片描述
用法示例:

  1. 测试相机前方是否有一堵墙(障碍)
  2. 光线是否击中目标
  3. 当鼠标移动时测试是否有物体位于光标下方,以此模拟鼠标事件
  4. 当物体朝向特定某处时提示信息

初始场景

三个球体,然后我们要发射一条光线穿过这些球体看其是否与之相交

const object1 = new THREE.Mesh(
    new THREE.SphereBufferGeometry(0.5, 16, 16),
    new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object1.position.x = - 2

const object2 = new THREE.Mesh(
    new THREE.SphereBufferGeometry(0.5, 16, 16),
    new THREE.MeshBasicMaterial({ color: '#ff0000' })
)

const object3 = new THREE.Mesh(
    new THREE.SphereBufferGeometry(0.5, 16, 16),
    new THREE.MeshBasicMaterial({ color: '#ff0000' })
)
object3.position.x = 2

scene.add(object1, object2, object3)

在这里插入图片描述

创建光线投射(RayCaster)

const raycaster = new THREE.Raycaster()

Raycaster( origin : Vector3, direction : Vector3, near : Float, far : Float )
origin —— 光线投射的原点向量。
direction —— 向射线提供方向的方向向量,应当被标准化(单位向量化.normalize())。
near —— 返回的所有结果比near远。near不能为负值,其默认值为0。
far —— 返回的所有结果都比far近。far不能小于near,其默认值为Infinity(正无穷)

设置射线原点及其方向向量

// 射线原点
const rayOrigin = new THREE.Vector3(-3,0,0)
//射线方向
const rayDirection = new THREE.Vector3(10,0,0)
// 必须将射线方向三维向量转化为单位向量
// 也就是说,将该向量的方向设置为和原向量相同,但是其长度(length)为1
rayDirection.normalize()
raycaster.set(rayOrigin,rayDirection)

投射射线

使用Raycaster的intersectObject()方法来测试一个对象,intersectObjects()方法测试一组对象

const intersect = raycaster.intersectObject(object2)
console.log(intersect);
const intersects = raycaster.intersectObjects([object1,object2,object3])
console.log(intersects);

始终使用数组,即便你只是在测试一个对象,因为光线可以多次穿过同一对象
在这里插入图片描述
在这里插入图片描述
查看打印结果对象包含了什么信息
distance – 光线原点与碰撞点之间的距离
face – 几何体的哪个面被光线击中
faceIndex – 那被击中的面的索引
object – 什么物体与碰撞有关
point – 碰撞准确位置的矢量
uv – 该几何体中的UV坐标

测试射线与物体相交

设置动画函数

const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // 物体动画:上下移动
    object1.position.y = Math.sin(elapsedTime * 0.3) * 1.5
    object2.position.y = Math.sin(elapsedTime * 0.8) * 1.5
    object3.position.y = Math.sin(elapsedTime * 1.4) * 1.5

    //创建射线
    const rayOrigin = new THREE.Vector3(-3,0,0)
    const rayDirection = new THREE.Vector3(1,0,0)
    rayDirection.normalize()
    raycaster.set(rayOrigin, rayDirection)

    // 物体对象数组
    const objectsToTest = [object1,object2, object3]
    // 被射线照射到的一组对象
    const intersects = raycaster.intersectObjects(objectsToTest)
    // 物体平时颜色为红色
    for(const object of objectsToTest){
        object.material.color.set('#ff0000')
    }
    // 被射线照射到的物体颜色变蓝
    for(const intersect of intersects){
        intersect.object.material.color.set('#0000ff')
    }

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

实际效果,三个球体分别以不同速率上下移动,当其与射线相交时,即被射线射中时,该球体颜色变为蓝色,未被射线射中时颜色为红色
在这里插入图片描述

通过鼠标使用光线投射

我们可以使用光线投射来测试物体是否在鼠标下面。为此我们需要的是鼠标的坐标,一个在水平轴和垂直轴上范围从-11的值。
因为鼠标只在屏幕移动,所以使用二维向量Vector2来创建鼠标变量,并监听鼠标移动事件,获取鼠标位置。
因为需要水平方向由左往右垂直方向由下往上的值范围始终在[-1,1]的区间内,因此需要对鼠标坐标位置进行标准化。

const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}
/**
 * Mouse
 */
const mouse = new THREE.Vector2()

window.addEventListener('mousemove', (_event) => {
    mouse.x = _event.clientX / sizes.width * 2 -1
    mouse.y = - (_event.clientY / sizes.height) * 2 + 1
    // console.log(mouse);
})

避免在mousemove事件回调中投射光线,而是要回动画函数中去投射光线。
之后使用setFromCamera()方法将光线定向到正确的方向
.setFromCamera ( coords : Vector2, camera : Camera ) : undefined
coords —— 在标准化设备坐标中鼠标的二维坐标 —— X分量与Y分量应当在-1到1之间。
camera —— 射线所来源的摄像机。

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () => {
  const elapsedTime = clock.getElapsedTime()

  // 物体动画:上下移动
  object1.position.y = Math.sin(elapsedTime * 0.3) * 1.5
  object2.position.y = Math.sin(elapsedTime * 0.8) * 1.5
  object3.position.y = Math.sin(elapsedTime * 1.4) * 1.5
  //射线重定向,来源为摄像机
  raycaster.setFromCamera(mouse, camera)
  const objectsToTest = [object1, object2, object3]
  const intersects = raycaster.intersectObjects(objectsToTest)
  for(const object of objectsToTest){
      object.material.color.set('#ff0000')
  }
  for(const intersect of intersects){
      intersect.object.material.color.set('#0000ff')
  }

  // Update controls
  controls.update()

  // Render
  renderer.render(scene, camera)

  // Call tick again on the next frame
  window.requestAnimationFrame(tick)
}

箭头为鼠标停放位置
在这里插入图片描述

鼠标移入移出事件

我们可能有时候有这么一个需求,当鼠标移动到物体上时触发一次鼠标移入事件mouseenter ,鼠标离开物体时触发一次鼠标移出事件mouseleave,可以在动画函数中添加如下代码:
思路是先在外面定义一个当前鼠标移入对象变量currentIntersect,值为null,然后对被光线射中的对象数组长度进行判断,如果不为0则说明射线与物体相交了,在里面判断当前鼠标移入对象的值,为空则触发mouseenter事件,然后将射线首先照射到的第一个对象赋值给currentIntersect,后面触发mouseleave事件相信也明白怎么做了。

  if (intersects.length) {
      if(currentIntersect==null){
          console.log('mouse enter');
      }
    currentIntersect = intersects[0]
  } else {
      if(currentIntersect){
          console.log('mouse leave');
      }
    currentIntersect = null
  }

在这里插入图片描述

鼠标点击事件

同样借助当前鼠标移入对象变量currentIntersect

window.addEventListener('click', _event => {
    if(currentIntersect){
        console.log('点击了球体');
    }
})

如果想要知道具体是哪个对象

window.addEventListener('click', _event => {
    if(currentIntersect){
        switch(currentIntersect.object){
            case object1:
                console.log('点击了对象1');
                break
            case object2:
                console.log('点击了对象2');
                break
            case object3:
                console.log('点击了对象3');
                break
        }
    }
})

在这里插入图片描述

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我会为您解答关于WebGL three.js的阴影与实现物体动画的问题。首先,让我们来了解一下WebGL three.js是什么。 WebGL three.js是一款基于WebGL的JavaScript 3D库,可以帮助我们快速搭建3D场景和应用。接下来我们来讲解阴影和实现物体动画的方法。 一、阴影 阴影是模拟物体之间的阴影效果,让3D场景更加真实。在three.js中,我们可以通过设置Mesh的castShadow和receiveShadow属性来实现阴影效果。 1. 首先,我们需要在场景中添加光源,例如SpotLight或DirectionalLight。 2. 然后,在需要投射阴影的物体上设置castShadow为true。 3. 最后,在需要接收阴影的物体上设置receiveShadow为true。 代码示例: ```javascript // 添加光源 const light = new THREE.SpotLight(0xffffff); light.position.set(0, 100, 0); light.castShadow = true; scene.add(light); // 添加需要投射阴影的物体 const cube = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), new THREE.MeshLambertMaterial({ color: 0xff0000 })); cube.castShadow = true; scene.add(cube); // 添加需要接收阴影的物体 const plane = new THREE.Mesh(new THREE.PlaneGeometry(200, 200, 1, 1), new THREE.MeshLambertMaterial({ color: 0xffffff })); plane.receiveShadow = true; plane.rotation.x = -Math.PI / 2; scene.add(plane); ``` 二、物体动画 在three.js中,我们可以通过Tween.js库来实现物体的动画效果。Tween.js是一款JavaScript动画库,可以帮助我们实现非常丰富的动画效果。 1. 首先,我们需要在HTML文件中引入Tween.js库文件。 2. 然后,在需要动画的物体上设置初始状态。 3. 最后,通过Tween.js库来设置物体的目标状态和动画效果,例如缓动动画(ease)或弹跳动画(bounce)。 代码示例: ```javascript // 引入Tween.js库文件 <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.min.js"></script> // 添加需要动画的物体 const cube = new THREE.Mesh(new THREE.BoxGeometry(10, 10, 10), new THREE.MeshLambertMaterial({ color: 0xff0000 })); cube.position.set(0, 0, 0); scene.add(cube); // 设置初始状态 const start = { x: 0, y: 0, z: 0 }; // 设置目标状态 const end = { x: 50, y: 50, z: 50 }; // 设置动画效果 const tween = new TWEEN.Tween(start) .to(end, 1000) .easing(TWEEN.Easing.Quadratic.InOut) .onUpdate(() => { cube.position.set(start.x, start.y, start.z); }) .start(); ``` 以上是关于WebGL three.js阴影与实现物体动画的方法,希望能够对您有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值