|
|
@@ -0,0 +1,240 @@
|
|
|
+<!DOCTYPE html>
|
|
|
+<html lang="en">
|
|
|
+ <head>
|
|
|
+ <title>three.js webgpu - multiple elements</title>
|
|
|
+ <meta charset="utf-8">
|
|
|
+ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
|
+ <link type="text/css" rel="stylesheet" href="main.css">
|
|
|
+ <style>
|
|
|
+
|
|
|
+ * {
|
|
|
+ box-sizing: border-box;
|
|
|
+ -moz-box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ body {
|
|
|
+ background-color: #fff;
|
|
|
+ color: #444;
|
|
|
+ }
|
|
|
+
|
|
|
+ a {
|
|
|
+ color: #08f;
|
|
|
+ }
|
|
|
+
|
|
|
+ #content {
|
|
|
+ position: absolute;
|
|
|
+ top: 0; width: 100%;
|
|
|
+ z-index: 1;
|
|
|
+ padding: 3em 0 0 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ #c {
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .list-item {
|
|
|
+ display: inline-block;
|
|
|
+ margin: 1em;
|
|
|
+ padding: 1em;
|
|
|
+ box-shadow: 1px 2px 4px 0px rgba(0,0,0,0.25);
|
|
|
+ }
|
|
|
+
|
|
|
+ .list-item > div:nth-child(1) {
|
|
|
+ width: 200px;
|
|
|
+ height: 200px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .list-item > div:nth-child(2) {
|
|
|
+ color: #888;
|
|
|
+ font-family: sans-serif;
|
|
|
+ font-size: large;
|
|
|
+ width: 200px;
|
|
|
+ margin-top: 0.5em;
|
|
|
+ }
|
|
|
+
|
|
|
+ </style>
|
|
|
+ </head>
|
|
|
+ <body>
|
|
|
+
|
|
|
+ <canvas id="c"></canvas>
|
|
|
+
|
|
|
+ <div id="content">
|
|
|
+ <div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - multiple elements</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <script type="importmap">
|
|
|
+ {
|
|
|
+ "imports": {
|
|
|
+ "three": "../build/three.webgpu.js",
|
|
|
+ "three/webgpu": "../build/three.webgpu.js",
|
|
|
+ "three/tsl": "../build/three.tsl.js",
|
|
|
+ "three/addons/": "./jsm/"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </script>
|
|
|
+
|
|
|
+ <script type="module">
|
|
|
+
|
|
|
+ import * as THREE from 'three';
|
|
|
+ import { color } from 'three/tsl';
|
|
|
+
|
|
|
+ import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
+
|
|
|
+ let canvas, renderer;
|
|
|
+
|
|
|
+ const scenes = [];
|
|
|
+
|
|
|
+ init();
|
|
|
+
|
|
|
+ function init() {
|
|
|
+
|
|
|
+ canvas = document.getElementById( 'c' );
|
|
|
+
|
|
|
+ const geometries = [
|
|
|
+ new THREE.BoxGeometry( 1, 1, 1 ),
|
|
|
+ new THREE.SphereGeometry( 0.5, 12, 8 ),
|
|
|
+ new THREE.DodecahedronGeometry( 0.5 ),
|
|
|
+ new THREE.CylinderGeometry( 0.5, 0.5, 1, 12 )
|
|
|
+ ];
|
|
|
+
|
|
|
+ const content = document.getElementById( 'content' );
|
|
|
+
|
|
|
+ for ( let i = 0; i < 40; i ++ ) {
|
|
|
+
|
|
|
+ const scene = new THREE.Scene();
|
|
|
+ scene.backgroundNode = color( 0xeeeeee );
|
|
|
+
|
|
|
+ // make a list item
|
|
|
+ const element = document.createElement( 'div' );
|
|
|
+ element.className = 'list-item';
|
|
|
+
|
|
|
+ const sceneElement = document.createElement( 'div' );
|
|
|
+ element.appendChild( sceneElement );
|
|
|
+
|
|
|
+ const descriptionElement = document.createElement( 'div' );
|
|
|
+ descriptionElement.innerText = 'Scene ' + ( i + 1 );
|
|
|
+ element.appendChild( descriptionElement );
|
|
|
+
|
|
|
+ // the element that represents the area we want to render the scene
|
|
|
+ scene.userData.element = sceneElement;
|
|
|
+ content.appendChild( element );
|
|
|
+
|
|
|
+ const camera = new THREE.PerspectiveCamera( 50, 1, 1, 10 );
|
|
|
+ camera.position.z = 2;
|
|
|
+ scene.userData.camera = camera;
|
|
|
+
|
|
|
+ const controls = new OrbitControls( scene.userData.camera, scene.userData.element );
|
|
|
+ controls.minDistance = 2;
|
|
|
+ controls.maxDistance = 5;
|
|
|
+ controls.enablePan = false;
|
|
|
+ controls.enableZoom = false;
|
|
|
+ scene.userData.controls = controls;
|
|
|
+
|
|
|
+ // add one random mesh to each scene
|
|
|
+ const geometry = geometries[ geometries.length * Math.random() | 0 ];
|
|
|
+
|
|
|
+ const material = new THREE.MeshStandardMaterial( {
|
|
|
+
|
|
|
+ color: new THREE.Color().setHSL( Math.random(), 1, 0.75, THREE.SRGBColorSpace ),
|
|
|
+ roughness: 0.5,
|
|
|
+ metalness: 0,
|
|
|
+ flatShading: true
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ scene.add( new THREE.Mesh( geometry, material ) );
|
|
|
+
|
|
|
+ scene.add( new THREE.HemisphereLight( 0xaaaaaa, 0x444444, 3 ) );
|
|
|
+
|
|
|
+ const light = new THREE.DirectionalLight( 0xffffff, 1.5 );
|
|
|
+ light.position.set( 1, 1, 1 );
|
|
|
+ scene.add( light );
|
|
|
+
|
|
|
+ scenes.push( scene );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ renderer = new THREE.WebGPURenderer( { canvas: canvas, antialias: true } );
|
|
|
+ renderer.setClearColor( 0xffffff, 1 );
|
|
|
+ renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
+ renderer.setAnimationLoop( animate );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function updateSize() {
|
|
|
+
|
|
|
+ const width = canvas.clientWidth;
|
|
|
+ const height = canvas.clientHeight;
|
|
|
+
|
|
|
+ if ( canvas.width !== width || canvas.height !== height ) {
|
|
|
+
|
|
|
+ renderer.setSize( width, height, false );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function animate() {
|
|
|
+
|
|
|
+ updateSize();
|
|
|
+
|
|
|
+ canvas.style.transform = `translateY(${window.scrollY}px)`;
|
|
|
+
|
|
|
+ renderer.setClearColor( 0xffffff );
|
|
|
+ renderer.setScissorTest( false );
|
|
|
+ renderer.setViewport( 0, 0, canvas.width, canvas.height );
|
|
|
+ renderer.clear();
|
|
|
+
|
|
|
+ //renderer.setClearColor( 0xe0e0e0 );
|
|
|
+ renderer.setScissorTest( true );
|
|
|
+
|
|
|
+ scenes.forEach( function ( scene ) {
|
|
|
+
|
|
|
+ // so something moves
|
|
|
+ scene.children[ 0 ].rotation.y = Date.now() * 0.001;
|
|
|
+
|
|
|
+ // get the element that is a place holder for where we want to
|
|
|
+ // draw the scene
|
|
|
+ const element = scene.userData.element;
|
|
|
+
|
|
|
+ // get its position relative to the page's viewport
|
|
|
+ const rect = element.getBoundingClientRect();
|
|
|
+
|
|
|
+ // check if it's offscreen. If so skip it
|
|
|
+ if ( rect.bottom < 0 || rect.top > renderer.domElement.clientHeight ||
|
|
|
+ rect.right < 0 || rect.left > renderer.domElement.clientWidth ) {
|
|
|
+
|
|
|
+ return; // it's off screen
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // set the viewport
|
|
|
+ const width = rect.right - rect.left;
|
|
|
+ const height = rect.bottom - rect.top;
|
|
|
+ const left = rect.left;
|
|
|
+ const top = rect.top;
|
|
|
+
|
|
|
+ renderer.setViewport( left, top, width, height );
|
|
|
+ renderer.setScissor( left, top, width, height );
|
|
|
+
|
|
|
+ const camera = scene.userData.camera;
|
|
|
+
|
|
|
+ //camera.aspect = width / height; // not changing in this example
|
|
|
+ //camera.updateProjectionMatrix();
|
|
|
+
|
|
|
+ //scene.userData.controls.update();
|
|
|
+
|
|
|
+ renderer.render( scene, camera );
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ </script>
|
|
|
+
|
|
|
+ </body>
|
|
|
+</html>
|