Three.js+TypeScript+Webpack学习记录(三)

news/2024/3/28 17:16:41/文章来源:https://blog.csdn.net/kuokuo666/article/details/130313907

使用环境参考

Node.js v16.19.1

正文

独立功能文件

我们不可能一直在 index.ts 中写代码,分离文件:

// init.ts
import * as THREE from 'three'export const initScene = () => {const scene = new THREE.Scene()scene.background = new THREE.Color('white')const light = new THREE.AmbientLight('white', 1.3)scene.add(light)return scene
}export const initCamera = () => {const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.set(0, 3, 5)camera.lookAt(new THREE.Vector3(0, 0, 0))return camera
}export const initWebGLRenderer = () => {const renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)return renderer
}
// load.ts
import * as THREE from 'three'
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'export const loadGLTF = (url: string) => new Promise<THREE.Group>((resolve, reject) => {const loader = new GLTFLoader()loader.load(url, (gltf: GLTF) => {console.log(gltf)resolve(gltf.scene)})
})
// index.ts
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { initCamera, initScene, initWebGLRenderer } from './init'
import { loadGLTF } from './load'class Game {scene: THREE.Scenecamera: THREE.PerspectiveCamerarenderer: THREE.WebGLRendererorbitControls: OrbitControlsconstructor() {this.scene = initScene()this.camera = initCamera()this.scene.add(this.camera)this.renderer = initWebGLRenderer()this.orbitControls = this.addOrbitControls(this.camera, this.renderer)this.addModel()this.addResizeEventListener()}addOrbitControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {const controls = new OrbitControls(camera, renderer.domElement)controls.autoRotate = truecontrols.enableDamping = truecontrols.update()return controls}async addModel() {const model = await loadGLTF('gltf/SheenChair.glb')this.scene.add(model)}addResizeEventListener() {window.addEventListener('resize', () => {this.camera.aspect = window.innerWidth / window.innerHeightthis.camera.updateProjectionMatrix()this.renderer.setSize(window.innerWidth, window.innerHeight)})}startMainLoop() {// 等待一帧用于初始化Promise.resolve().then(() => {this.step()})}step() {requestAnimationFrame(this.step.bind(this))this.orbitControls && this.orbitControls.update()this.renderer.render(this.scene, this.camera)}
}const game = new Game()
game.startMainLoop()

射线检测

鼠标点击物体是最常见的一个需求,对 dom 新增点击事件,然后计算相对于 canvas 的坐标比例,计算进 three 的坐标系(-1 ~ 1)。

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { initCamera, initScene, initWebGLRenderer } from './init'
import { loadGLTF } from './load'class Game {scene: THREE.Scenecamera: THREE.PerspectiveCamerarenderer: THREE.WebGLRendererorbitControls: OrbitControlsraycaster = new THREE.Raycaster()mouse = new THREE.Vector2()constructor() {this.scene = initScene()this.camera = initCamera()this.scene.add(this.camera)this.renderer = initWebGLRenderer()this.orbitControls = this.addOrbitControls(this.camera, this.renderer)this.addModel()this.addResizeEventListener()this.addClickEvent()}addClickEvent() {this.renderer.domElement.addEventListener('click', (ev) => {this.mouse.x = (ev.clientX / window.innerWidth) * 2 - 1this.mouse.y = -(ev.clientY / window.innerHeight) * 2 + 1this.raycaster.setFromCamera(this.mouse, this.camera)const intersects = this.raycaster.intersectObject(this.scene, true)console.log(intersects)})}addOrbitControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {const controls = new OrbitControls(camera, renderer.domElement)controls.autoRotate = truecontrols.enableDamping = truecontrols.update()return controls}async addModel() {const model = await loadGLTF('gltf/SheenChair.glb')this.scene.add(model)}addResizeEventListener() {window.addEventListener('resize', () => {this.camera.aspect = window.innerWidth / window.innerHeightthis.camera.updateProjectionMatrix()this.renderer.setSize(window.innerWidth, window.innerHeight)})}startMainLoop() {// 等待一帧用于初始化Promise.resolve().then(() => {this.step()})}step() {requestAnimationFrame(this.step.bind(this))this.orbitControls && this.orbitControls.update()this.renderer.render(this.scene, this.camera)}
}const game = new Game()
game.startMainLoop()

动画轨道

鼠标点击某个模型,对这个模型进行动画处理。

新建一个 animation.ts 负责处理动画

// animation.ts
import * as THREE from 'three'export class AnimationManager {mixers: THREE.AnimationMixer[] = []clock = new THREE.Clock()addOnePosAnima(obj: THREE.Object3D) {const positionTimes = [0, 1, 2]const positionArr = [0, 0, 0,0, 1, 0,0, 0, 0]const track = new THREE.VectorKeyframeTrack(`${obj.name}.position`,positionTimes,positionArr,THREE.InterpolateSmooth)// 动画名称,持续时间,trackconst clip = new THREE.AnimationClip('clip1', 2, [track])const mixer = new THREE.AnimationMixer(obj)const action = mixer.clipAction(clip)action.play()this.mixers.push(mixer)}step() {const dt = this.clock.getDelta()this.mixers.forEach(m => m.update(dt))}
}

在 index.ts 中,点击到哪儿模型就为其添加一个动画。

import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { initCamera, initScene, initWebGLRenderer } from './init'
import { loadGLTF } from './load'
import { AnimationManager } from './animation'class Game {scene: THREE.Scenecamera: THREE.PerspectiveCamerarenderer: THREE.WebGLRendererorbitControls: OrbitControlsraycaster = new THREE.Raycaster()mouse = new THREE.Vector2()animationManager = new AnimationManager()constructor() {this.scene = initScene()this.camera = initCamera()this.scene.add(this.camera)this.renderer = initWebGLRenderer()this.orbitControls = this.addOrbitControls(this.camera, this.renderer)this.addModel()this.addResizeEventListener()this.addClickEvent()}addClickEvent() {this.renderer.domElement.addEventListener('click', (ev) => {this.mouse.x = (ev.clientX / window.innerWidth) * 2 - 1this.mouse.y = -(ev.clientY / window.innerHeight) * 2 + 1this.raycaster.setFromCamera(this.mouse, this.camera)const intersects = this.raycaster.intersectObject(this.scene, true)console.log(intersects)if (intersects[0]) {this.animationManager.addOnePosAnima(intersects[0].object)}})}addOrbitControls(camera: THREE.Camera, renderer: THREE.WebGLRenderer) {const controls = new OrbitControls(camera, renderer.domElement)controls.autoRotate = truecontrols.enableDamping = truecontrols.update()return controls}async addModel() {const model = await loadGLTF('gltf/SheenChair.glb')this.scene.add(model)}addResizeEventListener() {window.addEventListener('resize', () => {this.camera.aspect = window.innerWidth / window.innerHeightthis.camera.updateProjectionMatrix()this.renderer.setSize(window.innerWidth, window.innerHeight)})}startMainLoop() {// 等待一帧用于初始化Promise.resolve().then(() => {this.step()})}step() {requestAnimationFrame(this.step.bind(this))this.orbitControls && this.orbitControls.update()this.animationManager.step()this.renderer.render(this.scene, this.camera)}
}const game = new Game()
game.startMainLoop()

要注意模型是由多个子 Mesh 组成,如果想动整体,查询 parent 或者提前用变量存好或者记录 name 进行查找。

更多文章与分享

Three 学习项目链接:https://github.com/KuoKuo666/threejs-study

个人网站:www.kuokuo666.com

2023!Day Day Up!

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT能用来写小说吗-gpt可以续写小说吗

怎么用ChatGPT写网文 ChatGPT是一个语言生成模型&#xff0c;可以用于生成各种文本&#xff0c;包括网文。下面是一些写网文的建议。 确定你的主题和情节。在开始写作之前&#xff0c;你需要确保你有一个明确的主题和情节&#xff0c;这可以帮助你更好地组织你的故事&#xff0…

电脑硬盘分区合并怎么操作?分享2个方法!

案例&#xff1a;电脑硬盘怎么分区&#xff1f; 【我把我的电脑硬盘分成了多个区域&#xff0c;这样可以方便存储和管理数据。现在我需要调整分区&#xff0c;对分区进行合并&#xff0c;但我不知道该如何操作&#xff0c;有没有小伙伴知道&#xff1f;】 在使用电脑的过程中…

【python视图3】networkx图操作示例

一、说明 根据定义&#xff0c;图是节点&#xff08;顶点&#xff09;以及已识别的节点对&#xff08;称为边、链接等&#xff09;的集合。在 NetworkX 中&#xff0c;节点可以是任何可哈希对象&#xff0c;例如文本字符串、图像、XML 对象、另一个图形、自定义节点对象等。 如…

perf生成火焰图

文章目录 1&#xff0c;top发现webserver进程空转情况下CPU占用高达200%2&#xff0c;使用性能分析工具perf来进行分析2.1&#xff0c;抓取采集样本2.2&#xff0c;使用perf简单分析性能数据 3&#xff0c;火焰图3.1&#xff0c;生成火焰图3.2&#xff0c;将生成的.svg文件用浏…

Chapter 4 :Constraining I/O Delay(ug903)

4.1 About Constraining I/O Delay 要在设计中准确地建模外部时序上下文&#xff0c;必须为输入和输出端口提供时序信息。由于XilinxVivado集成设计环境&#xff08;IDE&#xff09;只能识别FPGA边界内的时序&#xff0c;因此必须使用以下命令来指定超出这些边界的延迟…

数据库系统工程师——第五章 网络基础知识

文章目录 &#x1f4c2; 第五章、网络基础知识 &#x1f4c1; 5.1 计算机网络概述 &#x1f4d6; 5.1.1 计算机网络的概念 &#x1f4d6; 5.1.2 计算机网络的分类 &#x1f4d6; 5.1.3 网络的拓扑结构 &#x1f4c1; 5.2 网络硬件基础 &#x1f4d6; 5.2.1 网络设备 &…

【k8s】ruoyi微服务迁移到k8s

书接上回【传统方式部署Ruoyi微服务】&#xff0c;此刻要迁移至k8s。 环境说明 31 master &#xff0c; 32 node1 &#xff0c; 33 node2迁移思路 交付思路: 其实和交付到Linux主机上是一样的&#xff0c;无外乎将这些微服务都做成了Docker镜像; 1、微服务数据层: MySQL、 R…

“井电双控”地下水远程计量设施-实现地下水资源合理利用

“井电双控”地下水远程计量设施&#xff08;MGTR-W4122C&#xff09;是针对取水计量控制系统开发智能终端产品。集预收费、流量监测、电量监测、余额提醒、欠费停机、无线传输、远程控制等多种功能于一体&#xff0c;并可根据项目需求选择实体IC卡和APP电子卡取水两种方式。其…

换肤实现及LayoutInflater原理

文章目录 背景实现换肤步骤解析插件 apk 的包信息获取插件 apk 的 Resources 对象替换资源 简单的插件化换肤实现和存在的问题换肤如何动态刷新&#xff1f;控件换肤刷新的性能考虑如何降低 xml 布局中 View 的替换成本LayoutInflater 原理LayoutInflater.Factory2 替换 View 小…

David Silver Reinforcement Learning -- Markov process

1 Introduction 这个章节介绍关键的理论概念。 马尔科夫过程的作用&#xff1a; 1&#xff09;马尔科夫过程描述强化学习环境的方法&#xff0c;环境是完全能观测的&#xff1b; 2&#xff09;几乎所有的RL问题可以转换成MDP的形式&#xff1b; 2 Markov Processes 2.1 Mark…

从源码全面解析LinkedBlockingQueue的来龙去脉

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小黄&#xff0c;独角兽企业的Java开发工程师&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#x1f4d5;系列专栏&#xff1a;Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙…

mall-swarm微服务商城系统

mall-swarm是一套微服务商城系统&#xff0c;采用了 Spring Cloud 2021 & Alibaba、Spring Boot 2.7、Oauth2、MyBatis、Docker、Elasticsearch、Kubernetes等核心技术&#xff0c;同时提供了基于Vue的管理后台方便快速搭建系统。mall-swarm在电商业务的基础集成了注册中心…

【Excel统计分析插件】上海道宁为您提供统计分析、数据可视化和建模软件——Analyse-it

Analyse-it是Microsoft Excel中的 统计分析插件 它为Microsoft Excel带来了 易于使用的统计软件 Analyse-it在软件中 引入了一些新的创新统计分析 Analyse-it与 许多Excel加载项开发人员不同 使用完善的软件开发和QA实践 包括单元/集成/系统测试 敏捷开发、代码审查 …

HCIA-RS实验-ENSP搭建一个基础的IP网络

HCIA-RS是华为认证网络工程师&#xff08;Routing & Switching&#xff09;的缩写。通过考取HCIA-RS证书&#xff0c;可以证明自己有能力设计、实现和维护小型网络。而HCIA-RS实验则是考试的一部分&#xff0c;是考生必须要完成的实践环节。这将是第一篇文章&#xff0c;后…

【Android Framework (八) 】- Service

文章目录 知识回顾启动第一个流程initZygote的流程system_serverServiceManagerBinderLauncher的启动AMS 前言源码分析1.startService2.bindService 拓展知识1:Service的两种启动方式对Service生命周期有什么影响&#xff1f;2:Service的启动流程3:Service的onStartCommand返回…

紧密联结玩家 | 2023 Google 游戏开发者峰会

玩家的选择是对游戏莫大的认可&#xff0c;重视玩家反馈并和他们建立联系是您的游戏取得成功的关键。我们也在努力创造更多机会&#xff0c;让您的游戏从琳琅满目的列表中脱颖而出&#xff0c;帮助您吸引更多用户。 上篇内容我们介绍了帮助您优化游戏性能的几大功能更新&#x…

❀五一劳动节来啦❀

今年“五一”&#xff0c;4月29日至5月3日放假调休&#xff0c;共5天。 如果你在5月4日到5月6日请假3天&#xff0c;加上5月7日周日&#xff0c;就可以形成9天的假期。 一&#xff0c;五一劳动节的由来⭐ 国际劳动节又称“五一国际劳动节”“国际示威游行日”&#xff08;英语…

GPT详细安装教程-GPT软件国内也能使用

GPT (Generative Pre-trained Transformer) 是一种基于 Transformer 模型的自然语言处理模型&#xff0c;由 OpenAI 提出&#xff0c;可以应用于各种任务&#xff0c;如对话系统、文本生成、机器翻译等。GPT-3 是目前最大的语言模型之一&#xff0c;其预训练参数超过了 13 亿个…

python+vue 健康体检预约管理系统

该专门体检预约管理系统包括会员和管理员。其主要功能包括个人中心、会员管理、体检服务管理、类型管理、订单信息管理、取消订单管理、 体检报告管理、通知信息管理、交流论坛、系统管理等功能。 目 录 一、绪论 1 1.1研发背景和意义 2 1.2 国内研究动态 3 1.3论文主…

Cookies和Session案例-注册

1. 注册功能改进 1.1 service 将之前的注册案例的代码进行优化&#xff0c;将获取sqlsession工厂对象、获取sqlsession、获取mapper等操作从servlet中分离出来转变为三层架构的形式 在service目录下创建UserService public class UserService {SqlSessionFactory sqlSessionFa…