首页 > 其他分享 >(二)Three光线检测-实现摄像机向鼠标点击位置滑动动画

(二)Three光线检测-实现摄像机向鼠标点击位置滑动动画

时间:2022-10-28 15:13:59浏览次数:71  
标签:动画 const 鼠标 scene Three 摄像机 window camera THREE

(二)Three.js光线检测

摘要:使用three.js中的光线检测 Raycaster() ,实现一下效果:

  • 通过点击处的坐标,修改摄像机位置,实现摄像机由远及近的过渡动态效果(由远景到近景)

1、鼠标点击—摄像机过渡动画

1.1 THREE.Raycaster对象

官网:Raycaster – three.js docs (threejs.org)

因为使用鼠标对模型点击获取,那么,再three中可以使用Raycaster()光线检测来实现。再three官网上对Raycaster的解释为“此类旨在协助进行光线投射。Raycasting用于鼠标拾取(计算鼠标在3D空间中的哪些对象)等”,其原理便是

THREE.Raycaster对象从屏幕上的点击位置向场景中发射一束光线,与摄像机的位置形成一条光线,在这条光线路径上的物体,都会被检测到

1.1.1参数

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

1.2 使用到的方法

1.2.1 setFromCamera()

这个方法中有两个变量,

第一个是在标准化设备坐标中鼠标的二维坐标 —— X分量与Y分量应当在-1到1之间;

第二个是场景摄像机

.setFromCamera ( coords : Vector2, camera : Camera ) : undefined
coords —— 在标准化设备坐标中鼠标的二维坐标 —— X分量与Y分量应当在-1到1之间。
camera —— 射线所来源的摄像机。

1.2.2intersectObject()

这个方法是用来检测与射线相交的物体,返回值是一个Array数组;

检测所有在射线与物体之间,包括或不包括后代的相交部分。返回结果时,相交部分将按距离进行排序,最近的位于第一个。

.intersectObject ( object : Object3D, recursive : Boolean, optionalTarget : Array ) : Array
object —— 检查与射线相交的物体。
recursive —— 若为true,则同时也会检查所有的后代。否则将只会检查对象本身。默认值为true。
optionalTarget — (可选)设置结果的目标数组。如果不设置这个值,则一个新的Array会被实例化;如果设置了这个值,则在每次调用之前必须清空这个数组(例如:array.length = 0;)。

返回的数组如下所示:

参数解释:

distance —— 射线投射原点和相交部分之间的距离。
point —— 相交部分的点(世界坐标)
face —— 相交的面
faceIndex —— 相交的面的索引
object —— 相交的物体
uv —— 相交部分的点的UV坐标。
uv2 —— Second set of U,V coordinates at point of intersection
instanceId – The index number of the instance where the ray intersects the InstancedMesh

当计算这条射线是否和物体相交的时候,Raycaster将传入的对象委托给raycast方法。 这将可以让mesh对于光线投射的响应不同于lines和pointclouds。

请注意:对于网格来说,面必须朝向射线的原点,以便其能够被检测到。 用于交互的射线穿过面的背侧时,将不会被检测到。如果需要对物体中面的两侧进行光线投射, 你需要将material中的side属性设置为THREE.DoubleSide。

1.2.3 intersectObjects()

这个方法和上面的方法相差不多,本方法是用来检测一组物体;

检测所有在射线与这些物体之间,包括或不包括后代的相交部分。返回结果时,相交部分将按距离进行排序,最近的位于第一个),相交部分和.intersectObject所返回的格式是相同的。

.intersectObjects ( objects : Array, recursive : Boolean, optionalTarget : Array ) : Array
objects —— 检测和射线相交的一组物体。
recursive —— 若为true,则同时也会检测所有物体的后代。否则将只会检测对象本身的相交部分。默认值为true。
optionalTarget —— (可选)设置结果的目标数组。如果不设置这个值,则一个新的Array会被实例化;如果设置了这个值,则在每次调用之前必须清空这个数组(例如:array.length = 0;)。

1.3 gsap中的TweenMax动画

中文网址:TweenMax中文手册_TweenMax中文网

使用TweenMax动画,控制摄像机的变换速度,达到想要的平和效果;

