当前位置: 首页 > ai >正文

上传图片转成3D VR效果 / VR效果在项目中落地实践 / 应用到了用photo-sphere-viewer + A-Frame +Threejs 通过不同的技术分别实现了3D VR效果

系统简介 : 该系统为 react + TS + tailwindcss 响应式系统 , 上传图片后可实现手动旋转 3D 图片,还包含了 6 贴图立方体展示和 6 贴图动态展示

项目亮点 : 包含主流3D VR库 , 可根据具体需求选择具体的技术栈

全部页面概览

这是单面VR页面的代码(gif展示页面)

import React, { useRef, useState, useEffect } from 'react'
import { Viewer } from 'photo-sphere-viewer'
import 'photo-sphere-viewer/dist/photo-sphere-viewer.css'import { PlusOutlined } from '@ant-design/icons'
import { Image, Upload } from 'antd'
import type { GetProp, UploadFile, UploadProps } from 'antd'type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0]const getBase64 = (file: FileType): Promise<string> =>new Promise((resolve, reject) => {const reader = new FileReader()reader.readAsDataURL(file)reader.onload = () => resolve(reader.result as string)reader.onerror = error => reject(error)})export default function VrUploader () {const [selectedFileUrl, setSelectedFileUrl] = useState('')const [confirmedImageUrl, setConfirmedImageUrl] = useState('')const viewerRef = useRef<HTMLDivElement | null>(null)const viewerInstance = useRef<Viewer | null>(null)const [previewOpen, setPreviewOpen] = useState(false)const [previewImage, setPreviewImage] = useState('')const [fileList, setFileList] = useState<UploadFile[]>([])const handlePreview = async (file: UploadFile) => {if (!file.url && !file.preview) {file.preview = await getBase64(file.originFileObj as FileType)}setPreviewImage(file.url || (file.preview as string))setPreviewOpen(true)}const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {// 始终只保留最后一张图片const latestFileList = newFileList.slice(-1)setFileList(latestFileList)if (latestFileList.length > 0) {const file = latestFileList[0].originFileObj as Fileconst url = URL.createObjectURL(file)setSelectedFileUrl(url)} else {setSelectedFileUrl('')setConfirmedImageUrl('')}}const uploadButton = (<button style={{ border: 0, background: 'none' }} type='button'><PlusOutlined /><div style={{ marginTop: 8 }}>上传</div></button>)const handleConfirm = () => {if (selectedFileUrl) {setConfirmedImageUrl(selectedFileUrl)}}const handleCancel = () => {setFileList([])setSelectedFileUrl('')setConfirmedImageUrl('')// 销毁 Viewer 实例并清空引用if (viewerInstance.current) {viewerInstance.current.destroy()viewerInstance.current = null}}const [isRetote, setIsRetote] = useState(false) // 是否旋转const handleRetote = () => {setIsRetote(prev => {const newState = !previf (viewerInstance.current) {if (newState) {viewerInstance.current.startAutorotate()} else {viewerInstance.current.stopAutorotate()}}return newState})}useEffect(() => {if (confirmedImageUrl && viewerRef.current) {if (viewerInstance.current) {viewerInstance.current.setPanorama(confirmedImageUrl)} else {viewerInstance.current = new Viewer({container: viewerRef.current, //必---html元素信息panorama: confirmedImageUrl, //必---图片路径description: '<p>This is a description.</p>'})}}}, [confirmedImageUrl])return (<div className='p-4 cursor-pointer'><div className='bg-gray-100 flex items-center justify-center w-100'><div className='min-w-[70%]'><h2 className='text-2xl font-bold mb-4 mt-4'><p>单图上传</p><p>VR 生成(photo-sphere-viewer)</p></h2><div className='grid grid-cols-2 md:grid-cols-3 gap-4 mb-4'><Uploadaction='https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload'listType='picture-card'fileList={fileList}onPreview={handlePreview}onChange={handleChange}>{fileList.length >= 8 ? null : uploadButton}</Upload>{previewImage && (<ImagewrapperStyle={{ display: 'none' }}preview={{visible: previewOpen,onVisibleChange: visible => setPreviewOpen(visible),afterOpenChange: visible => !visible && setPreviewImage('')}}src={previewImage}/>)}</div>{/* 按钮区域 */}<div className='flex w-full justify-between'><buttononClick={handleConfirm}disabled={!selectedFileUrl}className={`px-4 py-2 rounded-md text-white ${selectedFileUrl? 'bg-blue-600 hover:bg-blue-700': 'bg-gray-400 cursor-not-allowed'}`}>生成 VR</button><buttononClick={handleRetote}className={`px-4 py-2 rounded-md text-white bg-green-600 hover:bg-green-700`}>{!isRetote ? '开始巡检' : '停止巡检'}</button><buttononClick={handleCancel}className={`px-4 py-2 rounded-md text-white bg-red-600 hover:bg-red-700`}>重置图片</button></div>{/* VR区域 */}<divclassName='h-[50vh] w-full bg-black mt-6 rounded overflow-hidden mb-6'ref={viewerRef}>{!selectedFileUrl && (<p className='text-white p-4'>请上传一张比例为2:1 / 圆形图</p>)}</div></div></div></div>)
}

完整代码 : GitHub - hyy20000804/Img-to-VR: 响应式图片转VR系统

遇到的问题总结: 

一.在单图 VR 模块

1.photo-sphere-viewer 目前 v5 版本不稳定,npm 中最新版本为 4.8.1,官网地址 https://photo-sphere-viewer.js.org/ 要切换为 v4 版本

2.默认配置器选择为等距矩形可处理等矩形图(2:1 的比例)和圆形图(正方形圆形),当传递手机拍摄的照片(9:16/3:4 的比例)时会存在 VR 效果不太完美的情况

3.自动旋转功能(即项目中的"开始巡检")也是通过 v4 的写法实现,如果按照 v5 官网写法会报错

二.在俩个多图 VR 模块中

6 张贴图尺寸不一时会报错警告

http://www.xdnf.cn/news/9898.html

相关文章:

  • 一种冷库低成本节能方案:不改动原有装备,实现年省电≥20%
  • 一台笔记本实现基因表达敲除?!scTenifoldKnk 单细胞基因模拟敲除教程
  • 差分互连的串扰-信号与电源完整性
  • opencut:如何用AI工具把中文图片/视频翻译成英语、日语、俄语等100多种语言!
  • 事务Transaction
  • 【聚焦国产8K全画幅摄像机应用】多图预警!
  • 圣杯布局和双飞翼布局的实现方法
  • 华为OD机试_2025 B卷_最小循环子数组(Python,100分)(附详细解题思路)
  • 技术文档撰写指南:从结构到细节的全流程解析
  • 【面板数据】上市公司供应链网络地位数据(2001-2024年)
  • 【C1】【一维数组】看电影
  • 重说话题“如何写好一份技术文档”
  • 经典深度学习网络【一天了解一个ok?】【基本点创新点】
  • Java中的栈数据结构及其常用方法
  • Cesium 报错:自定义材质报‘texture2D‘ : no matching overloaded function found错误
  • 【Unity】 HTFramework框架(六十六)缺省的运行时组件检视器
  • 「动态规划::状压DP」网格图递推 / AcWing 292|327(C++)
  • 2025京麟CTF-mememe
  • SpringBoot:统一功能处理、拦截器、适配器模式
  • GoC新阶段课程研发
  • jdbcTemplate防止注入写法
  • CompletableFuture高级编程指南
  • Python常用的内置函数
  • web ui自动化工具playwright
  • 【文献阅读】Hierarchical Reinforcement Learning: A ComprehensiveSurvey
  • WordPress_suretriggers 权限绕过漏洞复现(CVE-2025-3102)
  • 在Mathematica中求解带阻尼的波方程
  • 造血干细胞移植中,选择合适供者需综合多因素考量
  • 2025年5月29日 一阶惯性环节
  • 哈夫曼编码