123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <title>three.js webgl - gpgpu - protoplanet</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>
- #warning {
- color: #ff0000;
- }
- </style>
- </head>
- <body>
- <div id="info">
- <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - <span id="protoplanets"></span> webgl gpgpu debris
- </div>
- <!-- Fragment shader for protoplanet's position -->
- <script id="computeShaderPosition" type="x-shader/x-fragment">
- #define delta ( 1.0 / 60.0 )
- void main() {
- vec2 uv = gl_FragCoord.xy / resolution.xy;
- vec4 tmpPos = texture2D( texturePosition, uv );
- vec3 pos = tmpPos.xyz;
- vec4 tmpVel = texture2D( textureVelocity, uv );
- vec3 vel = tmpVel.xyz;
- float mass = tmpVel.w;
- if ( mass == 0.0 ) {
- vel = vec3( 0.0 );
- }
- // Dynamics
- pos += vel * delta;
- gl_FragColor = vec4( pos, 1.0 );
- }
- </script>
- <!-- Fragment shader for protoplanet's velocity -->
- <script id="computeShaderVelocity" type="x-shader/x-fragment">
- // For PI declaration:
- #include <common>
- #define delta ( 1.0 / 60.0 )
- uniform float gravityConstant;
- uniform float density;
- const float width = resolution.x;
- const float height = resolution.y;
- float radiusFromMass( float mass ) {
- // Calculate radius of a sphere from mass and density
- return pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
- }
- void main() {
- vec2 uv = gl_FragCoord.xy / resolution.xy;
- float idParticle = uv.y * resolution.x + uv.x;
- vec4 tmpPos = texture2D( texturePosition, uv );
- vec3 pos = tmpPos.xyz;
- vec4 tmpVel = texture2D( textureVelocity, uv );
- vec3 vel = tmpVel.xyz;
- float mass = tmpVel.w;
- if ( mass > 0.0 ) {
- float radius = radiusFromMass( mass );
- vec3 acceleration = vec3( 0.0 );
- // Gravity interaction
- for ( float y = 0.0; y < height; y++ ) {
- for ( float x = 0.0; x < width; x++ ) {
- vec2 secondParticleCoords = vec2( x + 0.5, y + 0.5 ) / resolution.xy;
- vec3 pos2 = texture2D( texturePosition, secondParticleCoords ).xyz;
- vec4 velTemp2 = texture2D( textureVelocity, secondParticleCoords );
- vec3 vel2 = velTemp2.xyz;
- float mass2 = velTemp2.w;
- float idParticle2 = secondParticleCoords.y * resolution.x + secondParticleCoords.x;
- if ( idParticle == idParticle2 ) {
- continue;
- }
- if ( mass2 == 0.0 ) {
- continue;
- }
- vec3 dPos = pos2 - pos;
- float distance = length( dPos );
- float radius2 = radiusFromMass( mass2 );
- if ( distance == 0.0 ) {
- continue;
- }
- // Checks collision
- if ( distance < radius + radius2 ) {
- if ( idParticle < idParticle2 ) {
- // This particle is aggregated by the other
- vel = ( vel * mass + vel2 * mass2 ) / ( mass + mass2 );
- mass += mass2;
- radius = radiusFromMass( mass );
- }
- else {
- // This particle dies
- mass = 0.0;
- radius = 0.0;
- vel = vec3( 0.0 );
- break;
- }
- }
- float distanceSq = distance * distance;
- float gravityField = gravityConstant * mass2 / distanceSq;
- gravityField = min( gravityField, 1000.0 );
- acceleration += gravityField * normalize( dPos );
- }
- if ( mass == 0.0 ) {
- break;
- }
- }
- // Dynamics
- vel += delta * acceleration;
- }
- gl_FragColor = vec4( vel, mass );
- }
- </script>
- <!-- Particles vertex shader -->
- <script type="x-shader/x-vertex" id="particleVertexShader">
- // For PI declaration:
- #include <common>
- uniform sampler2D texturePosition;
- uniform sampler2D textureVelocity;
- uniform float cameraConstant;
- uniform float density;
- varying vec4 vColor;
- float radiusFromMass( float mass ) {
- // Calculate radius of a sphere from mass and density
- return pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
- }
- void main() {
- vec4 posTemp = texture2D( texturePosition, uv );
- vec3 pos = posTemp.xyz;
- vec4 velTemp = texture2D( textureVelocity, uv );
- vec3 vel = velTemp.xyz;
- float mass = velTemp.w;
- vColor = vec4( 1.0, mass / 250.0, 0.0, 1.0 );
- vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );
- // Calculate radius of a sphere from mass and density
- //float radius = pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
- float radius = radiusFromMass( mass );
- // Apparent size in pixels
- if ( mass == 0.0 ) {
- gl_PointSize = 0.0;
- }
- else {
- gl_PointSize = radius * cameraConstant / ( - mvPosition.z );
- }
- gl_Position = projectionMatrix * mvPosition;
- }
- </script>
- <!-- Particles fragment shader -->
- <script type="x-shader/x-fragment" id="particleFragmentShader">
- varying vec4 vColor;
- void main() {
- float f = length( gl_PointCoord - vec2( 0.5, 0.5 ) );
- if ( f > 0.5 ) {
- discard;
- }
- gl_FragColor = vColor;
- }
- </script>
- <script type="module">
- import * as THREE from '../build/three.module.js';
- import Stats from './jsm/libs/stats.module.js';
- import { GUI } from './jsm/libs/dat.gui.module.js';
- import { OrbitControls } from './jsm/controls/OrbitControls.js';
- import { GPUComputationRenderer } from './jsm/misc/GPUComputationRenderer.js';
- var isIE = /Trident/i.test( navigator.userAgent );
- var isEdge = /Edge/i.test( navigator.userAgent );
- // Texture width for simulation (each texel is a debris particle)
- var WIDTH = ( isIE || isEdge ) ? 4 : 64;
- var container, stats;
- var camera, scene, renderer, geometry;
- var PARTICLES = WIDTH * WIDTH;
- var gpuCompute;
- var velocityVariable;
- var positionVariable;
- var velocityUniforms;
- var particleUniforms;
- var effectController;
- init();
- animate();
- function init() {
- container = document.createElement( 'div' );
- document.body.appendChild( container );
- camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 5, 15000 );
- camera.position.y = 120;
- camera.position.z = 400;
- scene = new THREE.Scene();
- renderer = new THREE.WebGLRenderer();
- renderer.setPixelRatio( window.devicePixelRatio );
- renderer.setSize( window.innerWidth, window.innerHeight );
- container.appendChild( renderer.domElement );
- var controls = new OrbitControls( camera, renderer.domElement );
- effectController = {
- // Can be changed dynamically
- gravityConstant: 100.0,
- density: 0.45,
- // Must restart simulation
- radius: 300,
- height: 8,
- exponent: 0.4,
- maxMass: 15.0,
- velocity: 70,
- velocityExponent: 0.2,
- randVelocity: 0.001
- };
- initComputeRenderer();
- stats = new Stats();
- container.appendChild( stats.dom );
- window.addEventListener( 'resize', onWindowResize, false );
- initGUI();
- initProtoplanets();
- dynamicValuesChanger();
- }
- function initComputeRenderer() {
- gpuCompute = new GPUComputationRenderer( WIDTH, WIDTH, renderer );
- var dtPosition = gpuCompute.createTexture();
- var dtVelocity = gpuCompute.createTexture();
- fillTextures( dtPosition, dtVelocity );
- velocityVariable = gpuCompute.addVariable( "textureVelocity", document.getElementById( 'computeShaderVelocity' ).textContent, dtVelocity );
- positionVariable = gpuCompute.addVariable( "texturePosition", document.getElementById( 'computeShaderPosition' ).textContent, dtPosition );
- gpuCompute.setVariableDependencies( velocityVariable, [ positionVariable, velocityVariable ] );
- gpuCompute.setVariableDependencies( positionVariable, [ positionVariable, velocityVariable ] );
- velocityUniforms = velocityVariable.material.uniforms;
- velocityUniforms[ "gravityConstant" ] = { value: 0.0 };
- velocityUniforms[ "density" ] = { value: 0.0 };
- var error = gpuCompute.init();
- if ( error !== null ) {
- console.error( error );
- }
- }
- function restartSimulation() {
- var dtPosition = gpuCompute.createTexture();
- var dtVelocity = gpuCompute.createTexture();
- fillTextures( dtPosition, dtVelocity );
- gpuCompute.renderTexture( dtPosition, positionVariable.renderTargets[ 0 ] );
- gpuCompute.renderTexture( dtPosition, positionVariable.renderTargets[ 1 ] );
- gpuCompute.renderTexture( dtVelocity, velocityVariable.renderTargets[ 0 ] );
- gpuCompute.renderTexture( dtVelocity, velocityVariable.renderTargets[ 1 ] );
- }
- function initProtoplanets() {
- geometry = new THREE.BufferGeometry();
- var positions = new Float32Array( PARTICLES * 3 );
- var p = 0;
- for ( var i = 0; i < PARTICLES; i ++ ) {
- positions[ p ++ ] = ( Math.random() * 2 - 1 ) * effectController.radius;
- positions[ p ++ ] = 0; //( Math.random() * 2 - 1 ) * effectController.radius;
- positions[ p ++ ] = ( Math.random() * 2 - 1 ) * effectController.radius;
- }
- var uvs = new Float32Array( PARTICLES * 2 );
- p = 0;
- for ( var j = 0; j < WIDTH; j ++ ) {
- for ( var i = 0; i < WIDTH; i ++ ) {
- uvs[ p ++ ] = i / ( WIDTH - 1 );
- uvs[ p ++ ] = j / ( WIDTH - 1 );
- }
- }
- geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
- geometry.setAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
- particleUniforms = {
- "texturePosition": { value: null },
- "textureVelocity": { value: null },
- "cameraConstant": { value: getCameraConstant( camera ) },
- "density": { value: 0.0 }
- };
- // THREE.ShaderMaterial
- var material = new THREE.ShaderMaterial( {
- uniforms: particleUniforms,
- vertexShader: document.getElementById( 'particleVertexShader' ).textContent,
- fragmentShader: document.getElementById( 'particleFragmentShader' ).textContent
- } );
- material.extensions.drawBuffers = true;
- var particles = new THREE.Points( geometry, material );
- particles.matrixAutoUpdate = false;
- particles.updateMatrix();
- scene.add( particles );
- }
- function fillTextures( texturePosition, textureVelocity ) {
- var posArray = texturePosition.image.data;
- var velArray = textureVelocity.image.data;
- var radius = effectController.radius;
- var height = effectController.height;
- var exponent = effectController.exponent;
- var maxMass = effectController.maxMass * 1024 / PARTICLES;
- var maxVel = effectController.velocity;
- var velExponent = effectController.velocityExponent;
- var randVel = effectController.randVelocity;
- for ( var k = 0, kl = posArray.length; k < kl; k += 4 ) {
- // Position
- var x, y, z, rr;
- do {
- x = ( Math.random() * 2 - 1 );
- z = ( Math.random() * 2 - 1 );
- rr = x * x + z * z;
- } while ( rr > 1 );
- rr = Math.sqrt( rr );
- var rExp = radius * Math.pow( rr, exponent );
- // Velocity
- var vel = maxVel * Math.pow( rr, velExponent );
- var vx = vel * z + ( Math.random() * 2 - 1 ) * randVel;
- var vy = ( Math.random() * 2 - 1 ) * randVel * 0.05;
- var vz = - vel * x + ( Math.random() * 2 - 1 ) * randVel;
- x *= rExp;
- z *= rExp;
- y = ( Math.random() * 2 - 1 ) * height;
- var mass = Math.random() * maxMass + 1;
- // Fill in texture values
- posArray[ k + 0 ] = x;
- posArray[ k + 1 ] = y;
- posArray[ k + 2 ] = z;
- posArray[ k + 3 ] = 1;
- velArray[ k + 0 ] = vx;
- velArray[ k + 1 ] = vy;
- velArray[ k + 2 ] = vz;
- velArray[ k + 3 ] = mass;
- }
- }
- function onWindowResize() {
- camera.aspect = window.innerWidth / window.innerHeight;
- camera.updateProjectionMatrix();
- renderer.setSize( window.innerWidth, window.innerHeight );
- particleUniforms[ "cameraConstant" ].value = getCameraConstant( camera );
- }
- function dynamicValuesChanger() {
- velocityUniforms[ "gravityConstant" ].value = effectController.gravityConstant;
- velocityUniforms[ "density" ].value = effectController.density;
- particleUniforms[ "density" ].value = effectController.density;
- }
- function initGUI() {
- var gui = new GUI( { width: 300 } );
- var folder1 = gui.addFolder( 'Dynamic parameters' );
- folder1.add( effectController, "gravityConstant", 0.0, 1000.0, 0.05 ).onChange( dynamicValuesChanger );
- folder1.add( effectController, "density", 0.0, 10.0, 0.001 ).onChange( dynamicValuesChanger );
- var folder2 = gui.addFolder( 'Static parameters' );
- folder2.add( effectController, "radius", 10.0, 1000.0, 1.0 );
- folder2.add( effectController, "height", 0.0, 50.0, 0.01 );
- folder2.add( effectController, "exponent", 0.0, 2.0, 0.001 );
- folder2.add( effectController, "maxMass", 1.0, 50.0, 0.1 );
- folder2.add( effectController, "velocity", 0.0, 150.0, 0.1 );
- folder2.add( effectController, "velocityExponent", 0.0, 1.0, 0.01 );
- folder2.add( effectController, "randVelocity", 0.0, 50.0, 0.1 );
- var buttonRestart = {
- restartSimulation: function () {
- restartSimulation();
- }
- };
- folder2.add( buttonRestart, 'restartSimulation' );
- folder1.open();
- folder2.open();
- }
- function getCameraConstant( camera ) {
- return window.innerHeight / ( Math.tan( THREE.MathUtils.DEG2RAD * 0.5 * camera.fov ) / camera.zoom );
- }
- function animate() {
- requestAnimationFrame( animate );
- render();
- stats.update();
- }
- function render() {
- gpuCompute.compute();
- particleUniforms[ "texturePosition" ].value = gpuCompute.getCurrentRenderTarget( positionVariable ).texture;
- particleUniforms[ "textureVelocity" ].value = gpuCompute.getCurrentRenderTarget( velocityVariable ).texture;
- renderer.render( scene, camera );
- }
- </script>
- </body>
- </html>
|