TweenLite.fromTo('div', 5, {opacity:1}, {opacity:0});
//动画目标:div
//起始状态:opacity:1
//终点状态:opacity:0
//补间:5秒完成状态改变

1.4主要代码

1.4.1设置光线检测

//光线检测,获取点击物体的坐标值
  rayClick() {
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();
    const camera = this.camera;
    const scene = this.scene;

    //对页面进行鼠标点击事件绑定
    window.addEventListener("mouseup", mouseup);

    //添加点击方法
    function mouseup(e) {
      // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
      mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;

      // 通过摄像机和鼠标位置更新射线
      //这里的摄像机要将外部定义的摄像机通过新的变量接受到,再次赋值使用,同下方的scene
      //因为鼠标点击事件的this指的是windows,不是这个场景Scene,解决办法可以在 const mouse = new THREE.Vector2();
      // 后重新赋值一下:const _this = this; 在点击函数中就可以使用_this.scene、_this.camera
      raycaster.setFromCamera(mouse, camera);

      // 计算物体和射线的焦点
      const intersects = raycaster.intersectObjects(scene.children);

      console.log(intersects);
      //选中后进行操作
      if (intersects.length) {
        var selected = intersects[0];
        //点击世界中的物体,改变摄像机位置到物体前,实现从远景到近景的切换效果
        TweenMax.to(camera.position, 2, {
            x: selected.point.x + 50,
            y: selected.point.y,
            z: selected.point.z + 100,
            ease:Expo.easeInOut,
            onComplete: function (){}
        })
        console.log("x坐标" + selected.point.x);
        console.log("y坐标" + selected.point.y);
        console.log("z坐标" + selected.point.z);
      }
    }
  }

1.5完整代码

html部分

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three实现摄像机动画</title>
    <link rel="stylesheet" href="./assets/css/index.css">
</head>
<body>
    <canvas id="canvasScene"></canvas>
    <script src="./js/index.js" type="module"></script>
</body>
</html>

index.js部分

import Scene from "./Scene";

const canvasEL = document.getElementById('canvasScene');

new Scene(canvasEL);

Scene.js部分

import * as THREE from "three";
//导入鼠标控制器控件
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
//导入fbx模型加载器
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
//导入TweenMax动画控件
import { TweenMax } from "gsap/gsap-core";

import { Expo } from "gsap";

export default class Scene {
  canvas;
  scene;
  camera;
  render;
  controls;
  light;

  constructor(el) {
    this.canvas = el;
    this.init();
  }

  init() {
    this.setRender();
    this.setScene();
    this.setCamera();

    this.setControls();
    this.setLight();
    this.animate();
    this.setFbx();
    this.rayClick();
  }

  setScene() {
    this.scene = new THREE.Scene();
    this.scene.background = new THREE.Color(0x002222);
  }
  // 设置相机
  setCamera() {
    this.camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      3000
    );
    this.camera.position.set(300, 200, 1000);
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();
    this.scene.add(this.camera);
  }
  // 设置渲染器
  setRender() {
    this.render = new THREE.WebGL1Renderer({
      canvas: this.canvas,
      //设置抗锯齿
      antialias: true,
    });
    //设置渲染编码
    this.render.outputEncoding = THREE.sRGBEncoding;
    //设置渲染宽高
    this.render.setSize(window.innerWidth, window.innerHeight);

    //监听页面大小变化,修改器的宽高、摄像机的比例
    window.addEventListener("resize", () => {
      this.camera.aspect = window.innerWidth / window.innerHeight;
      this.camera.updateProjectionMatrix();
      this.render.setSize(window.innerWidth, window.innerHeight);
    });
  }
  //设置控制器
  setControls() {
    this.controls = new OrbitControls(this.camera, this.render.domElement);
  }
  //设置灯光
  setLight() {
    this.light = new THREE.SpotLight();
    this.light.position.set(100, 500, 300);
    this.scene.add(this.light);
  }
  //设置渲染函数
  animate = () => {
    this.render.render(this.scene, this.camera);
    window.requestAnimationFrame(this.animate);
  };
  //添加fbx模型
  setFbx() {
    const fbxLoader = new FBXLoader();
    fbxLoader.load("./model/house.fbx", (house) => {
      const scale = 0.05;
      house.scale.set(scale, scale, scale);
      this.scene.add(house);
      house.position.set(0, 0, 0);
    });
  }

  //光线检测,获取点击物体的坐标值
  rayClick() {
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();
    const camera = this.camera;
    const scene = this.scene;

    //对页面进行鼠标点击事件绑定
    window.addEventListener("mouseup", mouseup);

    //添加点击方法
    function mouseup(e) {
      // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
      mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;

      // 通过摄像机和鼠标位置更新射线
      //这里的摄像机要将外部定义的摄像机通过新的变量接受到,再次赋值使用,同下方的scene
      raycaster.setFromCamera(mouse, camera);

      // 计算物体和射线的焦点
      const intersects = raycaster.intersectObjects(scene.children);

      console.log(intersects);
      //选中后进行操作
      if (intersects.length) {
        var selected = intersects[0];
        
        //点击世界中的物体,改变摄像机位置到物体前,实现从远景到近景的切换效果
        TweenMax.to(camera.position, 2, {
            x: selected.point.x + 50,
            y: selected.point.y,
            z: selected.point.z + 100,
            ease:Expo.easeInOut,
            onComplete: function (){}
        })
        console.log("x坐标" + selected.point.x);
        console.log("y坐标" + selected.point.y);
        console.log("z坐标" + selected.point.z);
      }
    }
  }
}

