forked from EXT/VR180-Web-Player
Compare commits
8 Commits
revert-1-f
...
v1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d52a722ce7 | ||
|
|
40e3711925 | ||
|
|
c41ad12b32 | ||
|
|
dbe6e5b1d9 | ||
|
|
626b7f451b | ||
|
|
a56e36eaf6 | ||
|
|
bebcb3d355 | ||
|
|
f4fb9cf6bb |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
index.html export-ignore
|
||||
README.md export-ignore
|
||||
sbs-video.mp4 export-ignore
|
||||
poster.jpg export-ignore
|
||||
@@ -4,10 +4,9 @@ A web-based video player for 180 degree, 3D video.
|
||||
Got an immersive video you want people to see with the Apple Vision Pro or Meta Quest headsets? Now you can put it on your website just like any other video! People will see the immersive 3D video if they have a capable headset or they'll get a 2D version on other devices.
|
||||
|
||||
## How to use it
|
||||
1. Drop the `vr180player` directory in the root level of your website.
|
||||
2. Link to the player CSS file `<link rel="stylesheet" href="vr180player/vr180-player.css">`.
|
||||
3. Add the player script `<script type="module" src="vr180player/vr180-player.js"></script>` before the closing body tag.
|
||||
4. And use this HTML snippet to embed your video:
|
||||
1. Link to the player CSS file `<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Verdi/VR180-Web-Player@v1.0.1/vr180player/vr180-player.css">`.
|
||||
2. Add the player script `<script type="module" src="https://cdn.jsdelivr.net/gh/Verdi/VR180-Web-Player@v1.0.1/vr180player/vr180-player.js"></script>` before the closing body tag.
|
||||
3. And use this HTML snippet to embed your video:
|
||||
```
|
||||
<div id="vr-container">
|
||||
<video id="vr180" poster="poster.jpg" title="Demo Video" crossOrigin="anonymous" playsinline>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>VR180 Web Player</title>
|
||||
<link rel="stylesheet" href="vr180player/vr180-player.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Verdi/VR180-Web-Player@v1.0.1/vr180player/vr180-player.css">
|
||||
<style>
|
||||
body {
|
||||
font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
@@ -28,6 +28,6 @@
|
||||
<!-- UI elements will be dynamically inserted here by JavaScript -->
|
||||
</div>
|
||||
</main>
|
||||
<script type="module" src="vr180player/vr180-player.js"></script>
|
||||
<script type="module" src="https://cdn.jsdelivr.net/gh/Verdi/VR180-Web-Player@v1.0.1/vr180player/vr180-player.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as THREE from 'https://unpkg.com/three/build/three.module.js';
|
||||
const _playerBase = new URL('.', import.meta.url).href;
|
||||
|
||||
let scene, camera, renderer, video, videoTexture, sphereMaterial;
|
||||
let vr180Mesh;
|
||||
@@ -111,7 +112,7 @@ function createPlayButton() {
|
||||
playButton.setAttribute('aria-label', 'Play video');
|
||||
|
||||
const playImg = document.createElement('img');
|
||||
playImg.src = 'vr180player/images/play.png';
|
||||
playImg.src = _playerBase + 'images/play.png';
|
||||
playImg.alt = 'Play';
|
||||
|
||||
playButton.appendChild(playImg);
|
||||
@@ -429,24 +430,41 @@ function init() {
|
||||
}
|
||||
|
||||
const xrCamera = renderer.xr.getCamera();
|
||||
let isLeftEye = true; // Default to left eye
|
||||
|
||||
if (xrCamera && xrCamera.cameras && xrCamera.cameras.length >= 2) {
|
||||
// Method 1: Direct camera reference comparison
|
||||
if (activeCamera === xrCamera.cameras[0]) {
|
||||
material.map.offset.x = 0;
|
||||
isLeftEye = true;
|
||||
} else if (activeCamera === xrCamera.cameras[1]) {
|
||||
material.map.offset.x = 0.5;
|
||||
isLeftEye = false;
|
||||
} else {
|
||||
material.map.offset.x = 0;
|
||||
// Method 2: View matrix position (matrixWorldInverse.elements[12] is the X translation)
|
||||
const viewMatrixX = activeCamera.matrixWorldInverse.elements[12];
|
||||
const leftCamX = xrCamera.cameras[0].matrixWorldInverse.elements[12];
|
||||
const rightCamX = xrCamera.cameras[1].matrixWorldInverse.elements[12];
|
||||
|
||||
const diffToLeft = Math.abs(viewMatrixX - leftCamX);
|
||||
const diffToRight = Math.abs(viewMatrixX - rightCamX);
|
||||
|
||||
if (diffToLeft < 0.001 || diffToLeft < diffToRight) {
|
||||
isLeftEye = true;
|
||||
} else if (diffToRight < 0.001) {
|
||||
isLeftEye = false;
|
||||
} else {
|
||||
// Method 3: Projection matrix asymmetry (elements[8] indicates eye offset)
|
||||
const projMatrixEl8 = activeCamera.projectionMatrix.elements[8];
|
||||
isLeftEye = projMatrixEl8 <= 0;
|
||||
}
|
||||
}
|
||||
material.map.repeat.x = 0.5;
|
||||
} else {
|
||||
// Fallback when xrCamera.cameras is not available
|
||||
const projMatrixEl8 = activeCamera.projectionMatrix.elements[8];
|
||||
if (projMatrixEl8 < -0.0001) {
|
||||
material.map.offset.x = 0; material.map.repeat.x = 0.5;
|
||||
} else if (projMatrixEl8 > 0.0001) {
|
||||
material.map.offset.x = 0.5; material.map.repeat.x = 0.5;
|
||||
}
|
||||
isLeftEye = projMatrixEl8 <= 0;
|
||||
}
|
||||
|
||||
material.map.offset.x = isLeftEye ? 0 : 0.5;
|
||||
material.map.repeat.x = 0.5;
|
||||
};
|
||||
|
||||
// Initialize 2D camera
|
||||
@@ -1731,6 +1749,10 @@ function renderXR(timestamp, frame) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
// Sync video texture before render to ensure frame consistency
|
||||
if (videoTexture && video && !video.paused && !video.ended) {
|
||||
videoTexture.needsUpdate = true;
|
||||
}
|
||||
handleControllerInteractions();
|
||||
renderer.render(scene, camera);
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user