three.js学习之vR展厅

目标

1、需要会的知识点three.js的场景,摄像机,渲染器,轨道控制器,坐标轴,场景适配,渲染循环创建立方缓冲几何体、纹理、3d物体
实现:创建立方几何体,纹理贴图镜面反向渲染,摄像机设置在内部,通过与创建3D 标记物体交互事件切换纹理贴图,创建视频纹理实现展厅视频展示
架构:vite + js

预览图

实现

一、初始化 vite 项目

  1. 命令:npm create vite@latest

  2. 选择 Vanilla 模版

  3. 选择 JS 语法

  4. 删除多余的内容
    在这里插入图片描述

  5. 清空 main.js 和 style.css 内容

  6. index.html 中只留下核心代码
    在这里插入图片描述

新建如下untils/init.js文件填入内容

	初始化场景,摄像机,渲染器,轨道控制器,坐标轴,场景适配,渲染循环
// 目标:初始化 three.js 基础环境
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { CSS3DRenderer } from 'three/addons/renderers/CSS3DRenderer.js';
export let scene, camera, renderer, controls, css3dRenderer;(function init() {scene = new THREE.Scene()camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 0.1renderer = new THREE.WebGLRenderer({ antialias: true })renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)
})();(function createControls() {controls = new OrbitControls(camera, renderer.domElement)controls.minPolarAngle = 0.25 * Math.PIcontrols.enableZoom = false
})();(function createHelper() {// const axesHelper = new THREE.AxesHelper(5)// scene.add(axesHelper)
})();(function resizeRender() {window.addEventListener('resize', () => {renderer.setSize(window.innerWidth, window.innerHeight)camera.aspect = window.innerWidth / window.innerHeightcamera.updateProjectionMatrix()})
})();(function create3dRenderer(){css3dRenderer = new CSS3DRenderer()css3dRenderer.setSize(window.innerWidth, window.innerHeight)css3dRenderer.domElement.style.position = 'fixed'css3dRenderer.domElement.style.left = '0'css3dRenderer.domElement.style.top = '0'css3dRenderer.domElement.style.pointerEvents = 'none'document.body.appendChild(css3dRenderer.domElement)
})();(function renderLoop() {renderer.render(scene, camera)controls.update()css3dRenderer.render(scene, camera)requestAnimationFrame(renderLoop)
})();

main.js - 创建立方缓冲几何体

import { camera, scene } from './utils/init.js'
import * as THREE from 'three'function createCube() {const geometry = new THREE.BoxGeometry(1, 1, 1)const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, side: THREE.DoubleSide })const cube = new THREE.Mesh(geometry, material)cube.scale.set(1, 1, -1)scene.add(cube)return cube
}

安装项目需要的所有依赖,并启动项目浏览
在这里插入图片描述

二、展厅-第一个页面内容展示

在这里插入图片描述

