diff --git a/index.html b/index.html
index c25c895..2c128d2 100644
--- a/index.html
+++ b/index.html
@@ -215,6 +215,8 @@
WebGL 3D Gaussian Splat Viewer
- w/s to tilt camera up/down
- q/e to roll camera counterclockwise/clockwise
- i/k and j/l to orbit
+- m to enable stereo view
+- b/n to decrease/increase stereo
trackpad
- scroll up/down/left/right to orbit
diff --git a/main.js b/main.js
index 8f7f527..d232870 100644
--- a/main.js
+++ b/main.js
@@ -1179,6 +1179,10 @@ async function main() {
let leftGamepadTrigger, rightGamepadTrigger;
+ // Globale Variable für Stereo-Basis
+ let stereoEnabled = false; // Standard: Stereo an
+ let eyeOffset = 0.03; // Startwert
+
const frame = (now) => {
let inv = invert4(viewMatrix);
let shiftKey =
@@ -1212,6 +1216,12 @@ async function main() {
if (activeKeys.includes("KeyE")) inv = rotate4(inv, -0.01, 0, 0, 1);
if (activeKeys.includes("KeyW")) inv = rotate4(inv, 0.005, 1, 0, 0);
if (activeKeys.includes("KeyS")) inv = rotate4(inv, -0.005, 1, 0, 0);
+ if (activeKeys.includes("KeyB")) eyeOffset = Math.max(-0.1, eyeOffset - 0.001);
+ if (activeKeys.includes("KeyN")) eyeOffset = Math.min(0.1, eyeOffset + 0.001);
+ if (activeKeys.includes("KeyM")) {
+ stereoEnabled = !stereoEnabled;
+ activeKeys = activeKeys.filter(k => k !== "KeyM");
+ }
const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
let isJumping = activeKeys.includes("Space");
@@ -1357,9 +1367,38 @@ async function main() {
if (vertexCount > 0) {
document.getElementById("spinner").style.display = "none";
- gl.uniformMatrix4fv(u_view, false, actualViewMatrix);
gl.clear(gl.COLOR_BUFFER_BIT);
- gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, vertexCount);
+
+ if (stereoEnabled) {
+ // Stereo-Rendering (Side-by-Side)
+ const halfWidth = gl.canvas.width / 2;
+ // Left Eye
+ let leftProj = getProjectionMatrix(camera.fx, camera.fy, halfWidth, gl.canvas.height);
+ let leftInv = translate4(invert4(actualViewMatrix), -eyeOffset, 0, 0);
+ let leftView = invert4(leftInv);
+ gl.viewport(0, 0, halfWidth, gl.canvas.height);
+ gl.uniform2fv(u_viewport, new Float32Array([halfWidth, gl.canvas.height]));
+ gl.uniformMatrix4fv(u_projection, false, leftProj);
+ gl.uniformMatrix4fv(u_view, false, leftView);
+ gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, vertexCount);
+ // Right Eye
+ let rightProj = getProjectionMatrix(camera.fx, camera.fy, halfWidth, gl.canvas.height);
+ let rightInv = translate4(invert4(actualViewMatrix), eyeOffset, 0, 0);
+ let rightView = invert4(rightInv);
+ gl.viewport(halfWidth, 0, halfWidth, gl.canvas.height);
+ gl.uniform2fv(u_viewport, new Float32Array([halfWidth, gl.canvas.height]));
+ gl.uniformMatrix4fv(u_projection, false, rightProj);
+ gl.uniformMatrix4fv(u_view, false, rightView);
+ gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, vertexCount);
+ } else {
+ // Mono-Rendering
+ gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
+ gl.uniform2fv(u_viewport, new Float32Array([gl.canvas.width, gl.canvas.height]));
+ gl.uniformMatrix4fv(u_projection, false, projectionMatrix);
+ gl.uniformMatrix4fv(u_view, false, actualViewMatrix);
+ gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, vertexCount);
+ }
+
} else {
gl.clear(gl.COLOR_BUFFER_BIT);
document.getElementById("spinner").style.display = "";
@@ -1371,7 +1410,7 @@ async function main() {
} else {
document.getElementById("progress").style.display = "none";
}
- fps.innerText = Math.round(avgFps) + " fps";
+ fps.innerText = Math.round(avgFps) + " fps | stereo=" + (stereoEnabled ? eyeOffset.toFixed(3) : "off");
if (isNaN(currentCameraIndex)) {
camid.innerText = "";
}