<template>
<div>
<div id="renderer-container">
<!-- 左侧 -->
<!-- <div class="left" onmouseover="over" >
《
</div> -->
</div>
</div>
</template>
<script setup lang="ts">
// 引入three.js
import * as THREE from 'three';
import { onMounted, ref, reactive, nextTick } from 'vue';
console.log(THREE);
let video: HTMLVideoElement;
let texture: THREE.VideoTexture;
var renderer: THREE.WebGLRenderer;
let scene: THREE.Scene;
let camera: THREE.PerspectiveCamera;
onMounted(() => {
// 全景设备参数
const DEVICE_OPTS = {
width: 12800, // 默认宽度,周界坐标宽度
height: 1658, // 默认高度,周界坐标高度
minDistance: 400,
distance: 2000,
distanceStep: 340,
maxDistance: 2100,
zoomSpeed: 6,
locateLimitX: 1450, // 标签定位到视图中心,限制X值(因限制移动边界,边界上的标签无法定位到视图中心,故限制了定位中心边界)
locateLimitY: 120, // 标签定位到视图中心,限制Y值(因限制移动边界,边界上的标签无法定位到视图中心,故限制了定位中心边界)
labelShowLevelNum: 5 // 控制标签分级显示的分级数量
};
// 要素唯一标识
const FID_META = {
perimeter: 'perimeter', // 周界
label: 'label', // 标签
linkLocate: 'linkLocate', // 联动定位
clapping_video: 'clapping_video', // 裁剪视频
}
// let src ='rec_20240222110402_20240222120401_cf0f1bcc1293610f.mp4';
// let configs=window.ConfigMeta
const container = document.getElementById("renderer-container");
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x00ff00);
const distance = DEVICE_OPTS.distance;
const aspect = 6.9922222;//
const width = DEVICE_OPTS.width;
const height = width / aspect;
const fov = getCameraFov(container, distance, width); // 获取摄像机视锥体垂直视野角度
camera = new THREE.PerspectiveCamera(fov, container.clientWidth / container.clientHeight, 0.1, 10000);
camera.position.set(0, 0, distance);
// 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
console.log(window.devicePixelRatio);
renderer.setPixelRatio(window.devicePixelRatio)// 设置设备像素比
// 设置渲染器大小
renderer.setSize(container.clientWidth, container.clientHeight);
// 获取box
container.appendChild(renderer.domElement);
// renderer.setClearColor(0x000000, 0) // 渲染背景颜色
// renderer.localClippingEnabled = true // 定义渲染器是否考虑对象级剪切平面
//渲染器大小随窗口大小改变
window.addEventListener("resize", function () {
console.log('resize');
let width = container.clientWidth;
let height = width / aspect;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
});
const geometry = new THREE.BoxGeometry(width, height, 0.1); // 构建一个立方体
geometry.computeBoundingBox(); // 计算立方体的边界
if (geometry.boundingBox) {
// console.log('bounding box: ', geometry.boundingBox)
// const { min, max } = geometry.boundingBox;
// this.extent = [min.x, min.y, max.x, max.y];
}
// const videoTexture = getVideoTexture() // 获取视频纹理
video = document.createElement('video')//创建视频元素
video.src = "./rec_20240222100402_20240222110402_cf0f1bcc1293610f.mp4";
video.preload = 'auto'
video.autoplay = true
video.loop = true;//循环播放
video.muted = true;//静音
video.crossOrigin = "anonymous";//跨域
video.setAttribute("webkit-playsinline", "webkit-playsinline");//ios内联播放
video.play();//播放视频
texture = new THREE.VideoTexture(video);//创建视频纹理
texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
texture.minFilter = THREE.LinearFilter;//线性滤波
texture.magFilter = THREE.LinearFilter;
// texture.format = THREE.RGBFormat;
const material = new THREE.MeshBasicMaterial({ color: 0xffff00, opacity: 0.0, transparent: true });
let videoMesh = new THREE.Mesh(geometry, material);
scene.add(videoMesh)//添加到场景中
var texture2 = new THREE.VideoTexture(video);//创建视频纹理
texture2.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
texture2.minFilter = THREE.LinearFilter;//线性滤波
texture2.magFilter = THREE.LinearFilter;
// texture2.format = THREE.RGBFormat;
const spliceWidth = width / 2 // 拼接立方体宽度
const spliceHeight = height * 2 // 拼接立方体高度
// 构建立方体1(左边,需要裁剪下部 3/4)
const geometry1 = new THREE.BoxGeometry(spliceWidth, spliceHeight, 0.1) // 构建一个立方体1
const material1 = new THREE.MeshBasicMaterial({
// color: 0xff0000,
// opacity: 0.5,
// transparent: true,
map: texture,
clippingPlanes: [new THREE.Plane(new THREE.Vector3(0, 1, 0), spliceHeight / 4)], // 裁剪平面
clipShadows: true // 定义是否根据此材质上指定的剪裁平面剪切阴影
})
const mesh1 = new THREE.Mesh(geometry1, material1)
mesh1.position.set(-spliceWidth / 2, -spliceHeight / 4, 0); // 设置位置
mesh1.name = FID_META.clapping_video;
videoMesh.add(mesh1)
// 构建立方体2(右边,需要裁剪上部 3/4)--- 拷贝 mesh 和 material
const mesh2 = mesh1.clone()
const material2 = material1.clone()
material2.clippingPlanes = [new THREE.Plane(new THREE.Vector3(0, -1, 0), spliceHeight / 4)]
mesh2.material = material2
mesh2.position.set(spliceWidth / 2, spliceHeight / 4, 0); // 设置位置
mesh1.name = FID_META.clapping_video;
videoMesh.add(mesh2)
scene.add(videoMesh);
animate();
})
// 渲染函数
function animate() {
requestAnimationFrame(animate);
if (video.readyState === video.HAVE_ENOUGH_DATA) {
texture.needsUpdate = true;
}
// if (video2.readyState === video2.HAVE_ENOUGH_DATA) {
// texture2.needsUpdate = true;
// }
renderer.render(scene, camera);
}
function getCameraFov(element: any, distance: any, width: any) {
console.log(element.clientWidth, element.clientHeight);
const aspect = element.clientWidth / element.clientHeight
const fov = 2 * Math.atan(width / aspect / (2 * distance)) * (180 / Math.PI) // in degrees
return fov
}
</script>
<style scoped lang='scss'>
@import './index.scss';
</style>
使用three.js 将一段上下的视频做成全景
最新推荐文章于 2024-05-24 15:18:06 发布