Merge pull request #2 from forge-gfx/multiview

Tweak multiple viewports example
This commit is contained in:
Diego Marcos
2025-05-25 23:07:14 -07:00
committed by GitHub
2 changed files with 67 additions and 20 deletions
+4
View File
@@ -86,5 +86,9 @@
"cat.spz": { "cat.spz": {
"url": "https://forge-gfx.github.io/assets/splats/cat.spz", "url": "https://forge-gfx.github.io/assets/splats/cat.spz",
"directory": "splats" "directory": "splats"
},
"arrow.glb": {
"url": "https://forge-gfx.github.io/assets/models/arrow.glb",
"directory": "models"
} }
} }
+63 -20
View File
@@ -1,10 +1,34 @@
<!DOCTYPE html> <!DOCTYPE html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Forge • Multiple Viewpoints</title> <title>Forge • Multiple Viewpoints</title>
<style> body {margin: 0;} </style> <style>
body {
margin: 0;
background-color: black;
}
header {
margin: 0 auto;
padding: 10px 0;
min-width: 400px;
width: 50%;
}
input {
width: 100%;
padding: 10px 0;
}
</style>
</head> </head>
<body> <body>
<header>
<input type="range" min="-100" max="100" id="camera1">
<input type="range" min="-100" max="100" id="camera2">
</header>
<script type="importmap"> <script type="importmap">
{ {
"imports": { "imports": {
@@ -16,25 +40,27 @@
<script type="module"> <script type="module">
import * as THREE from "three"; import * as THREE from "three";
import { ForgeRenderer, ForgeViewpoint, SplatMesh } from "@forge-gfx/forge"; import { ForgeRenderer, ForgeViewpoint, SplatMesh } from "@forge-gfx/forge";
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { getAssetFileURL } from "/examples/js/get-asset-url.js"; import { getAssetFileURL } from "/examples/js/get-asset-url.js";
const scene = new THREE.Scene(); const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000); scene.background = new THREE.Color(0x000000);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer(); const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight); const header_height = document.querySelector('header').getBoundingClientRect().height;
renderer.setSize(window.innerWidth, window.innerHeight - header_height);
document.body.appendChild(renderer.domElement) document.body.appendChild(renderer.domElement)
// Explicitly create a ForgeRenderer in the scene to spawn new viewpoints // Explicitly create a ForgeRenderer in the scene to spawn new viewpoints
const forge = new ForgeRenderer({ renderer }); const forge = new ForgeRenderer({ renderer });
const splatURL = await getAssetFileURL("butterfly.spz"); const splatURL = await getAssetFileURL("butterfly.spz");
const butterfly = new SplatMesh({ url: splatURL}); const butterfly = new SplatMesh({ url: splatURL });
butterfly.quaternion.set(1, 0, 0, 0); butterfly.quaternion.set(1, 0, 0, 0);
butterfly.position.set(0, 0, -1); butterfly.position.set(0, 0, -3);
scene.add(butterfly); scene.add(butterfly);
function createViewpoint(origin, screenPos) { async function createViewpoint(origin, screenPos, index) {
// Create a textured rectangle at the near plane to show the viewpoint // Create a textured rectangle at the near plane to show the viewpoint
const width = 320; const width = 320;
const height = 240; const height = 240;
@@ -49,15 +75,30 @@
viewCamera.position.copy(origin); viewCamera.position.copy(origin);
viewCamera.lookAt(butterfly.position); viewCamera.lookAt(butterfly.position);
// Create a cone to show the viewpoint camera // Load arrow object to show where each camera is
const cone = new THREE.Mesh( const arrowURL = await getAssetFileURL("arrow.glb");
new THREE.ConeGeometry(0.1, 0.1, 32), new GLTFLoader().load(arrowURL, gltf => {
new THREE.MeshNormalMaterial(), // Different material for each camera
); const arrowMaterial = new THREE.MeshMatcapMaterial({
cone.position.copy(viewCamera.position); matcap: [
cone.quaternion.copy(viewCamera.quaternion); new THREE.TextureLoader().load("https://raw.githubusercontent.com/nidorx/matcaps/master/128/1A2461_3D70DB_2C3C8F_2C6CAC-128px.png"),
cone.rotateX(Math.PI / 2); new THREE.TextureLoader().load("https://raw.githubusercontent.com/nidorx/matcaps/master/128/9D282A_38191D_DFC6CD_D6495A-128px.png")
scene.add(cone); ][index]
});
const arrow = gltf.scene.children[0];
arrow.material = arrowMaterial;
arrow.position.copy(viewCamera.position);
arrow.lookAt(butterfly.position);
scene.add(arrow);
// Connect slider to both camera and arrow
document.getElementById("camera" + (index + 1)).oninput = ev => {
viewCamera.position.x = (ev.target.value / 100);
arrow.position.x = viewCamera.position.x;
arrow.lookAt(butterfly.position);
};
});
// Spawn a new viewpoint that auto-updates its sort order, double // Spawn a new viewpoint that auto-updates its sort order, double
// buffered in case the texture is used recursively in the scene // buffered in case the texture is used recursively in the scene
@@ -70,12 +111,13 @@
screen.material.map = texture; screen.material.map = texture;
}, },
}); });
return viewpoint; return viewpoint;
} }
const viewpoints = [ const viewpoints = [
createViewpoint(new THREE.Vector3(0, 0.5, -1), new THREE.Vector2(-0.5, 0.5)), await createViewpoint(new THREE.Vector3(0, 1.5, -3), new THREE.Vector2(-0.5, 0.5), 0),
createViewpoint(new THREE.Vector3(0.5, -0.5, -1), new THREE.Vector2(-0.5, -0.5)), await createViewpoint(new THREE.Vector3(0.5, -1, -3), new THREE.Vector2(0.5, -0.5), 1),
]; ];
renderer.setAnimationLoop(function animate(time) { renderer.setAnimationLoop(function animate(time) {
@@ -83,12 +125,13 @@
renderer.render(scene, camera); renderer.render(scene, camera);
butterfly.rotation.y += 0.01; butterfly.rotation.y += 0.01;
// Adjust the background for visibility and render the viewpoints // Render each viewpoint (with a different background each)
scene.background.set(0x000040); for (let i = 0; i < viewpoints.length; i++) {
for (const viewpoint of viewpoints) { scene.background.set([0x202040, 0x302020][i]);
viewpoint.renderTarget({ scene, camera: viewpoint.camera }); viewpoints[i].renderTarget({ scene, camera: viewpoints[i].camera });
} }
}); });
</script> </script>
</body> </body>
</html> </html>