标签:动画,const,鼠标,scene,Three,摄像机,window,camera,THREE
From: https://www.cnblogs.com/zw-521/p/16836052.html

相关文章

  • threejs中的一些术语
    zh:{ 'menubar/file':'文件', 'menubar/file/new':'新建', 'menubar/file/import':'导入', 'menubar/file/export/geometry':'导出几何体', 'menubar......
  • React动画实现方案之 Framer Motion,让你的页面“自己”动起来
    前言相信很多前端同学都或多或少和动画打过交道。有的时候是产品想要的过度效果;有的时候是UI想要的酷炫动画。但是有没有人考虑过,是不是我们的页面上面的每一次变化,都可以......
  • Vue(V 3.2.37)使用Three.js(V 0.145.0)加载3D模型的详细步骤
    Vue(V3.2.37)使用Three.js(V0.145.0)加载3D模型的详细步骤1、安装three命令:pnpminstallthree引入three和加载器import*asTHREEfrom'three'import{OBJLoader......
  • 如何用webgl(three.js)搭建一个3D库房,3D仓库3D码头,3D集装箱,车辆定位,叉车定位可视
    序又是快两个月没写随笔了,长时间不总结项目,不锻炼文笔,一开篇,多少都会有些生疏,不知道如何开篇,如何写下去。有点江郎才尽,黔驴技穷的感觉。写随笔,通常三步走,第一步,......
  • 3D渲染和动画制作软件:KeyShot Pro中文版
    mac上哪款渲染软件好用呢?KeyShotPro中文版是您非常不错的选择,功能强大的3d渲染和动画制作软件,Keyshot新的3D绘画工具允许你通过在模型表面直接绘画或冲压来进一步定制你的......
  • js缓动动画
      //缓动动画 //封装缓动动画函数传递两个参数需要执行动画的对象和目标位置 functionanimate(obj,target){      //先把原先的定时器清除,只......
  • unity 给动画帧添加触发事件并调用函数
    动画事件的作用:假设你要在动画的某一帧执行某个函数方法做一些判断,比如当人物攻击动画武器挥砍出去的一帧执行攻击函数 添加动画帧事件方法①点击需要店家动画事件的......
  • css3动画次数限制
    <!DOCTYPEhtml><html><head><metacharset="utf-8"><title>菜鸟教程(runoob.com)</title><style>div{width:100px;height:100px;background:re......
  • flash动画设计并发布、嵌入到网页
    【创意内容】Flash动画设计,二维动画自己选择了动画主题,有三个板块:bubbles动画、蝴蝶飞动画、全球游线图动画,都是自己做的,使用了场景运用动画、图片的滚动、形状遮罩等功能......
  • 3d旋转动画
    <!DOCTYPEhtml><htmllang="en"><head>  <metacharset="UTF-8">  <metahttp-equiv="X-UA-Compatible"content="IE=edge">  <metaname="viewport"......