| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 | <!DOCTYPE html><html lang="en">	<head>		<title>three.js webgl - custom attributes [particles][billboards]</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">	</head>	<body>		<div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - custom attributes example - particles - billboards</div>		<div id="container"></div>		<script type="x-shader/x-vertex" id="vertexshader">			attribute float size;			attribute vec3 ca;			varying vec3 vColor;			void main() {				vColor = ca;				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );				gl_PointSize = size * ( 300.0 / -mvPosition.z );				gl_Position = projectionMatrix * mvPosition;			}		</script>		<script type="x-shader/x-fragment" id="fragmentshader">			uniform vec3 color;			uniform sampler2D pointTexture;			varying vec3 vColor;			void main() {				vec4 color = vec4( color * vColor, 1.0 ) * texture2D( pointTexture, gl_PointCoord );				gl_FragColor = color;			}		</script>		<!-- Import maps polyfill -->		<!-- Remove this when import maps will be widely supported -->		<script async src="https://unpkg.com/[email protected]/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 Stats from 'three/addons/libs/stats.module.js';			import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';			let renderer, scene, camera, stats;			let sphere, length1;			const WIDTH = window.innerWidth;			const HEIGHT = window.innerHeight;			init();			animate();			function init() {				camera = new THREE.PerspectiveCamera( 45, WIDTH / HEIGHT, 1, 10000 );				camera.position.z = 300;				scene = new THREE.Scene();				const radius = 100, segments = 68, rings = 38;				let sphereGeometry = new THREE.SphereGeometry( radius, segments, rings );				let boxGeometry = new THREE.BoxGeometry( 0.8 * radius, 0.8 * radius, 0.8 * radius, 10, 10, 10 );				// if normal and uv attributes are not removed, mergeVertices() can't consolidate identical vertices with different normal/uv data				sphereGeometry.deleteAttribute( 'normal' );				sphereGeometry.deleteAttribute( 'uv' );				boxGeometry.deleteAttribute( 'normal' );				boxGeometry.deleteAttribute( 'uv' );				sphereGeometry = BufferGeometryUtils.mergeVertices( sphereGeometry );				boxGeometry = BufferGeometryUtils.mergeVertices( boxGeometry );				const combinedGeometry = BufferGeometryUtils.mergeBufferGeometries( [ sphereGeometry, boxGeometry ] );				const positionAttribute = combinedGeometry.getAttribute( 'position' );				const colors = [];				const sizes = [];				const color = new THREE.Color();				const vertex = new THREE.Vector3();				length1 = sphereGeometry.getAttribute( 'position' ).count;				for ( let i = 0, l = positionAttribute.count; i < l; i ++ ) {					vertex.fromBufferAttribute( positionAttribute, i );					if ( i < length1 ) {						color.setHSL( 0.01 + 0.1 * ( i / length1 ), 0.99, ( vertex.y + radius ) / ( 4 * radius ) );					} else {						color.setHSL( 0.6, 0.75, 0.25 + vertex.y / ( 2 * radius ) );					}					color.toArray( colors, i * 3 );					sizes[ i ] = i < length1 ? 10 : 40;				}				const geometry = new THREE.BufferGeometry();				geometry.setAttribute( 'position', positionAttribute );				geometry.setAttribute( 'size', new THREE.Float32BufferAttribute( sizes, 1 ) );				geometry.setAttribute( 'ca', new THREE.Float32BufferAttribute( colors, 3 ) );				//				const texture = new THREE.TextureLoader().load( 'textures/sprites/disc.png' );				texture.wrapS = THREE.RepeatWrapping;				texture.wrapT = THREE.RepeatWrapping;				const material = new THREE.ShaderMaterial( {					uniforms: {						color: { value: new THREE.Color( 0xffffff ) },						pointTexture: { value: texture }					},					vertexShader: document.getElementById( 'vertexshader' ).textContent,					fragmentShader: document.getElementById( 'fragmentshader' ).textContent,					transparent: true				} );				//				sphere = new THREE.Points( geometry, material );				scene.add( sphere );				//				renderer = new THREE.WebGLRenderer();				renderer.setPixelRatio( window.devicePixelRatio );				renderer.setSize( WIDTH, HEIGHT );				const container = document.getElementById( 'container' );				container.appendChild( renderer.domElement );				stats = new Stats();				container.appendChild( stats.dom );				//				window.addEventListener( 'resize', onWindowResize );			}			function onWindowResize() {				camera.aspect = window.innerWidth / window.innerHeight;				camera.updateProjectionMatrix();				renderer.setSize( window.innerWidth, window.innerHeight );			}			function sortPoints() {				const vector = new THREE.Vector3();				// Model View Projection matrix				const matrix = new THREE.Matrix4();				matrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );				matrix.multiply( sphere.matrixWorld );				//				const geometry = sphere.geometry;				let index = geometry.getIndex();				const positions = geometry.getAttribute( 'position' ).array;				const length = positions.length / 3;				if ( index === null ) {					const array = new Uint16Array( length );					for ( let i = 0; i < length; i ++ ) {						array[ i ] = i;					}					index = new THREE.BufferAttribute( array, 1 );					geometry.setIndex( index );				}				const sortArray = [];				for ( let i = 0; i < length; i ++ ) {					vector.fromArray( positions, i * 3 );					vector.applyMatrix4( matrix );					sortArray.push( [ vector.z, i ] );				}				function numericalSort( a, b ) {					return b[ 0 ] - a[ 0 ];				}				sortArray.sort( numericalSort );				const indices = index.array;				for ( let i = 0; i < length; i ++ ) {					indices[ i ] = sortArray[ i ][ 1 ];				}				geometry.index.needsUpdate = true;			}			function animate() {				requestAnimationFrame( animate );				render();				stats.update();			}			function render() {				const time = Date.now() * 0.005;				sphere.rotation.y = 0.02 * time;				sphere.rotation.z = 0.02 * time;				const geometry = sphere.geometry;				const attributes = geometry.attributes;				for ( let i = 0; i < attributes.size.array.length; i ++ ) {					if ( i < length1 ) {						attributes.size.array[ i ] = 16 + 12 * Math.sin( 0.1 * i + time );					}				}				attributes.size.needsUpdate = true;				sortPoints();				renderer.render( scene, camera );			}		</script></body></html>
 |