Video 360
360 equirectangular viewer video
Equirectangular 360 Video Viewer. Click on the video to play/pause.
// This work is licensed under CC BY 4.0
// https://creativecommons.org/licenses/by/4.0/
struct VertexOutput {
@builtin(position) pos: vec4<f32>,
@location(0) xy: vec2<f32>,
};
struct Sys {
time: f32,
frame: u32,
mouse: vec4<f32>,
resolution: vec2<f32>,
aspect: vec2<f32>
}
struct Params {
fov: f32,
};
const PI = 3.14159265359;
@group(0) @binding(0) var<uniform> sys: Sys;
@group(0) @binding(1) var<uniform> params: Params;
@group(0) @binding(2) var video360: texture_external;
@group(0) @binding(3) var samp: sampler;
@vertex
fn vertexMain(@location(0) pos: vec2<f32>) -> VertexOutput {
var output: VertexOutput;
// by default we pass the vertices of a square in the range [-1,1] in x and y
output.pos = vec4<f32>(pos, 0.0, 1.0);
// in this case we pass the xy coordinates as normalised coordinates [-1,1].
// [-1,-1] is the bottom left corner and [1,1] is the top right corner
output.xy = pos;
return output;
}
@fragment
fn fragmentMain(input: VertexOutput) -> @location(0) vec4<f32> {
// Convert FOV from degrees to radians and calculate the tan of half the FOV
let fov = tan(radians(params.fov) * 0.5);
// Calculate the ray direction
let dir = normalize(vec3<f32>(input.xy * fov * sys.aspect, -1.0) );
//mouse is [0,0] top left corner and [1,1] bottom right corner
//we adjust so that [0,0] is the center of the screen
let m = sys.mouse.xy * -1. + .5;
// Rotate the ray based on mouse input
let yaw = m.x * 2.0 * PI;
let pitch = m.y * PI;
// Create rotation matrices
let cosy = cos(yaw);
let siny = sin(yaw);
let cosp = cos(pitch);
let sinp = sin(pitch);
// Rotate around Y-axis (yaw)
let rotY = mat3x3<f32>(
cosy, 0.0, -siny,
0.0, 1.0, 0.0,
siny, 0.0, cosy
);
// Rotate around X-axis (pitch)
let rotX = mat3x3<f32>(
1.0, 0.0, 0.0,
0.0, cosp, sinp,
0.0, -sinp, cosp
);
// Apply rotations: first yaw, then pitch
let rotdir = rotY * rotX * dir;
// Convert the rotated direction to equirectangular coordinates
let equ = atan2(rotdir.x, -rotdir.z) / (2.0 * PI) + .5;
let eqv = acos(rotdir.y) / PI;
// Sample the equirectangular video texture
return textureSampleBaseClampToEdge(video360, samp, vec2<f32>(equ, eqv));
}
import { PSpec, Definitions } from "../../lib/poiesis/index.ts";
export const video360 = async (code:string, defs:Definitions) => {
// Create a video element to capture the video stream
const video = document.createElement('video');
video.src = "/assets/video/redsea.mp4";
video.loop = true;
video.muted = true;
await video.play();
video.pause();
document.addEventListener('click', () => {
video.muted = false;
video.paused ? video.play() : video.pause();
});
const spec = (w:number, h: number):PSpec => {
return {
code: code,
defs: defs,
uniforms: () => ({ params: { fov: 90 } }),
textures: [
{ name: "video360", data: video }
]
}
}
return spec;
}