【Three.js】知识梳理二十二:相机视角的平滑过渡与点击模型视角切换

news2024/11/25 9:52:32

在 Three.js 中,实现相机视角的平滑过渡和点击模型切换到查看模型视角是一个常见且有用的功能。这种效果不仅能提升用户体验,还能为场景互动添加更多的动态元素。本文将详细介绍如何在 Three.js 中实现这一功能。

image.png

1. 基本设置

首先,我们需要创建一个基本的 Three.js 场景,包括相机、渲染器、光源以及一些示例模型。

创建场景和相机
// 创建场景
const scene = new THREE.Scene();
​
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 10);
​
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
​
// 创建光源
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 10, 10);
scene.add(light);
添加示例模型
// 创建一个简单的几何体
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 1, 0);
scene.add(cube);
​
// 创建另一个几何体
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(2, 1, 0);
scene.add(sphere);

2. 引入动画库

为了实现平滑过渡,我们引入 tween.js 动画库。

<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.js"></script>

3. 实现相机视角的平滑切换

定义相机切换函数
function smoothCameraTransition(targetPosition, targetLookAt) {
    // 保存当前相机的位置和朝向
    const startPosition = camera.position.clone();
    const startLookAt = new THREE.Vector3();
    camera.getWorldDirection(startLookAt);
​
    // 创建 tween 动画
    new TWEEN.Tween(startPosition)
        .to(targetPosition, 2000) // 动画持续时间为2000毫秒
        .easing(TWEEN.Easing.Quadratic.InOut) // 使用缓动函数
        .onUpdate(() => {
            camera.position.copy(startPosition);
        })
        .start();
​
    new TWEEN.Tween(startLookAt)
        .to(targetLookAt, 2000)
        .easing(TWEEN.Easing.Quadratic.InOut)
        .onUpdate(() => {
            camera.lookAt(startLookAt);
        })
        .start();
}
更新渲染循环

确保在渲染循环中更新 tween 动画。

function animate() {
    requestAnimationFrame(animate);
    TWEEN.update();
    renderer.render(scene, camera);
}
animate();

4. 实现点击模型切换视角

添加射线投射器

我们需要添加射线投射器来检测用户点击的模型。

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
​
function onMouseClick(event) {
    // 将鼠标点击位置转换为标准化设备坐标
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
​
    // 更新射线投射器
    raycaster.setFromCamera(mouse, camera);
​
    // 计算交互对象
    const intersects = raycaster.intersectObjects(scene.children);
​
    if (intersects.length > 0) {
        const intersectedObject = intersects[0].object;
        // 切换相机视角到点击的模型
        const targetPosition = new THREE.Vector3().copy(intersectedObject.position).add(new THREE.Vector3(0, 2, 5));
        const targetLookAt = intersectedObject.position.clone();
        smoothCameraTransition(targetPosition, targetLookAt);
    }
}
​
window.addEventListener('click', onMouseClick, false);

5. 完整代码示例

将上述代码片段整合在一起,形成一个完整的示例。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js Smooth Camera Transition</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.js"></script>
    <script>
        // 基本设置
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(0, 5, 10);
​
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);
​
        const light = new THREE.DirectionalLight(0xffffff, 1);
        light.position.set(0, 10, 10);
        scene.add(light);
​
        // 添加示例模型
        const geometry = new THREE.BoxGeometry();
        const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
        const cube = new THREE.Mesh(geometry, material);
        cube.position.set(0, 1, 0);
        scene.add(cube);
​
        const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32);
        const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
        const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.position.set(2, 1, 0);
        scene.add(sphere);
​
        // 动画函数
        function smoothCameraTransition(targetPosition, targetLookAt) {
            const startPosition = camera.position.clone();
            const startLookAt = new THREE.Vector3();
            camera.getWorldDirection(startLookAt);
​
            new TWEEN.Tween(startPosition)
                .to(targetPosition, 2000)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .onUpdate(() => {
                    camera.position.copy(startPosition);
                })
                .start();
​
            new TWEEN.Tween(startLookAt)
                .to(targetLookAt, 2000)
                .easing(TWEEN.Easing.Quadratic.InOut)
                .onUpdate(() => {
                    camera.lookAt(startLookAt);
                })
                .start();
        }
​
        function animate() {
            requestAnimationFrame(animate);
            TWEEN.update();
            renderer.render(scene, camera);
        }
        animate();
