|
|
@@ -26,7 +26,7 @@
|
|
|
<script type="module">
|
|
|
|
|
|
import * as THREE from 'three';
|
|
|
- import { color } from 'three/tsl';
|
|
|
+ import { color, storage, Fn, instanceIndex, sin, timerLocal, float, uniform, attribute, mix, vec3 } from 'three/tsl';
|
|
|
|
|
|
import Stats from 'three/addons/libs/stats.module.js';
|
|
|
|
|
|
@@ -42,11 +42,15 @@
|
|
|
let material;
|
|
|
let stats;
|
|
|
let gui;
|
|
|
+ let effectController;
|
|
|
|
|
|
// viewport
|
|
|
let insetWidth;
|
|
|
let insetHeight;
|
|
|
|
|
|
+ // compute
|
|
|
+ let computeSize;
|
|
|
+
|
|
|
init();
|
|
|
|
|
|
function init() {
|
|
|
@@ -72,10 +76,16 @@
|
|
|
|
|
|
backgroundNode = color( 0x222222 );
|
|
|
|
|
|
- // Position and THREE.Color Data
|
|
|
+ effectController = {
|
|
|
|
|
|
- const positions = [];
|
|
|
- const colors = [];
|
|
|
+ pulseSpeed: uniform( 6 ),
|
|
|
+ minWidth: uniform( 6 ),
|
|
|
+ maxWidth: uniform( 12 ),
|
|
|
+ alphaToCoverage: true,
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ // Position and THREE.Color Data
|
|
|
|
|
|
const points = GeometryUtils.hilbert3D( new THREE.Vector3( 0, 0, 0 ), 20.0, 1, 0, 1, 2, 3, 4, 5, 6, 7 );
|
|
|
|
|
|
@@ -84,6 +94,10 @@
|
|
|
const point = new THREE.Vector3();
|
|
|
const pointColor = new THREE.Color();
|
|
|
|
|
|
+ const positions = [];
|
|
|
+ const colors = [];
|
|
|
+ const sizes = new Float32Array( divisions );
|
|
|
+
|
|
|
for ( let i = 0, l = divisions; i < l; i ++ ) {
|
|
|
|
|
|
const t = i / l;
|
|
|
@@ -94,6 +108,8 @@
|
|
|
pointColor.setHSL( t, 1.0, 0.5, THREE.SRGBColorSpace );
|
|
|
colors.push( pointColor.r, pointColor.g, pointColor.b );
|
|
|
|
|
|
+ sizes[ i ] = 10.0;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
// Instanced Points
|
|
|
@@ -102,31 +118,59 @@
|
|
|
geometry.setPositions( positions );
|
|
|
geometry.setColors( colors );
|
|
|
|
|
|
+ const instanceSizeBufferAttribute = new THREE.StorageInstancedBufferAttribute( sizes, 1 );
|
|
|
+ geometry.setAttribute( 'instanceSize', instanceSizeBufferAttribute );
|
|
|
+ const instanceSizeStorage = storage( instanceSizeBufferAttribute, 'float', instanceSizeBufferAttribute.count );
|
|
|
+
|
|
|
+ computeSize = Fn( () => {
|
|
|
+
|
|
|
+ const { pulseSpeed, minWidth, maxWidth } = effectController;
|
|
|
+
|
|
|
+ const time = timerLocal().add( float( instanceIndex ) );
|
|
|
+
|
|
|
+ const sizeFactor = sin( time.mul( pulseSpeed ) ).add( 1 ).div( 2 );
|
|
|
+
|
|
|
+ instanceSizeStorage.element( instanceIndex ).assign( sizeFactor.mul( maxWidth.sub( minWidth ) ).add( minWidth ) );
|
|
|
+
|
|
|
+ } )().compute( divisions );
|
|
|
+
|
|
|
geometry.instanceCount = positions.length / 3; // this should not be necessary
|
|
|
|
|
|
material = new THREE.InstancedPointsNodeMaterial( {
|
|
|
|
|
|
color: 0xffffff,
|
|
|
pointWidth: 10, // in pixel units
|
|
|
-
|
|
|
vertexColors: true,
|
|
|
alphaToCoverage: true,
|
|
|
|
|
|
} );
|
|
|
|
|
|
+ const attributeRange = attribute( 'instanceSize' ).sub( 1 );
|
|
|
+
|
|
|
+ material.pointWidthNode = attribute( 'instanceSize' );
|
|
|
+ material.pointColorNode = mix( vec3( 0.0 ), attribute( 'instanceColor' ), attributeRange.div( float( effectController.maxWidth.sub( 1 ) ) ) );
|
|
|
+
|
|
|
const instancedPoints = new InstancedPoints( geometry, material );
|
|
|
instancedPoints.scale.set( 1, 1, 1 );
|
|
|
scene.add( instancedPoints );
|
|
|
|
|
|
- //
|
|
|
-
|
|
|
window.addEventListener( 'resize', onWindowResize );
|
|
|
onWindowResize();
|
|
|
|
|
|
stats = new Stats();
|
|
|
document.body.appendChild( stats.dom );
|
|
|
|
|
|
- initGui();
|
|
|
+ gui = new GUI();
|
|
|
+
|
|
|
+ gui.add( effectController, 'alphaToCoverage' ).onChange( function ( val ) {
|
|
|
+
|
|
|
+ material.alphaToCoverage = val;
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ gui.add( effectController.minWidth, 'value', 1, 20, 1 ).name( 'minWidth' );
|
|
|
+ gui.add( effectController.maxWidth, 'value', 2, 20, 1 ).name( 'maxWidth' );
|
|
|
+ gui.add( effectController.pulseSpeed, 'value', 1, 20, 0.1 ).name( 'pulseSpeed' );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -149,6 +193,9 @@
|
|
|
|
|
|
stats.update();
|
|
|
|
|
|
+ // compute
|
|
|
+ renderer.compute( computeSize );
|
|
|
+
|
|
|
// main scene
|
|
|
|
|
|
renderer.setViewport( 0, 0, window.innerWidth, window.innerHeight );
|
|
|
@@ -189,29 +236,6 @@
|
|
|
|
|
|
//
|
|
|
|
|
|
- function initGui() {
|
|
|
-
|
|
|
- gui = new GUI();
|
|
|
-
|
|
|
- const param = {
|
|
|
- 'width': 10,
|
|
|
- 'alphaToCoverage': true,
|
|
|
- };
|
|
|
-
|
|
|
- gui.add( param, 'width', 1, 20, 1 ).onChange( function ( val ) {
|
|
|
-
|
|
|
- material.pointWidth = val;
|
|
|
-
|
|
|
- } );
|
|
|
-
|
|
|
- gui.add( param, 'alphaToCoverage' ).onChange( function ( val ) {
|
|
|
-
|
|
|
- material.alphaToCoverage = val;
|
|
|
-
|
|
|
- } );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
</script>
|
|
|
|
|
|
</body>
|