You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
654 lines
14 KiB
654 lines
14 KiB
2 years ago
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
<title>three.js webgl - multiple elements with text</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;
|
||
|
margin: auto;
|
||
|
padding: .5in;
|
||
|
max-width: 7in;
|
||
|
text-align: justify;
|
||
|
}
|
||
|
|
||
|
a {
|
||
|
color: #08f;
|
||
|
}
|
||
|
|
||
|
#info {
|
||
|
left: 0px;
|
||
|
}
|
||
|
|
||
|
.view {
|
||
|
width: 5in;
|
||
|
height: 5in;
|
||
|
margin: auto;
|
||
|
}
|
||
|
|
||
|
#c {
|
||
|
position: fixed;
|
||
|
left: 0px; top: 0px;
|
||
|
width: 100%;
|
||
|
height: 100%;
|
||
|
background-color: #fff;
|
||
|
z-index: -1;
|
||
|
}
|
||
|
|
||
|
.math {
|
||
|
text-align: center;
|
||
|
}
|
||
|
|
||
|
.math-frac {
|
||
|
display: inline-block;
|
||
|
vertical-align: middle;
|
||
|
}
|
||
|
|
||
|
.math-num {
|
||
|
display: block;
|
||
|
}
|
||
|
|
||
|
.math-denom {
|
||
|
display: block;
|
||
|
border-top: 1px solid;
|
||
|
}
|
||
|
|
||
|
.math-sqrt {
|
||
|
display: inline-block;
|
||
|
transform: scale(1, 1.3);
|
||
|
}
|
||
|
|
||
|
.math-sqrt-stem {
|
||
|
display: inline-block;
|
||
|
border-top: 1px solid;
|
||
|
margin-top: 5px;
|
||
|
}
|
||
|
</style>
|
||
|
</head>
|
||
|
<body>
|
||
|
|
||
|
<canvas id="c"></canvas>
|
||
|
|
||
|
<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - multiple elements with text - webgl</div>
|
||
|
|
||
|
<!-- Import maps polyfill -->
|
||
|
<!-- Remove this when import maps will be widely supported -->
|
||
|
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
|
||
|
|
||
|
<script type="importmap">
|
||
|
{
|
||
|
"imports": {
|
||
|
"three": "../build/three.module.js",
|
||
|
"three/addons/": "./jsm/"
|
||
|
}
|
||
|
}
|
||
|
</script>
|
||
|
|
||
|
<script type="module">
|
||
|
|
||
|
import * as THREE from 'three';
|
||
|
|
||
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||
|
|
||
|
const scenes = [];
|
||
|
|
||
|
const clock = new THREE.Clock();
|
||
|
|
||
|
let views, t, canvas, renderer;
|
||
|
|
||
|
window.onload = init;
|
||
|
|
||
|
function init() {
|
||
|
|
||
|
const balls = 20;
|
||
|
const size = .25;
|
||
|
|
||
|
const colors = [
|
||
|
'rgb(0,127,255)', 'rgb(255,0,0)', 'rgb(0,255,0)', 'rgb(0,255,255)',
|
||
|
'rgb(255,0,255)', 'rgb(255,0,127)', 'rgb(255,255,0)', 'rgb(0,255,127)'
|
||
|
];
|
||
|
|
||
|
canvas = document.getElementById( 'c' );
|
||
|
|
||
|
renderer = new THREE.WebGLRenderer( { canvas: canvas, antialias: true } );
|
||
|
renderer.setPixelRatio( window.devicePixelRatio );
|
||
|
|
||
|
views = document.querySelectorAll( '.view' );
|
||
|
|
||
|
for ( let n = 0; n < views.length; n ++ ) {
|
||
|
|
||
|
const scene = new THREE.Scene();
|
||
|
scene.background = new THREE.Color( 0xffffff );
|
||
|
|
||
|
const geometry0 = new THREE.BufferGeometry();
|
||
|
const geometry1 = new THREE.BufferGeometry();
|
||
|
|
||
|
const vertices = [];
|
||
|
|
||
|
if ( views[ n ].lattice ) {
|
||
|
|
||
|
const range = balls / 2;
|
||
|
for ( let i = - range; i <= range; i ++ ) {
|
||
|
|
||
|
for ( let j = - range; j <= range; j ++ ) {
|
||
|
|
||
|
for ( let k = - range; k <= range; k ++ ) {
|
||
|
|
||
|
vertices.push( i, j, k );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
for ( let m = 0; m < Math.pow( balls, 3 ); m ++ ) {
|
||
|
|
||
|
const i = balls * Math.random() - balls / 2;
|
||
|
const j = balls * Math.random() - balls / 2;
|
||
|
const k = balls * Math.random() - balls / 2;
|
||
|
|
||
|
vertices.push( i, j, k );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
geometry0.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
|
||
|
geometry1.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices.slice(), 3 ) );
|
||
|
|
||
|
const index = Math.floor( colors.length * Math.random() );
|
||
|
|
||
|
const canvas2 = document.createElement( 'canvas' );
|
||
|
canvas2.width = 128;
|
||
|
canvas2.height = 128;
|
||
|
const context = canvas2.getContext( '2d' );
|
||
|
context.arc( 64, 64, 64, 0, 2 * Math.PI );
|
||
|
context.fillStyle = colors[ index ];
|
||
|
context.fill();
|
||
|
const texture = new THREE.CanvasTexture( canvas2 );
|
||
|
|
||
|
const material = new THREE.PointsMaterial( { size: size, map: texture, transparent: true, alphaTest: 0.1 } );
|
||
|
|
||
|
scene.add( new THREE.Points( geometry0, material ) );
|
||
|
|
||
|
scene.userData.view = views[ n ];
|
||
|
scene.userData.geometry1 = geometry1;
|
||
|
|
||
|
const camera = new THREE.PerspectiveCamera( 75, 1, 0.1, 100 );
|
||
|
camera.position.set( 0, 0, 1.2 * balls );
|
||
|
scene.userData.camera = camera;
|
||
|
|
||
|
const controls = new OrbitControls( camera, views[ n ] );
|
||
|
scene.userData.controls = controls;
|
||
|
|
||
|
scenes.push( scene );
|
||
|
|
||
|
}
|
||
|
|
||
|
t = 0;
|
||
|
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() {
|
||
|
|
||
|
render();
|
||
|
requestAnimationFrame( animate );
|
||
|
|
||
|
}
|
||
|
|
||
|
function render() {
|
||
|
|
||
|
updateSize();
|
||
|
|
||
|
renderer.setClearColor( 0xffffff );
|
||
|
renderer.setScissorTest( false );
|
||
|
renderer.clear();
|
||
|
|
||
|
renderer.setClearColor( 0x000000 );
|
||
|
renderer.setScissorTest( true );
|
||
|
|
||
|
scenes.forEach( function ( scene ) {
|
||
|
|
||
|
const rect = scene.userData.view.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 bottom = renderer.domElement.clientHeight - rect.bottom;
|
||
|
|
||
|
renderer.setViewport( left, bottom, width, height );
|
||
|
renderer.setScissor( left, bottom, width, height );
|
||
|
|
||
|
renderer.render( scene, scene.userData.camera );
|
||
|
|
||
|
const points = scene.children[ 0 ];
|
||
|
const position = points.geometry.attributes.position;
|
||
|
|
||
|
const point = new THREE.Vector3();
|
||
|
const offset = new THREE.Vector3();
|
||
|
|
||
|
for ( let i = 0; i < position.count; i ++ ) {
|
||
|
|
||
|
point.fromBufferAttribute( scene.userData.geometry1.attributes.position, i );
|
||
|
|
||
|
scene.userData.view.displacement( point.x, point.y, point.z, t / 5, offset );
|
||
|
|
||
|
position.setXYZ( i, point.x + offset.x, point.y + offset.y, point.z + offset.z );
|
||
|
|
||
|
}
|
||
|
|
||
|
position.needsUpdate = true;
|
||
|
|
||
|
} );
|
||
|
|
||
|
t += clock.getDelta() * 60;
|
||
|
|
||
|
}
|
||
|
|
||
|
</script>
|
||
|
|
||
|
<p>Sound waves whose geometry is determined by a single dimension, plane waves, obey the wave equation</p>
|
||
|
|
||
|
<!-- css math formatting inspired by http://mathquill.com/mathquill/mathquill.css -->
|
||
|
|
||
|
<div class="math">
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
∂<sup>2</sup><i>u</i>
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
∂<i>r</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
−
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
1<sup></sup> <!-- sup for vertical alignment -->
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
<i>c</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
∂<sup>2</sup><i>u</i>
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
∂<i>t</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
= 0
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>where <i>c</i> designates the speed of sound in the medium. The monochromatic solution for plane waves will be taken to be</p>
|
||
|
|
||
|
<div class="math">
|
||
|
|
||
|
<i>u</i>(<i>r</i>,<i>t</i>) = sin(<i>k</i><i>r</i> ± ω<i>t</i>)
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>where ω is the frequency and <i>k</i>=ω/<i>c</i> is the wave number. The sign chosen in the argument determines the direction of movement of the waves.</p>
|
||
|
|
||
|
<p>Here is a plane wave moving on a three-dimensional lattice of atoms:</p>
|
||
|
|
||
|
<div class="view">
|
||
|
|
||
|
<script>
|
||
|
|
||
|
/* eslint-disable prefer-const*/
|
||
|
let parent = document.scripts[ document.scripts.length - 1 ].parentNode;
|
||
|
|
||
|
parent.displacement = function ( x, y, z, t, target ) {
|
||
|
|
||
|
return target.set( Math.sin( x - t ), 0, 0 );
|
||
|
|
||
|
};
|
||
|
|
||
|
parent.lattice = true;
|
||
|
|
||
|
</script>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>Here is a plane wave moving through a three-dimensional random distribution of molecules:</p>
|
||
|
|
||
|
<div class="view">
|
||
|
|
||
|
<script>
|
||
|
|
||
|
parent = document.scripts[ document.scripts.length - 1 ].parentNode;
|
||
|
|
||
|
parent.displacement = function ( x, y, z, t, target ) {
|
||
|
|
||
|
return target.set( Math.sin( x - t ), 0, 0 );
|
||
|
|
||
|
};
|
||
|
|
||
|
parent.lattice = false;
|
||
|
|
||
|
</script>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>Sound waves whose geometry is determined by two dimensions, cylindrical waves, obey the wave equation</p>
|
||
|
|
||
|
<div class="math">
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
∂<sup>2</sup><i>u</i>
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
∂<i>r</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
+
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
1
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
<i>r</i>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
∂<i>u</i>
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
∂<i>r</i>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
−
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
1<sup></sup> <!-- sup for vertical alignment -->
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
<i>c</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
∂<sup>2</sup><i>u</i>
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
∂<i>t</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
= 0
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>The monochromatic solution for cylindrical sound waves will be taken to be</p>
|
||
|
|
||
|
<div class="math">
|
||
|
|
||
|
<i>u</i>(<i>r</i>,<i>t</i>) =
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
sin(<i>k</i><i>r</i> ± ω<i>t</i>)
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
<span class="math-sqrt">√</span><span class="math-sqrt-stem"><i>r</i></span>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>Here is a cylindrical wave moving on a three-dimensional lattice of atoms:</p>
|
||
|
|
||
|
<div class="view">
|
||
|
|
||
|
<script>
|
||
|
|
||
|
parent = document.scripts[ document.scripts.length - 1 ].parentNode;
|
||
|
|
||
|
parent.displacement = function ( x, y, z, t, target ) {
|
||
|
|
||
|
if ( x * x + y * y < 0.01 ) {
|
||
|
|
||
|
return target.set( 0, 0, 0 );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const rho = Math.sqrt( x * x + y * y );
|
||
|
const phi = Math.atan2( y, x );
|
||
|
|
||
|
return target.set( 1.5 * Math.cos( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 1.5 * Math.sin( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 0 );
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
parent.lattice = true;
|
||
|
|
||
|
</script>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>Here is a cylindrical wave moving through a three-dimensional random distribution of molecules:</p>
|
||
|
|
||
|
<div class="view">
|
||
|
|
||
|
<script>
|
||
|
|
||
|
parent = document.scripts[ document.scripts.length - 1 ].parentNode;
|
||
|
|
||
|
parent.displacement = function ( x, y, z, t, target ) {
|
||
|
|
||
|
if ( x * x + y * y < 0.01 ) {
|
||
|
|
||
|
return target.set( 0, 0, 0 );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const rho = Math.sqrt( x * x + y * y );
|
||
|
const phi = Math.atan2( y, x );
|
||
|
|
||
|
return target.set( 1.5 * Math.cos( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 1.5 * Math.sin( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 0 );
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
parent.lattice = false;
|
||
|
|
||
|
</script>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>Sound waves whose geometry is determined by three dimensions, spherical waves, obey the wave equation</p>
|
||
|
|
||
|
<div class="math">
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
∂<sup>2</sup><i>u</i>
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
∂<i>r</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
+
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
2
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
<i>r</i>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
∂<i>u</i>
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
∂<i>r</i>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
−
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
1<sup></sup> <!-- sup for vertical alignment -->
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
<i>c</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
∂<sup>2</sup><i>u</i>
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
∂<i>t</i><sup>2</sup>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
= 0
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>The monochromatic solution for spherical sound waves will be taken to be</p>
|
||
|
|
||
|
<div class="math">
|
||
|
|
||
|
<i>u</i>(<i>r</i>,<i>t</i>) =
|
||
|
|
||
|
<span class="math-frac">
|
||
|
<span class="math-num">
|
||
|
sin(<i>k</i><i>r</i> ± ω<i>t</i>)
|
||
|
</span>
|
||
|
<span class="math-denom">
|
||
|
<i>r</i>
|
||
|
</span>
|
||
|
</span>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>Here is a spherical wave moving on a three-dimensional lattice of atoms:</p>
|
||
|
|
||
|
<div class="view">
|
||
|
|
||
|
<script>
|
||
|
|
||
|
parent = document.scripts[ document.scripts.length - 1 ].parentNode;
|
||
|
|
||
|
parent.displacement = function ( x, y, z, t, target ) {
|
||
|
|
||
|
if ( x * x + y * y + z * z < 0.01 ) {
|
||
|
|
||
|
return target.set( 0, 0, 0 );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const r = Math.sqrt( x * x + y * y + z * z );
|
||
|
const theta = Math.acos( z / r );
|
||
|
const phi = Math.atan2( y, x );
|
||
|
|
||
|
return target.set( 3 * Math.cos( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r, 3 * Math.sin( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r, 3 * Math.cos( theta ) * Math.sin( r - t ) / r );
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
parent.lattice = true;
|
||
|
|
||
|
</script>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>Here is a spherical wave moving through a three-dimensional random distribution of molecules:</p>
|
||
|
|
||
|
<div class="view">
|
||
|
|
||
|
<script>
|
||
|
|
||
|
parent = document.scripts[ document.scripts.length - 1 ].parentNode;
|
||
|
|
||
|
parent.displacement = function ( x, y, z, t, target ) {
|
||
|
|
||
|
if ( x * x + y * y + z * z < 0.01 ) {
|
||
|
|
||
|
return target.set( 0, 0, 0 );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
const r = Math.sqrt( x * x + y * y + z * z );
|
||
|
const theta = Math.acos( z / r );
|
||
|
const phi = Math.atan2( y, x );
|
||
|
|
||
|
return target.set( 3 * Math.cos( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r, 3 * Math.sin( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r, 3 * Math.cos( theta ) * Math.sin( r - t ) / r );
|
||
|
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
parent.lattice = false;
|
||
|
|
||
|
</script>
|
||
|
|
||
|
</div>
|
||
|
|
||
|
<p>The mathematical description of sound waves can be carried to higher dimensions, but one needs to wait for Four.js and its higher-dimensional successors to attempt visualizations.</p>
|
||
|
|
||
|
</body>
|
||
|
</html>
|