​
        // 添加射线投射器
        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2();
​
        function onMouseClick(event) {
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
            raycaster.setFromCamera(mouse, camera);
            const intersects = raycaster.intersectObjects(scene.children);
​
            if (intersects.length > 0) {
                const intersectedObject = intersects[0].object;
                const targetPosition = new THREE.Vector3().copy(intersectedObject.position).add(new THREE.Vector3(0, 2, 5));
                const targetLookAt = intersectedObject.position.clone();
                smoothCameraTransition(targetPosition, targetLookAt);
            }
        }
​
        window.addEventListener('click', onMouseClick, false);
    </script>
</body>
</html>

通过以上步骤,我们成功实现了 Three.js 中的平滑相机视角切换和点击模型视角切换功能。这种技术可以大大提升用户的交互体验,并为3D场景添加更多的动态效果。希望本文对你在 Three.js 开发中有所帮助。

附送250套精选项目源码

源码截图

 源码获取:关注公众号「码农园区」,回复 【源码】,即可获取全套源码下载链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1818922.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

镜像拉取失败:[ERROR] Failed to pull docker image

问题描述 执行 bash docker/scripts/dev_start.sh 命令提示错误&#xff1a; permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post “http://%2Fvar%2Frun%2Fdocker.sock/v1.45/images/create?fromImageregistry.b…

DBeaverUE Mac版:数据库管理新纪元,一键掌控所有数据

DBeaverUE for Mac是一款卓越的数据库管理软件&#xff0c;专为Mac用户设计&#xff0c;提供了一套高效、稳定且全面的数据库解决方案。以下是关于这款插件的详细介绍&#xff1a; 1. 强大的数据库支持 DBeaverUE for Mac支持多种数据库系统&#xff0c;包括但不限于MySQL、Po…

警务反诈RPA的用途:提高反诈骗工作效率,保护公众财产安全

互联网时代&#xff0c;电信诈骗手段不断翻新&#xff0c;作案地域广&#xff0c;打击难度大&#xff0c;反诈工作迎来巨大的挑战。为了提升办案效率&#xff0c;精准打击犯罪&#xff0c;以科技赋能反诈工作、构建反诈新格局迫在眉睫。而RPA机器人由于能够快速、准确地处理大量…

使用大模型进行时间序列预测

今天想聊聊这周一篇关于使用语言模型进行时间序列预测的工作&#xff0c;这个工作的主要亮点有四个: 首先提出的Chronos框架将时间序列通过缩放和量化转换为token序列&#xff0c;从而可以直接使用语言模型架构(如T5, GPT-2等)来建模时间序列&#xff0c;不需要对模型架构做任…

早知 121私人导航升级新版本, 第一次使用原生dialog标签。

早知121项目介绍说明 早知121 - 一个快速创建私人导航网站。 用途&#xff1a; 创建个人的工作导航&#xff0c;收集常用网址&#xff0c;可贡献给同事。创建个人垂直领域导航 优点&#xff1a; - 不需懂技术&#xff0c;不用维护服务器&#xff0c;维护私人导航收藏站。 网…

全域推广和标准推广哪个更好。谁更容易获客?

随着全域概念的兴起&#xff0c;全域推广逐渐走进人们视野&#xff0c;并成为新的互联网热词。在此背景下&#xff0c;与全域推广相关的话题&#xff0c;如全域推广是什么及全域推广和标准推广的区别等成为了许多创业者讨论和搜索的对象。 所谓的全域推广&#xff0c;简单来说…

redis 一些笔记1

redis 一、redis事务二、管道2.1 事务与管道的区别 三、主从复制3.13.2 权限细节3.3 基本操作命令3.4 常用3.4.1 一主几从3.4.2 薪火相传3.4.3 反客为主 3.5 步骤3.6 缺点 一、redis事务 放在一个队列里&#xff0c;依次执行&#xff0c;并不保证一致性。与mysql事务不同。 命…

酒店安全管理新趋势:远程可燃气体报警器的应用与校准维护

随着酒店行业的快速发展和人们对安全问题的日益关注&#xff0c;酒店安全管理已成为业界的重要议题。 近年来&#xff0c;远程可燃气体报警器作为一种先进的安全技术&#xff0c;逐渐在酒店行业中得到广泛应用&#xff0c;为酒店的安全管理提供了强有力的保障。 一、酒店安全新…