目标准备:
1.6 个面纹理图片(镜面翻转)
2.地上热点交互标记(借助 gui 定位位置)
const sceneInfoObj = {one: { // 第一个场景里数据publicPath: 'technology/1/',imgUrlArr: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],markList: [ // 当前空间中所有标记信息对象{name: 'landMark',imgUrl: 'other/landmark.png',wh: [0.05, 0.05],position: [-0.46, -0.11, -0.11],rotation: [1.42, 0.68, 1.63],targetAttr: 'two'}]},
}function setMaterialCube(infoObj) {const { publicPath, imgUrlArr, markList } = infoObjconst textureLoader = new THREE.TextureLoader()textureLoader.setPath(publicPath)const materialArr = imgUrlArr.map(imgStr => {const texture = textureLoader.load(imgStr)texture.colorSpace = THREE.SRGBColorSpacereturn new THREE.MeshBasicMaterial({map: texture,side: THREE.DoubleSide})})cubeObj.material = materialArrmarkList.forEach(markObj => {// 地板标记if (markObj.name === 'landMark') createLandMark(markObj)})scene.add(group)
}function createLandMark(infoObj) {const { imgUrl, wh, position, rotation, targetAttr } = infoObjconst geometry = new THREE.PlaneGeometry(...wh)const material = new THREE.MeshBasicMaterial({map: (new THREE.TextureLoader()).load(imgUrl),side: THREE.DoubleSide,transparent: true})const mesh = new THREE.Mesh(geometry, material)mesh.position.set(...position)mesh.rotation.set(...rotation)// 给地上热点标记添加名字-方便点击时进行区分mesh.name = 'mark'// three.js 3D 物体也可以自定义属性和值(方便后续获取绑定的这个数据)// 绑定这个地上热点标记,要切换到哪个场景信息对象,对应名字属性mesh.userData.attr = targetAttrgroup.add(mesh)
}setMaterialCube(sceneInfoObj.one) // 默认先渲染第一个场景信息

三、展厅第二个页面
在这里插入图片描述

准备
1.准备第二个场景相关数据
2.与 3D 物体交互事件绑定
3.准备清除当前场景热点标记函数
4.切换纹理,重新创建当下场景热点标记
two: {
publicPath: 'technology/2/',
imgUrlArr: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
markList: [{name: 'landMark',imgUrl: 'other/landmark.png',wh: [0.05, 0.05],position: [0.47, -0.2, 0],rotation: [1.48, 0.26, -1.78],targetAttr: 'one' // 目标场景信息对象属性}, {name: 'landMark',imgUrl: 'other/landmark.png',wh: [0.05, 0.05],position: [-0.46, -0.16, -0.3],rotation: [1.21, 0.78, 0],targetAttr: 'three' // 目标场景信息对象属性}
]
},
function clear() {// 清除组内物体const list = [...group.children]list.forEach(obj => {if (!obj.isCSS3DObject) {obj.geometry.dispose()obj.material.dispose()}group.remove(obj)})
}// 在 setMaterialCube 里先调用 clear 清除当下场景空间中的物体标记(都在 Group 组中)function bindClick() {const rayCaster = new THREE.Raycaster()const pointer = new THREE.Vector2()// 5.2 与 3D 物体交互事件绑定window.addEventListener('click', e => {pointer.x = (e.clientX / window.innerWidth) * 2 - 1pointer.y = -(e.clientY / window.innerHeight) * 2 + 1rayCaster.setFromCamera(pointer, camera)const list = rayCaster.intersectObjects(scene.children)// 查找到我点击的热点标记物体const obj = list.find(obj => obj.object.name === 'mark')if (obj) {// 提取物体上绑定的自定义属性,切换场景// 5.4 切换纹理,重新创建当前场景下的热点标记const infoObj = sceneInfoObj[obj.object.userData.attr]setMaterialCube(infoObj)}})
}

四、展厅 第三个页面

在这里插入图片描述

1.准备第三个场景相关数据
2.与 3D 物体交互事件绑定
3.准备清除当前场景热点标记函数
4.切换纹理,重新创建当下场景热点标记
ps:因为前面流程代码已经准备好了,切换点击->关联场景属性 key 名 -> 清空当下空间物体 -> 重新创建新空间物体和纹理贴图,所以准备好数据就可以切换空间了
three: {
publicPath: 'technology/3/',
imgUrlArr: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
markList: [{name: 'landMark',imgUrl: 'other/landmark.png',wh: [0.05, 0.05],position: [0.4, -0.18, 0.32],rotation: [-1.53, -0.04, -1.26],targetAttr: 'two' // 目标场景信息对象属性}, {name: 'landMark',imgUrl: 'other/landmark.png',wh: [0.05, 0.05],position: [0.32, -0.16, -0.33],rotation: [1.46, 0.1, -0.17],targetAttr: 'four' // 目标场景信息对象属性}
]
},

五、展厅-第四个页面
在这里插入图片描述

准备:
1.准备第四个场景相关数据
2.准备创建 DOM 的热点标记函数
(这里采用 DOM 的热点标记,使用 CSS3D 技术)
four: {
publicPath: 'technology/4/',
imgUrlArr: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
markList: [{name: 'landMark',imgUrl: 'other/landmark.png',wh: [0.05, 0.05],position: [-0.35, -0.22, 0.4],rotation: [-0.85, -0.45, -1.8],targetAttr: 'three' // 目标场景信息对象属性},{name: 'dom',position: [0.49, 0, 0],rotation: [0, -0.5 * Math.PI, 0],targetAttr: 'five', // 目标场景信息对象属性active(e) {setMaterialCube(sceneInfoObj.five)}}
]
},
function createDomMark(infoObj) {const { position, rotation, active } = infoObjconst tag = document.createElement('span')tag.className = 'mark-style'tag.innerHTML = '前进'tag.style.pointerEvents = 'all'tag.addEventListener('click', e => {// 为了保证这个函数通用,回调数据对象中的函数代码active(e)})// DOM -> 3D 物体const tag3d = new CSS3DObject(tag)tag3d.scale.set(1 / 800, 1 / 800, 1 / 800)tag3d.position.set(...position)tag3d.rotation.set(...rotation)group.add(tag3d)
}
// 修改 setMaterialCube 内代码
markList.forEach(markObj => {
// 地板标记
if (markObj.name === 'landMark') createLandMark(markObj)
// 原生 DOM 标记
else if (markObj.name === 'dom') createDomMark(markObj)
})

六、展厅-第五个页面

在这里插入图片描述

1.准备第五个场景相关数据
2.准备创建 Video 的物体函数
(这里采用视频转 3D 物体技术)
(浏览器要求当前页面自动播放的视频是静音的,我们可以后续加上声音标签dom或者声音模型
导入进行控制点击声音播放)
3.控制轨道控制器拉动,旋转
five: {publicPath: 'technology/5/',imgUrlArr: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],markList: [{name: 'landMark',imgUrl: 'other/landmark.png',wh: [0.03, 0.03],position: [-0.05, -0.05, 0.4],rotation: [1.21, -0.15, -0.69],targetAttr: 'four' // 目标场景信息对象属性},{name: 'video',imgUrl: 'video/movie.mp4',wh: [0.2, 0.1],position: [0.49, 0.04, 0.045],rotation: [0, -0.5 * Math.PI, 0]}]}
function createVideoMark(infoObj) {
const { imgUrl, wh, position, rotation } = infoObj
// 原生 video 承载视频
const video = document.createElement('video')
video.src = imgUrl
video.muted = true
video.addEventListener('loadedmetadata', () => {
video.play()
})const plane = new THREE.PlaneGeometry(...wh)
const material = new THREE.MeshBasicMaterial({
map: (new THREE.VideoTexture(video))
})
const mesh = new THREE.Mesh(plane, material)
mesh.position.set(...position)
mesh.rotation.set(...rotation)
group.add(mesh)
}
markList.forEach(markObj => {
// 地板标记
if (markObj.name === 'landMark') createLandMark(markObj)
// 原生 DOM 标记
else if (markObj.name === 'dom') createDomMark(markObj)
// Video 标记
else if (markObj.name === 'video') createVideoMark(markObj)
})

git项目地址

https://github.com/geyixia/vr-memorial-hall
注意:video文件超过100M, git push 不上去,我没加git扩展
所以clone下来项目后需要你自己加上一个video
在这里插入图片描述

鸣谢-广告

学程序上黑马,黑马程序员成就IT黑马,感谢黑马讲师的视频课程

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

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

相关文章

Springboot使用sqlcipher4加密sqlite数据库

在有些业务场景,需要使用sqlite数据库,但sqlite数据库生的db文件,是明文的,该文件被别人拿到,就可以看到里面的所有数据,非常不安全,市面上有很多对sqlite数据库文件加密的方式,但都…

vscode远程ssh服务器且更改服务器别名

目录 1、打开VS Code并确保已安装"Remote - SSH"扩展。如果尚未安装,请在扩展市场中搜索并安装它。 2、单击左下角的"Remote Explorer"图标,打开远程资源管理器。 3、在远程资源管理器中,单击右上角的齿轮图标&#x…

1712A 300A嵌入式电源系统

1712A 300A嵌入式电源系统 1712A 300A嵌入式电源系统采用模块化设计、组合式结构,由控制器、整流模块、交流配电单元、直流配电单元等组成。该系统将交流电转换成稳定的-48V直流电,用于铁塔、移动、电信、联通等公司的传输、接入网,以及专网等…

vscode 连接ubuntu git下载缓慢

在ubuntu20.04下载: git clone https://github.com/introlab/rtabmap.git src/rtabmap 挂掉情况 export https_proxyhttp://10.10.10.176:7890export http_proxyhttp://10.10.10.176:7890 其中 10.10.10.176是我本机的ip地址,7890是我的代理后几位 如…

【PPT】ppt里面使用svg图标

要想编辑好的PPT,少不了小图标的美化,图标可以使PPT变得更有趣,更易懂,更美观。 对于png,主要处理它的颜色,可使用【重新着色】功能。 对于jpg,主要处理它的背景,删除背景后同png处…

JSONUtil.parse将java对象转为json时,需要在java对象中设置get、set方法

想要使用JSONUtil.parse将java对象转为json格式&#xff0c;但是一直为空&#xff0c;代码如下 public class MyTest {public static void main(String[] args) {Test3<String> test3 new Test3<>("2","hhhhhhaaa");System.out.println(JSON…

UI设计师岗位的基本职责八篇

UI设计师岗位的基本职责1 职责&#xff1a; 1. 负责公司互联网产品app、web、h5等的用户界面设计工作; 2. 负责运营活动相关的平面及视频设计支持; 3. 负责完成产品相关的界面、图标、动画等的图形界面设计&#xff0c;并参与制定、编写产品视觉设计规范文档; 4. 整理和分…

Oracle 云服务即将支持 PostgreSQL!

2023 年 9 月 19 日&#xff0c;Oracle 产品团队发布了一篇文章&#xff0c;宣布 Oracle 云基础架构&#xff08;OCI&#xff09;开始提供 PostgreSQL 服务。目前支持的版本为 PostgreSQL 14.9&#xff0c;提供有限支持&#xff0c;12 月份将会提供正式版本。 众所周知&#x…

京东获取推荐商品列表 API

item_recommend-获取推荐商品列表 请求参数 请求参数&#xff1a;type 参数说明&#xff1a;type:推荐类型 进入API测试页 响应参数 Version: Date: 名称类型必须示例值描述 items items[]0获取推荐商品列表 num_iid Bigint010021415166448宝贝ID detail_url String0http…

Tabby All configured authentication methods failed

文章目录 重要序言错误原因tabby的连接设置 总结 重要序言 Tabby是一款美观耐用的软件&#xff0c;平常一直用来输入密码方法SSH公司服务器&#xff0c;后来为了另外一台服务器加了SSH私钥&#xff0c;之后Tabby SSH连接死活不成功&#xff0c;哎&#xff0c;折腾了好久&#…

C++ stack和queue模拟实现

目录 stack习题练习逆波兰表达式求值基本计算器 stack模拟实现queuequeue模拟实现deque了解priority_queuepriority_queue模拟实现仿函数 stack stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提…

修炼k8s+flink+hdfs+dlink(三:安装dlink)

一&#xff1a;mysql初始化。 mysql -uroot -p123456 create database dinky; grant all privileges on dinky.* to dinky% identified by dinky with grant option; flush privileges;二&#xff1a;上传dinky。 上传至目录/opt/app/dlink tar -zxvf dlink-release-0.7.4.t…

asp.net饭店订餐管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio计算机设计定制

一、源码特点 asp.net 饭店订餐管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语 言开发 asp.net饭店订餐系统 二、功能介…

HTML5开发实例-3D全景(ThreeJs全景Demo) 详解(图)

前言 在现在市面上很多全景H5的环境下,要实现全景的方式有很多,可以用css3直接构建也可以用基于threeJs的库来实现,还有很多别的制作全景的软件使用 本教学适用于未开发过3D全景的工程狮 如果觉得内容太无聊可以直接跳到最后 下载代码 理论 整个3D全景所用的相关理论就…

知识增强语言模型提示 零样本知识图谱问答10.8

知识增强语言模型提示 零样本知识图谱问答 摘要介绍相关工作方法零样本QA的LM提示知识增强的LM提示与知识问题相关的知识检索 摘要 大型语言模型&#xff08;LLM&#xff09;能够执行 零样本closed-book问答任务 &#xff0c;依靠其在预训练期间存储在参数中的内部知识。然而&…

在React中,什么是props(属性)?如何向组件传递props?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

QTableWidget 表格增删数据

QTableWidgetQTableWidgetQTableWidget部分使用方法&#xff0c;如在表格中插入或删除一行数据以及清空表格数据等。在添加数据时&#xff0c;设置了条件判断如正则表达式&#xff0c;若用户输入的数据不合法&#xff0c;则添加失败并提示用户错误的地方&#xff0c;便于用户修…

API接口安全运营研究(内附官方开发平台api接口接入方式)

摘 要 根据当前API技术发展的趋势&#xff0c;从实际应用中发生的安全事件出发&#xff0c;分析并讨论相关API安全运营问题。从风险角度阐述了API接口安全存在的问题&#xff0c;探讨了API检测技术在安全运营中起到的作用&#xff0c;同时针对API安全运营实践&#xff0c;提出…

Day 4 C++

算术运算符重载 种类&#xff1a; - * / % #include <iostream>using namespace std;class Cacu {friend const Cacu operator(const Cacu &l,const Cacu &r);friend const Cacu operator-(const Cacu &l,const Cacu &r);friend const Cacu operator*…

mysql面试题32:MySQL数据库服务器性能分析的方法命令有哪些?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:MySQL数据库服务器性能分析的方法命令有哪些? MySQL数据库服务器性能分析的方法和命令有以下几种: EXPLAIN命令:用于分析查询语句的执行计划,…