(二)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动画,控制摄像机的变换速度,达到想要的平和效果;
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);
}
}
}
}
(二)Three光线检测-实现摄像机向鼠标点击位置滑动动画的更多相关文章
- js 获取页面高度和宽度(兼容 ie firefox chrome),获取鼠标点击位置
<script> //得到页面高度 var yScroll = (document.documentElement.scrollHeight >document.documentEl ...
- u3d 鼠标点击位置,物体移动过去。 U3d mouse clicks position, objects move past.
u3d 鼠标点击位置,物体移动过去. U3d mouse clicks position, objects move past. 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱: ...
- 每天一个JavaScript实例-铺货鼠标点击位置并将元素移动到该位置
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- C# winform 获取鼠标点击位置
说明:该篇随笔的代码内容并非出自本人,是在其他网站搜寻的,出处已经不记得了,本次随笔只为记录,目的帮助自己,帮助他人. 实现的原理也不做多的赘述,直接上代码. 第一个类是需要用到的Windows AP ...
- [QGLViewer]3D场景鼠标点击位置
重载鼠标事件: void AxMapControl::mousePressEvent(QMouseEvent* e) { switch(currentTool) { case AX_DRAW_DIRE ...
- JQuery------实现鼠标点击和滑动不同效果
如图: 代码: html <ul class="price-brand-right"> @foreach (Brand item in ViewBag.Brand) { ...
- unity中让摄像机移动到鼠标点击的位置和鼠标控制平移视角
private Vector3 targetVector3; private float movespeed=0.5f; private bool IsOver = true; private Gam ...
- unity中让物体移动到鼠标点击地面任一点的位置(单击移动和双击暂停移动)并生成图标
using UnityEngine; using System.Collections.Generic; using UnityEngine.EventSystems; using UnityEngi ...
- (原)python中matplot中获得鼠标点击的位置及显示灰度图像
转载请注明出处: http://www.cnblogs.com/darkknightzh/p/6182474.html 参考网址: http://matplotlib.org/examples/pyl ...
随机推荐
- k8s驱逐篇(2)-kubelet节点压力驱逐
kubelet节点压力驱逐 kubelet监控集群节点的 CPU.内存.磁盘空间和文件系统的inode 等资源,根据kubelet启动参数中的驱逐策略配置,当这些资源中的一个或者多个达到特定的消耗水平 ...
- ruby 字符注音标签
<ruby/>标签下的文本可以注音,注音由一对<rt/>标签完成. <ruby> 汉<rt>han</rt> 字<rt>zi&l ...
- rcu stall 导致的hung 记录
synchronize_sched 也会在wait_rcu_gp 的长时间等待导致进入hung ,假设rcu没有及时执行的话, 另外,如果rcu积累到一定程度,内存自然就不足了,可能会oom. rcu ...
- P4767 [IOI2000]邮局 - 平行四边形不等式优化DP
There is a straight highway with villages alongside the highway. The highway is represented as an in ...
- java数组---概念
1.数组的定义 数组是相同类型数据的有序集合.数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成.其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们. 2.数组的建 ...
- SpringMVC 02: SpringMVC响应get和post请求 + 5种获取前端数据的方式
响应get和post请求 SpringMVC中使用@RequestMapping注解完成对get请求和post请求的响应 项目结构和配置文件与SpringMVC博客集中的"SpringMVC ...
- 常用的SSH,你了解多少?(长文警告)
1.SSH工作原理 从ssh的加密方式说开去,看下文 1.1.对称加密 客户端和服务端采用相同的密钥进行数据的加解密,很难保证密钥不丢失,或者被截获.隐藏着中间人攻击的风险 如果攻击者插在用户与远程主 ...
- Altium Designer 18学习
目录 目录 快捷键 通孔 敷铜 修改铜皮与导线之间的间隔 去除指定敷铜区域 DRC设计规则检查问题: 快捷键 EJC 快速跳转到器件 M 移动 CTRL+M 测量距离 通孔 敷铜 放置多边形平面 -- ...
- 大家都能看得懂的源码之ahooks useInfiniteScroll
本文是深入浅出 ahooks 源码系列文章的第十七篇,该系列已整理成文档-地址.觉得还不错,给个 star 支持一下哈,Thanks. 简介 useInfiniteScroll 封装了常见的无限滚动逻 ...
- KingbaseESV8R6临时表和全局临时表
临时表概述 临时表用于存放只存在于事务或会话期间的数据.临时表中的数据对会话是私有的,每个会话只能看到和修改自己会话的数据. 您可以创建全局(global)临时表或本地(locall)临时表. 下表列 ...