element 树组件 tree 横向纵向滚动条

Html <el-cardshadow"hover"class"solo flex-2"style"height: calc(100vh - 1.6rem); border: 1px solid #ebeef5"><div slot"header" class"clearfix"><span>问题分类</span></div><div …

千万级流量冲击下,如何保证极致性能

1 简要介绍 随着互联网的快速发展&#xff0c;网络应用的流量规模不断攀升&#xff0c;特别是在电商大促、明星直播、重大赛事、头条热搜等热点事件中&#xff0c;秒级100w请求成为了常态。在这样的流量冲击下&#xff0c;如何确保系统稳定、高效地处理每一个请求&#xff0c;为…

ip地址公和内有什么区别

在数字化世界中&#xff0c;IP地址扮演着至关重要的角色。它不仅是网络设备的身份标识&#xff0c;更是信息传输的桥梁。然而&#xff0c;并非所有IP地址都拥有相同的属性和功能。公有IP地址和私有IP地址&#xff0c;作为IP地址的两大类别&#xff0c;它们存在着显著的差异。虎…

【网络编程】基于TCP的服务器端/客户端

TCP是Transmission Control Protocol(传输控制协议)简写。因为TCP套接字是面向连接的&#xff0c;因此又称为基于流的套接字。 把协议分为多个层次&#xff0c;设计更容易&#xff0c;通过标准化操作设计开放式系统 网络层介绍 链路层 链路层是物理连接领域标准化的结果&…

车牌号识别(低级版)

import cv2 from matplotlib import pyplot as plt import os import numpy as np from paddleocr import PaddleOCR, draw_ocr from PIL import Image, ImageDraw, ImageFont# 利用paddelOCR进行文字扫描&#xff0c;并输出结果 def text_scan(img_path):ocr PaddleOCR(use_a…

自养号测评避坑指南

朋友们&#xff0c;今天来聊一聊关于自养号测评的一些重要知识点——自养号测评避坑指南。 在跨境电商领域中&#xff0c;自养号测评是每个卖家必需要会的技术。它能帮助商家提升店铺的权重和产品排名&#xff0c;从而带来曝光率和销售量。 但这个过程里也有不少坑&#xff0…

失眠焦虑的解脱之道:找回内心的平静

&#x1f343; 在这个快节奏的时代&#xff0c;失眠与焦虑似乎成了许多人的隐形敌人。每当夜幕降临&#xff0c;它们便悄悄潜入心底&#xff0c;扰乱我们的思绪&#xff0c;让宁静的夜晚变得无比漫长。然而&#xff0c;生活总有办法让我们找回内心的平静&#xff0c;只需稍作调…

OLED柔性屏的显示效果如何

OLED柔性屏的显示效果非常出色&#xff0c;具有多方面的优势。以下是关于OLED柔性屏显示效果的详细分析&#xff1a; 色彩表现&#xff1a;OLED柔性屏的每个像素都可以独立发光&#xff0c;因此色彩准确性极高。黑色呈现得非常深邃&#xff0c;而亮部则展现出鲜明而生动的细节。…

2024网络安全学习路线 非常详细 推荐学习

关键词&#xff1a;网络安全入门、渗透测试学习、零基础学安全、网络安全学习路线 首先咱们聊聊&#xff0c;学习网络安全方向通常会有哪些问题 1、打基础时间太长 学基础花费很长时间&#xff0c;光语言都有几门&#xff0c;有些人会倒在学习 linux 系统及命令的路上&#…

【云原生】Kubernetes----Rancher助力Kubernetes监控

目录 引言 一、为什么需要监控K8s集群&#xff1f; 二、Rancher监控K8s集群的优势 三、Rancher和k8s的区别 四、Rancher 安装及配置 &#xff08;一&#xff09;安装rancher 1.下载镜像 2.运行容器 3.登录Rancher平台 4.添加集群 5.查看集群 6.Rancher 部署监控系统…

私域引流宝PHP源码 以及搭建教程

私域引流宝PHP源码 以及搭建教程

uni-date-picker 禁用日期功能

在uni-datetime-picker组件中 calendar.vue <template><view class"uni-calendar" mouseleave"leaveCale"><view v-if"!insert && show" class"uni-calendar__mask" :class"{uni-calendar--mask-show:an…