├── public
├── font.mp4
└── galaxie2.png
├── src
└── shaders
│ ├── fragment.glsl
│ ├── computeShaderPosition.glsl
│ ├── vertex.glsl
│ └── computeShaderVelocity.glsl
├── .gitignore
├── package.json
├── LICENSE
├── README.md
├── index.html
├── style.css
└── main.js
/public/font.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/N0rvel/galaxy_sim/HEAD/public/font.mp4
--------------------------------------------------------------------------------
/public/galaxie2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/N0rvel/galaxy_sim/HEAD/public/galaxie2.png
--------------------------------------------------------------------------------
/src/shaders/fragment.glsl:
--------------------------------------------------------------------------------
1 | export default `
2 |
3 | varying vec4 vColor;
4 |
5 |
6 | void main()
7 | {
8 | gl_FragColor = vColor;
9 | }
10 | `
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "galaxy-threejs",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "devDependencies": {
12 | "vite": "^3.0.0"
13 | },
14 | "dependencies": {
15 | "@types/dat.gui": "^0.7.7",
16 | "dat.gui": "^0.7.9",
17 | "three": "^0.142.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/shaders/computeShaderPosition.glsl:
--------------------------------------------------------------------------------
1 | export default `
2 | #define delta ( 1.0 / 30.0 )
3 |
4 | void main() {
5 |
6 | vec2 uv = gl_FragCoord.xy / resolution.xy;
7 |
8 | vec4 tmpPos = texture2D( texturePosition, uv );
9 | vec3 pos = tmpPos.xyz;
10 |
11 | vec4 tmpVel = texture2D( textureVelocity, uv );
12 | vec3 vel = tmpVel.xyz;
13 |
14 | // Dynamics
15 | if (pos.x != 0.0 && pos.y != 0.0 && pos.z != 0.0){
16 | pos += vel * delta;
17 | }
18 |
19 | // Output the positions and isDarkMatter in the output color
20 | gl_FragColor = vec4( pos, tmpPos.w);
21 | }
22 | `
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Bastien Proudhom
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Galaxy Simulation with ThreeJS and Shaders
2 |
3 | This project is a simulation of thousands of particles that reproduce gravity to simulate galaxies. It uses ThreeJS for 3D rendering and shaders to calculate the movement of particles in real-time.
4 |
5 | 
6 |
7 | # Demo
8 | Live demo here: https://galaxym.ovh
9 |
10 | ## Usage
11 |
12 | 1. Start the development server: `npm run dev`
13 | 2. Open your browser and navigate to `http://127.0.0.1:5174/`
14 |
15 |
16 | ## Features
17 |
18 | - Simulation of thousands of particles that reproduce gravity to simulate galaxies
19 |
20 |
21 | - A menu to change the settings of the simulation (like the graphics settings, the number of particles, the interaction rate...)
22 |
23 |
24 | - A camera controled with the mouse and wheel to zoom-in/zoom-out
25 |
26 | - It can simulate an entire expanding universe :
27 | 
28 |
29 | - It can simulate a unique galaxy :
30 | 
31 |
32 |
33 | - It can simulate the collision between two galaxies :
34 | 
35 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Galaxy Simulation
10 |
11 |
12 |
13 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/shaders/vertex.glsl:
--------------------------------------------------------------------------------
1 | export default `
2 | // For PI declaration:
3 | #include
4 |
5 | // Declare uniforms for texture samplers
6 | uniform sampler2D texturePosition;
7 | uniform sampler2D textureVelocity;
8 |
9 | // Declare uniforms for camera parameters and particle count
10 | uniform float cameraConstant;
11 | uniform float particlesCount;
12 | // Declare constant for maximum acceleration that can be displayed
13 | uniform float uMaxAccelerationColor;
14 |
15 | // Declare uniform for luminosity
16 | uniform float uLuminosity;
17 | uniform float uHideDarkMatter;
18 |
19 | // Declare varying variable for color
20 | varying vec4 vColor;
21 |
22 |
23 | // Normalize an acceleration value to a range of 0 to 1
24 | float normalized(float acc){
25 | return (acc-0.)/(uMaxAccelerationColor-0.);
26 | }
27 |
28 | void main() {
29 | // Retrieve position data from texture
30 | vec4 posTemp = texture2D( texturePosition, uv );
31 | vec3 pos = posTemp.xyz;
32 | float hideDarkMatter = posTemp.w;
33 |
34 | // Retrieve velocity data from texture and calculate acceleration
35 | vec4 velTemp = texture2D( textureVelocity, uv );
36 | vec3 vel = velTemp.xyz;
37 | float acc = velTemp.w;
38 |
39 | vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );
40 |
41 | /**
42 | * Size
43 | */
44 | gl_PointSize = 1.0;
45 | // Scale point size based on the distance of the particle from the camera
46 | gl_PointSize *= ( 1.0 / - mvPosition.z );
47 |
48 | // Calculate the final position of the particle using the projection matrix
49 | gl_Position = projectionMatrix * mvPosition;
50 |
51 | /**
52 | * Color
53 | */
54 | // Declare colors for low and high acceleration vec3(1.,0.843,0.388)
55 | vec3 hightAccelerationColor= vec3(1.,0.376,0.188);
56 | vec3 lowAccelerationColor= vec3(0.012,0.063,0.988);
57 | vec3 finalColor = vec3(0.0,0.0,0.0);
58 | if(uHideDarkMatter == 1.0) {
59 | if(hideDarkMatter == 0.0){
60 | // Interpolate color based on acceleration
61 | finalColor = mix(lowAccelerationColor, hightAccelerationColor, normalized(acc));
62 | } else {
63 | finalColor = vec3(0.0,0.0,0.0);
64 | }
65 | } else {
66 | finalColor = mix(lowAccelerationColor, hightAccelerationColor, normalized(acc));
67 | }
68 |
69 |
70 | // Set the color of the particle
71 | vColor = vec4(finalColor, uLuminosity);
72 | }
73 | `
74 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | body{
2 | margin: 0;
3 | }
4 |
5 | a{
6 | display: block;
7 | text-decoration: inherit; /* no underline */
8 | font-family: "Inter var",ui-sans-serif,system-ui,-apple-system,system-ui,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
9 | font-size: 1.125rem;
10 | font-weight: 600;
11 | border: 1px solid transparent;
12 | border-radius: .75rem;
13 | box-sizing: border-box;
14 | color: #FFFFFF;
15 | width: 520px;
16 | background-color: #111827;
17 | padding: 14px;
18 | height: 77px;
19 | fill: #FFFFFF;
20 | }
21 |
22 | .dg li:not(.folder){
23 | background: #1a1a1ad1!important;
24 | }
25 |
26 | .main-container{
27 | height: 100vh;
28 | background: black;
29 | display: flex;
30 | align-items: center;
31 | justify-content: center;
32 | }
33 |
34 | .button-container{
35 | display: flex;
36 | justify-content: center;
37 | width: 100%;
38 | z-index: 1;
39 | margin-bottom: 10px;
40 | flex-wrap: wrap;
41 | }
42 |
43 | .calque{
44 | z-index: 1;
45 | width: 100vw;
46 | height: 100vh;
47 | }
48 |
49 | .container-buttons{
50 | display: flex;
51 | justify-content: center;
52 | width: 100%;
53 | z-index: 1;
54 | margin-top: 415px;
55 | flex-wrap: wrap;
56 | }
57 |
58 | .button-svg{
59 | display: flex;
60 | align-items: center;
61 | }
62 |
63 | .button-svg span{
64 | margin-left: 20px;
65 | }
66 |
67 | .button {
68 | margin: 0 10px 10px 10px;
69 | background-color: #111827;
70 | border: 1px solid transparent;
71 | border-radius: .75rem;
72 | box-sizing: border-box;
73 | color: #FFFFFF;
74 | cursor: pointer;
75 | flex: 0 0 auto;
76 | font-family: "Inter var",ui-sans-serif,system-ui,-apple-system,system-ui,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
77 | font-size: 1.125rem;
78 | font-weight: 600;
79 | line-height: 1.5rem;
80 | padding: .75rem 1.2rem;
81 | text-align: center;
82 | text-decoration: none #6B7280 solid;
83 | text-decoration-thickness: auto;
84 | transition-duration: .2s;
85 | transition-property: background-color,border-color,color,fill,stroke;
86 | transition-timing-function: cubic-bezier(.4, 0, 0.2, 1);
87 | user-select: none;
88 | -webkit-user-select: none;
89 | touch-action: manipulation;
90 | width: 250px;
91 | }
92 |
93 | .button:hover, a:hover{
94 | background-color: #374151;
95 | cursor: pointer;
96 | }
97 |
98 | .button:focus {
99 | box-shadow: none;
100 | outline: 2px solid transparent;
101 | outline-offset: 2px;
102 | }
103 | .small{
104 | font-size: 11px;
105 | }
106 |
107 | #fontVideo{
108 | max-height: 1080px;
109 | max-width: 1920px;
110 | height: 100vh;
111 | width: 100vw;
112 | object-fit: cover;
113 | position: fixed;
114 | }
--------------------------------------------------------------------------------
/src/shaders/computeShaderVelocity.glsl:
--------------------------------------------------------------------------------
1 | export default `
2 | // For PI declaration:
3 | #include
4 |
5 | uniform float timeStep;
6 | uniform float gravity;
7 | uniform float interactionRate;
8 | uniform float blackHoleForce;
9 | uniform float uMaxAccelerationColor;
10 |
11 |
12 | const float width = resolution.x;
13 | const float height = resolution.y;
14 |
15 |
16 | void main() {
17 |
18 | // Calculate the ID and UV coordinate of the current pixel
19 | vec2 uv = gl_FragCoord.xy / resolution.xy;
20 | float idParticle = uv.y * resolution.x + uv.x;
21 |
22 | // Sample the position and velocity of the current particle from the input textures
23 | vec4 tmpPos = texture2D( texturePosition, uv );
24 | vec3 pos = tmpPos.xyz;
25 |
26 | vec4 tmpVel = texture2D( textureVelocity, uv );
27 | vec3 vel = tmpVel.xyz;
28 |
29 | float accColor = tmpVel.w;
30 |
31 | // Initialize the acceleration to zero
32 | vec3 acceleration = vec3( 0.0 );
33 |
34 | // Calculate the acceleration due to gravity from all other particles
35 | for ( float y = 0.0; y < height * interactionRate; y++ ) {
36 | for ( float x = 0.0; x < width * interactionRate; x++ ) {
37 | // Calculate the UV coordinate of the other particle
38 | vec2 secondParticleCoords = vec2( x + 0.5, y + 0.5) / resolution.xy;
39 | // Sample the position and velocity of the other particle
40 | vec3 pos2 = texture2D( texturePosition, secondParticleCoords ).xyz;
41 | vec4 velTemp2 = texture2D( textureVelocity, secondParticleCoords );
42 | vec3 vel2 = velTemp2.xyz;
43 |
44 | // Calculate the ID of the other particle
45 | float idParticle2 = secondParticleCoords.y * resolution.x + secondParticleCoords.x;
46 |
47 | // Skip the current particle
48 | if ( idParticle == idParticle2 ) {
49 | continue;
50 | }
51 |
52 | // Calculate the distance and displacement between the two particles
53 | vec3 dPos = pos2 - pos;
54 | float distance = length( dPos );
55 |
56 | // Calculate the acceleration due to gravity using Newton's law of universal gravitation
57 | float distanceSq = (distance * distance) + 1.0;
58 | float gravityField = gravity * 1.0 / distanceSq;
59 |
60 | // Limit the maximum acceleration due to gravity
61 | gravityField = min( gravityField, 1.0 );
62 | // Use a stronger force for the black hole
63 | if(pos2.x == 0.0 && pos2.y == 0.0 && pos2.z == 0.0){
64 | gravityField = gravity * blackHoleForce / distanceSq;
65 | }
66 | // Add the acceleration to the total acceleration
67 | acceleration += gravityField * normalize( dPos );
68 | }
69 | }
70 |
71 | // Update the velocity based on the acceleration and elapsed time
72 | vel += timeStep * acceleration;
73 |
74 | // Store the acceleration in the fourth component of the output color
75 | accColor = length(acceleration);
76 | if (length(accColor) > uMaxAccelerationColor) {
77 | // If it does, set it to the maximum value
78 | accColor = normalize(accColor) * uMaxAccelerationColor;
79 | }
80 |
81 | // Output the velocity and acceleration in the output color
82 | gl_FragColor = vec4( vel, accColor );
83 | }
84 | `
85 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import galaxyVortexShader from '/src/shaders/vertex.glsl';
2 | import galaxyFragmentShader from '/src/shaders/fragment.glsl';
3 | import computeShaderVelocity from '/src/shaders/computeShaderVelocity.glsl';
4 | import computeShaderPosition from '/src/shaders/computeShaderPosition.glsl';
5 | import {GUI} from "dat.gui";
6 | import * as THREE from "three";
7 | import Stats from "three/examples/jsm/libs/stats.module";
8 |
9 | import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
10 | import {GPUComputationRenderer} from "three/examples/jsm/misc/GPUComputationRenderer";
11 | import {EffectComposer} from "three/examples/jsm/postprocessing/EffectComposer";
12 | import {UnrealBloomPass} from "three/examples/jsm/postprocessing/UnrealBloomPass";
13 | import {RenderPass} from "three/examples/jsm/postprocessing/RenderPass";
14 | import {ShaderPass} from "three/examples/jsm/postprocessing/ShaderPass";
15 | import {BlendShader} from "three/examples/jsm/shaders/BlendShader";
16 | import {SavePass} from "three/examples/jsm/postprocessing/SavePass";
17 | import {CopyShader} from "three/examples/jsm/shaders/CopyShader";
18 |
19 | let container, stats;
20 | let camera, scene, renderer, geometry, composer;
21 |
22 |
23 | let gpuCompute;
24 | let velocityVariable;
25 | let positionVariable;
26 | let velocityUniforms;
27 | let particleUniforms;
28 | let effectController;
29 | let particles;
30 | let material;
31 | let controls;
32 | let luminosity;
33 | let paused = false;
34 | let autoRotation = true;
35 | let bloom = { strength: 1.0};
36 | let bloomPass;
37 | // motion blur
38 | let renderTargetParameters;
39 | let savePass;
40 | let blendPass;
41 | /*--------------------------INITIALISATION-----------------------------------------------*/
42 | const gravity = 20;
43 | const interactionRate = 1.0;
44 | const timeStep = 0.001;
45 | const blackHoleForce = 100.0;
46 | const constLuminosity = 1.0;
47 | const numberOfStars = 30000;
48 | const radius = 100;
49 | const height = 5;
50 | const middleVelocity = 2;
51 | const velocity = 15;
52 | const typeOfSimulation = { "Galaxie": 1, "Univers": 2, "Collision de galaxies": 3 };
53 | renderTargetParameters = {
54 | minFilter: THREE.LinearFilter,
55 | magFilter: THREE.LinearFilter,
56 | stencilBuffer: false
57 | };
58 |
59 | // save pass
60 | savePass = new SavePass(
61 | new THREE.WebGLRenderTarget(
62 | window.innerWidth,
63 | window.innerHeight,
64 | renderTargetParameters
65 | )
66 | );
67 |
68 | // blend pass
69 | blendPass = new ShaderPass(BlendShader, "tDiffuse1");
70 | blendPass.uniforms["tDiffuse2"].value = savePass.renderTarget.texture;
71 | blendPass.uniforms["mixRatio"].value = 0.5;
72 |
73 | // output pass
74 | const outputPass = new ShaderPass(CopyShader);
75 | outputPass.renderToScreen = true;
76 |
77 | effectController = {
78 | // Can be changed dynamically
79 | gravity: gravity,
80 | interactionRate: interactionRate,
81 | timeStep: timeStep,
82 | blackHoleForce: blackHoleForce,
83 | luminosity: constLuminosity,
84 | maxAccelerationColor: 50.0,
85 | maxAccelerationColorPercent: 5,
86 | motionBlur: false,
87 | hideDarkMatter: false,
88 |
89 | // Must restart simulation
90 | numberOfStars: numberOfStars,
91 | radius: radius,
92 | height: height,
93 | middleVelocity: middleVelocity,
94 | velocity: velocity,
95 | typeOfSimulation: 1,
96 | autoRotation: false
97 | };
98 |
99 | let PARTICLES = effectController.numberOfStars;
100 |
101 | // 1 = normal mode ; 2 = experimental mode
102 | let selectedChoice = 1;
103 | document.getElementById("choice1").addEventListener("click", () => selectChoice(1));
104 | document.getElementById("choice2").addEventListener("click", () => selectChoice(2));
105 | function selectChoice(choice) {
106 | selectedChoice = choice;
107 | document.getElementById("main-container").remove();
108 | if (selectedChoice === 1){
109 | effectController = {
110 | // Can be changed dynamically
111 | gravity: gravity,
112 | interactionRate: 0.5,
113 | timeStep: timeStep,
114 | blackHoleForce: blackHoleForce,
115 | luminosity: constLuminosity,
116 | maxAccelerationColor: 4.0,
117 | maxAccelerationColorPercent: 0.4,
118 | motionBlur: false,
119 | hideDarkMatter: false,
120 |
121 | // Must restart simulation
122 | numberOfStars: 10000,
123 | radius: 50,
124 | height: height,
125 | middleVelocity: middleVelocity,
126 | velocity: 7,
127 | typeOfSimulation: 1,
128 | autoRotation: false
129 | };
130 | }
131 | init(effectController.typeOfSimulation.toString());
132 | animate();
133 | }
134 |
135 |
136 | /*-------------------------------------------------------------------------*/
137 |
138 | /**
139 | *
140 | * @param typeOfSimulation
141 | */
142 | function init(typeOfSimulation) {
143 |
144 | container = document.createElement( 'div' );
145 | document.body.appendChild( container );
146 |
147 | camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.01, 9999999999999999999 );
148 | camera.position.x = 15
149 | camera.position.y = 112;
150 | camera.position.z = 168;
151 |
152 | if (effectController.typeOfSimulation === 3){
153 | camera.position.x = 15
154 | camera.position.y = 456;
155 | camera.position.z = 504;
156 | }
157 |
158 | if (selectedChoice === 1 && effectController.typeOfSimulation === 2){
159 | camera.position.x = 15
160 | camera.position.y = 456;
161 | camera.position.z = 504;
162 | }
163 |
164 |
165 | scene = new THREE.Scene();
166 |
167 | renderer = new THREE.WebGLRenderer();
168 | renderer.setPixelRatio( window.devicePixelRatio );
169 | renderer.setSize( window.innerWidth, window.innerHeight );
170 | container.appendChild( renderer.domElement );
171 |
172 | controls = new OrbitControls( camera, renderer.domElement );
173 | if (effectController.typeOfSimulation === 1 || effectController.typeOfSimulation === 3) {
174 | controls.autoRotate = false;
175 | } else if (effectController.typeOfSimulation === 2){
176 | controls.autoRotate = true;
177 | controls.autoRotateSpeed = -1.0;
178 | }
179 |
180 | initComputeRenderer(typeOfSimulation);
181 |
182 | // Show fps, ping, etc
183 | stats = new Stats();
184 | container.appendChild( stats.dom );
185 |
186 | window.addEventListener( 'resize', onWindowResize );
187 |
188 | initGUI();
189 | initParticles(typeOfSimulation);
190 | dynamicValuesChanger();
191 | const renderScene = new RenderPass( scene, camera );
192 |
193 | /* ---- Adding bloom effect ---- */
194 | bloomPass = new UnrealBloomPass(
195 | new THREE.Vector2( window.innerWidth, window.innerHeight ),
196 | 0,
197 | 0,
198 | 0
199 | );
200 | bloomPass.strength = bloom.strength;
201 |
202 | composer = new EffectComposer( renderer );
203 | composer.addPass( renderScene );
204 | composer.addPass( bloomPass );
205 | composer.addPass(blendPass);
206 | composer.addPass(savePass);
207 | composer.addPass(outputPass);
208 | }
209 |
210 | function initComputeRenderer(typeOfSimulation) {
211 | let textureSize = Math.round(Math.sqrt(effectController.numberOfStars));
212 | gpuCompute = new GPUComputationRenderer( textureSize, textureSize, renderer );
213 | if ( renderer.capabilities.isWebGL2 === false ) {
214 | gpuCompute.setDataType( THREE.HalfFloatType );
215 | }
216 |
217 | const dtPosition = gpuCompute.createTexture();
218 | const dtVelocity = gpuCompute.createTexture();
219 |
220 | if (typeOfSimulation === "1"){
221 | fillTextures( dtPosition, dtVelocity );
222 | } else if (typeOfSimulation === "2"){
223 | fillUniverseTextures( dtPosition, dtVelocity )
224 | } else if (typeOfSimulation === "3"){
225 | fillGalaxiesCollisionTextures( dtPosition, dtVelocity )
226 | }
227 |
228 | velocityVariable = gpuCompute.addVariable( 'textureVelocity', computeShaderVelocity, dtVelocity );
229 | positionVariable = gpuCompute.addVariable( 'texturePosition', computeShaderPosition, dtPosition );
230 |
231 | gpuCompute.setVariableDependencies( velocityVariable, [ positionVariable, velocityVariable ] );
232 | gpuCompute.setVariableDependencies( positionVariable, [ positionVariable, velocityVariable ] );
233 |
234 | velocityUniforms = velocityVariable.material.uniforms;
235 | velocityUniforms[ 'gravity' ] = { value: 0.0 };
236 | velocityUniforms[ 'interactionRate' ] = { value: 0.0 };
237 | velocityUniforms[ 'timeStep' ] = { value: 0.0 };
238 | velocityUniforms[ 'uMaxAccelerationColor' ] = { value: 0.0 };
239 | velocityUniforms[ 'blackHoleForce' ] = { value: 0.0 };
240 | velocityUniforms[ 'luminosity' ] = { value: 0.0 };
241 |
242 | const error = gpuCompute.init();
243 |
244 | if ( error !== null ) {
245 | console.error( error );
246 | }
247 | }
248 |
249 | /**
250 | * Init particles (material, positions, uvs coordinates)
251 | * @param typeOfSimulation
252 | */
253 | function initParticles(typeOfSimulation) {
254 |
255 | // Create a buffer geometry to store the particle data
256 | geometry = new THREE.BufferGeometry();
257 |
258 | // Create array to store the position of the particles
259 | const positions = new Float32Array( PARTICLES * 3 );
260 |
261 | // Create an array to store the UV coordinates of each particle
262 | const uvs = new Float32Array( PARTICLES * 2 );
263 |
264 | // Calculate the size of the matrix based on the number of particles
265 | let matrixSize = Math.sqrt(effectController.numberOfStars);
266 | let p = 0;
267 | for ( let j = 0; j < matrixSize; j ++ ) {
268 | for ( let i = 0; i < matrixSize; i ++ ) {
269 | uvs[ p ++ ] = i / ( matrixSize - 1 );
270 | uvs[ p ++ ] = j / ( matrixSize - 1 );
271 | }
272 | }
273 |
274 | geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
275 | geometry.setAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
276 |
277 | particleUniforms = {
278 | 'texturePosition': { value: null },
279 | 'textureVelocity': { value: null },
280 | 'cameraConstant': { value: getCameraConstant( camera ) },
281 | 'particlesCount': { value: PARTICLES },
282 | 'uMaxAccelerationColor': { value: effectController.maxAccelerationColor },
283 | 'uLuminosity' : { value: luminosity},
284 | 'uHideDarkMatter' : { value: effectController.hideDarkMatter},
285 | };
286 |
287 | // THREE.ShaderMaterial
288 | // Create the material of the particles
289 | material = new THREE.ShaderMaterial( {
290 | depthWrite: false,
291 | blending: THREE.AdditiveBlending,
292 | vertexColors: true,
293 | uniforms: particleUniforms,
294 | vertexShader: galaxyVortexShader,
295 | fragmentShader: galaxyFragmentShader
296 | });
297 | if (typeOfSimulation === "2"){
298 | material = new THREE.ShaderMaterial( {
299 | depthWrite: false,
300 | blending: THREE.AdditiveBlending,
301 | vertexColors: true,
302 | uniforms: particleUniforms,
303 | vertexShader: galaxyVortexShader,
304 | fragmentShader: galaxyFragmentShader
305 | });
306 | }
307 |
308 | particles = new THREE.Points( geometry, material );
309 | particles.frustumCulled = false;
310 | particles.matrixAutoUpdate = false;
311 | particles.updateMatrix();
312 | scene.add( particles );
313 | }
314 |
315 | /**
316 | * Init positions et volocities for all particles
317 | * @param texturePosition array that contain positions of particles
318 | * @param textureVelocity array that contain velocities of particles
319 | */
320 | function fillTextures( texturePosition, textureVelocity ) {
321 |
322 | const posArray = texturePosition.image.data;
323 | const velArray = textureVelocity.image.data;
324 |
325 | const radius = effectController.radius;
326 | const height = effectController.height;
327 | const middleVelocity = effectController.middleVelocity;
328 | const maxVel = effectController.velocity;
329 |
330 | for ( let k = 0, kl = posArray.length; k < kl; k += 4 ) {
331 | // Position
332 | let x, z, rr, y, vx, vy, vz;
333 | // The first particle will be the black hole
334 | if (k === 0){
335 | x = 0;
336 | z = 0;
337 | y = 0;
338 | rr = 0;
339 | } else {
340 | // Generate random position for the particle within the radius
341 | do {
342 | x = ( Math.random() * 2 - 1 );
343 | z = ( Math.random() * 2 - 1 );
344 | // The variable rr is used to calculate the distance from the center of the radius for each particle.
345 | // It is used in the calculation of rExp which is used to determine the position of the particle within the radius.
346 | // If a particle is closer to the center, rr will be smaller, and rExp will be larger, which means that the particle will be placed closer to the center.
347 | // It also can affect the velocity of the particle as it is used in the calculation of the velocity of the particle.
348 | rr = x * x + z * z;
349 |
350 | } while ( rr > 1 );
351 | rr = Math.sqrt( rr );
352 |
353 | const rExp = radius * Math.pow( rr, middleVelocity );
354 |
355 | // Velocity
356 | const vel = maxVel * Math.pow( rr, 0.2 );
357 |
358 | vx = vel * z + ( Math.random() * 2 - 1 ) * 0.001;
359 | vy = ( Math.random() * 2 - 1 ) * 0.001 * 0.05;
360 | vz = - vel * x + ( Math.random() * 2 - 1 ) * 0.001;
361 |
362 | x *= rExp;
363 | z *= rExp;
364 | y = ( Math.random() * 2 - 1 ) * height;
365 | }
366 |
367 | // Fill in texture values
368 | posArray[ k + 0 ] = x;
369 | posArray[ k + 1 ] = y;
370 | posArray[ k + 2 ] = z;
371 |
372 | // Hide dark matter (hide 85% of stars)
373 | if (k > 0.85 * (posArray.length / 4)){
374 | posArray[ k + 3 ] = 1;
375 | } else {
376 | posArray[ k + 3 ] = 0;
377 | }
378 |
379 |
380 | velArray[ k + 0 ] = vx;
381 | velArray[ k + 1 ] = vy;
382 | velArray[ k + 2 ] = vz;
383 | velArray[ k + 3 ] = 0;
384 | }
385 | }
386 |
387 | /**
388 | * Init positions et volocities for all particles
389 | * @param texturePosition array that contain positions of particles
390 | * @param textureVelocity array that contain velocities of particles
391 | */
392 | function fillUniverseTextures( texturePosition, textureVelocity ) {
393 |
394 | const posArray = texturePosition.image.data;
395 | const velArray = textureVelocity.image.data;
396 |
397 | // Set the radius of the sphere
398 | const radius = effectController.radius;
399 |
400 | // Set the pulse strength
401 | let pulseScale = 5;
402 | if (selectedChoice === 1){
403 | pulseScale = 3.18;
404 | }
405 |
406 | for ( let k = 0, kl = posArray.length; k < kl; k += 4 ) {
407 | // Generate random point within a unit sphere
408 | let x, y, z;
409 | do {
410 | x = ( Math.random() * 2 - 1 );
411 | y = ( Math.random() * 2 - 1 );
412 | z = ( Math.random() * 2 - 1 );
413 | } while ( x*x + y*y + z*z > 1 );
414 |
415 | // Scale point to desired radius
416 | x *= radius;
417 | y *= radius;
418 | z *= radius;
419 |
420 | // Velocity
421 | const vx = pulseScale * x;
422 | const vy = pulseScale * y;
423 | const vz = pulseScale * z;
424 |
425 | // Fill in texture values
426 | posArray[ k + 0 ] = x;
427 | posArray[ k + 1 ] = y;
428 | posArray[ k + 2 ] = z;
429 | // Hide dark matter (hide 85% of stars)
430 | if (k > 0.85 * (posArray.length / 4)){
431 | posArray[ k + 3 ] = 1;
432 | } else {
433 | posArray[ k + 3 ] = 0;
434 | }
435 |
436 | velArray[ k + 0 ] = vx;
437 | velArray[ k + 1 ] = vy;
438 | velArray[ k + 2 ] = vz;
439 | velArray[ k + 3 ] = 0;
440 | }
441 | }
442 |
443 | function fillGalaxiesCollisionTextures( texturePosition, textureVelocity ){
444 | const posArray = texturePosition.image.data;
445 | const velArray = textureVelocity.image.data;
446 |
447 | const radius = effectController.radius;
448 | const height = effectController.height;
449 | const middleVelocity = effectController.middleVelocity;
450 | const maxVel = effectController.velocity;
451 | let indice = 0;
452 | for ( let k = 0, kl = posArray.length; k < kl; k += 4 ) {
453 | // Position
454 | let x, z, rr, y, vx, vy, vz;
455 | // If pair
456 | if (indice % 2 === 0){
457 | // Generate random position for the particle within the radius
458 | do {
459 | x = ( Math.random() * 2 - 1 );
460 | z = ( Math.random() * 2 - 1 );
461 | // The variable rr is used to calculate the distance from the center of the radius for each particle.
462 | // It is used in the calculation of rExp which is used to determine the position of the particle within the radius.
463 | // If a particle is closer to the center, rr will be smaller, and rExp will be larger, which means that the particle will be placed closer to the center.
464 | // It also can affect the velocity of the particle as it is used in the calculation of the velocity of the particle.
465 | rr = x * x + z * z;
466 |
467 | } while ( rr > 1 );
468 | rr = Math.sqrt( rr );
469 |
470 | const rExp = radius * Math.pow( rr, middleVelocity );
471 |
472 | // Velocity
473 | const vel = maxVel * Math.pow( rr, 0.2 );
474 |
475 | vx = vel * z + ( Math.random() * 2 - 1 ) * 0.001;
476 | vy = ( Math.random() * 2 - 1 ) * 0.001 * 0.05;
477 | vz = - vel * x + ( Math.random() * 2 - 1 ) * 0.001;
478 |
479 | x *= rExp;
480 | z *= rExp;
481 | y = ( Math.random() * 2 - 1 ) * height;
482 | }
483 | // If impair
484 | else {
485 | // Generate random position for the particle within the radius
486 | do {
487 | x = ( Math.random() * 2 - 1 );
488 | y = ( Math.random() * 2 - 1 );
489 | // The variable rr is used to calculate the distance from the center of the radius for each particle.
490 | // It is used in the calculation of rExp which is used to determine the position of the particle within the radius.
491 | // If a particle is closer to the center, rr will be smaller, and rExp will be larger, which means that the particle will be placed closer to the center.
492 | // It also can affect the velocity of the particle as it is used in the calculation of the velocity of the particle.
493 | rr = x*x + y*y;
494 |
495 | } while ( rr > 1 );
496 | rr = Math.sqrt( rr );
497 |
498 | const rExp = radius * Math.pow( rr, middleVelocity );
499 |
500 | // Velocity
501 | const vel = maxVel * Math.pow( rr, 0.2 );
502 |
503 | vx = -vel * y + ( Math.random() * 2 - 1 ) * 0.001;
504 | vy = vel * x + ( Math.random() * 2 - 1 ) * 0.001;
505 | vz = -( Math.random() * 2 - 1 ) * 0.001 * 0.05;
506 | const angle = -Math.PI/4;
507 |
508 | const vy_temp = vy;
509 | const vz_temp = vz;
510 | vy = vy_temp * Math.cos(angle) - vz_temp * Math.sin(angle);
511 | vz = vy_temp * Math.sin(angle) + vz_temp * Math.cos(angle);
512 |
513 | x = x*rExp +200;
514 | y = y*rExp +200;
515 | z = ( Math.random() * 2 - 1 ) * height +10;
516 | const y_temp = y;
517 | const z_temp = z;
518 | y = y_temp * Math.cos(angle) - z_temp * Math.sin(angle);
519 | z = y_temp * Math.sin(angle) + z_temp * Math.cos(angle);
520 | }
521 |
522 |
523 | // Fill in texture values
524 | posArray[ k + 0 ] = x;
525 | posArray[ k + 1 ] = y;
526 | posArray[ k + 2 ] = z;
527 | // Hide dark matter (hide 85% of stars)
528 | if (k > 0.85 * (posArray.length / 4)){
529 | posArray[ k + 3 ] = 1;
530 | } else {
531 | posArray[ k + 3 ] = 0;
532 | }
533 |
534 | velArray[ k + 0 ] = vx;
535 | velArray[ k + 1 ] = vy;
536 | velArray[ k + 2 ] = vz;
537 | velArray[ k + 3 ] = 0;
538 | indice++;
539 | }
540 | }
541 |
542 | /**
543 | * Restart the simulation
544 | */
545 | function restartSimulation() {
546 | paused = false;
547 | scene.remove(particles);
548 | material.dispose();
549 | geometry.dispose();
550 | document.getElementsByClassName('dg ac').item(0).removeChild(document.getElementsByClassName('dg main a').item(0));
551 |
552 | document.body.removeChild(document.querySelector('canvas').parentNode);
553 |
554 | PARTICLES = effectController.numberOfStars;
555 |
556 | init(effectController.typeOfSimulation.toString());
557 | }
558 |
559 | function resetParameters(){
560 | switchSimulation();
561 | }
562 |
563 | /**
564 | * manage the resize of the windows to keep the scene centered
565 | */
566 | function onWindowResize() {
567 | camera.aspect = window.innerWidth / window.innerHeight;
568 | camera.updateProjectionMatrix();
569 | renderer.setSize( window.innerWidth, window.innerHeight );
570 | particleUniforms[ 'cameraConstant' ].value = getCameraConstant( camera );
571 | }
572 |
573 | function dynamicValuesChanger() {
574 | velocityUniforms[ 'gravity' ].value = effectController.gravity;
575 | velocityUniforms[ 'interactionRate' ].value = effectController.interactionRate;
576 | velocityUniforms[ 'timeStep' ].value = effectController.timeStep;
577 | console.log(effectController.maxAccelerationColor);
578 | velocityUniforms[ 'uMaxAccelerationColor' ].value = effectController.maxAccelerationColor;
579 | velocityUniforms[ 'blackHoleForce' ].value = effectController.blackHoleForce;
580 | velocityUniforms[ 'luminosity' ].value = effectController.luminosity;
581 | }
582 |
583 | /**
584 | * Init the menu
585 | */
586 | function initGUI() {
587 |
588 | const gui = new GUI( { width: 350 } );
589 |
590 | const folder1 = gui.addFolder( 'Dynamic Parameters' );
591 |
592 | const folderGraphicSettings = gui.addFolder( 'Graphics settings' );
593 |
594 | const folder2 = gui.addFolder( 'Static parameters (need to restart the simulation)' );
595 |
596 | folder1.add( effectController, 'gravity', 0.0, 1000.0, 0.05 ).onChange( dynamicValuesChanger ).name("Gravitational force");
597 | folder1.add( effectController, 'interactionRate', 0.0, 1.0, 0.001 ).onChange( dynamicValuesChanger ).name("Interaction rate (%)");
598 | folder1.add( effectController, 'timeStep', 0.0, 0.01, 0.0001 ).onChange( dynamicValuesChanger ).name("Time step");
599 | folder1.add( effectController, 'hideDarkMatter', 0, 1, 1 ).onChange( function ( value ) {
600 | effectController.hideDarkMatter = value ;
601 | } ).name("Hide dark matter");
602 | folderGraphicSettings.add( bloom, 'strength', 0.0, 2.0, 0.1 ).onChange( function ( value ) {
603 | bloom.strength = value ;
604 | bloomPass.strength = bloom.strength;
605 | } ).name("Bloom");
606 | folderGraphicSettings.add( effectController, 'motionBlur', 0, 1, 1 ).onChange( function ( value ) {
607 | effectController.motionBlur = value ;
608 | } ).name("Motion blur");
609 | if (effectController.typeOfSimulation === 1 || effectController.typeOfSimulation === 3){
610 | folder1.add( effectController, 'blackHoleForce', 0.0, 10000.0, 1.0 ).onChange( dynamicValuesChanger ).name("Black hole mass");
611 | folderGraphicSettings.add( effectController, 'maxAccelerationColorPercent', 0.01, 100, 0.01 ).onChange( function ( value ) {
612 | effectController.maxAccelerationColor = value * 10;
613 | dynamicValuesChanger();
614 | } ).name("Colors mix (%)");
615 | folder2.add( effectController, 'numberOfStars', 2.0, 1000000.0, 1.0 ).name("Number of stars");
616 | folder2.add( effectController, 'radius', 1.0, 1000.0, 1.0 ).name("Galaxy diameter");
617 | folder2.add( effectController, 'height', 0.0, 50.0, 0.01 ).name("Galaxy height");
618 | folder2.add( effectController, 'middleVelocity', 0.0, 20.0, 0.001 ).name("Center rotation speed");
619 | folder2.add( effectController, 'velocity', 0.0, 150.0, 0.1 ).name("Initial rotation speed");
620 | } else if (effectController.typeOfSimulation === 2){
621 | folderGraphicSettings.add( effectController, 'luminosity', 0.0, 1.0, 0.0001 ).onChange( dynamicValuesChanger ).name("Luminosity");
622 | folderGraphicSettings.add( effectController, 'maxAccelerationColorPercent', 0.01, 100, 0.01 ).onChange( function ( value ) {
623 | effectController.maxAccelerationColor = value / 10;
624 | dynamicValuesChanger();
625 | } ).name("Colors mix (%)");
626 | folder2.add( effectController, 'numberOfStars', 2.0, 10000000.0, 1.0 ).name("Number of galaxies");
627 | folder2.add( effectController, 'radius', 1.0, 1000.0, 1.0 ).name("Initial diameter of the universe");
628 | folder2.add( effectController, 'autoRotation').name('Auto-rotation').listen().onChange(function(){setChecked()});
629 | }
630 |
631 |
632 | const buttonRestart = {
633 | restartSimulation: function () {
634 | restartSimulation();
635 | }
636 | };
637 |
638 | const buttonReset = {
639 | resetParameters: function () {
640 | resetParameters();
641 | }
642 | };
643 | const buttonPause = {
644 | pauseSimulation: function () {
645 | }
646 | };
647 |
648 |
649 | function setChecked(){
650 | autoRotation = !autoRotation;
651 | controls.autoRotate = autoRotation;
652 | }
653 |
654 | folder2.add( effectController, 'typeOfSimulation', typeOfSimulation ).onChange(switchSimulation).name("Type of simulation");
655 | folder2.add( buttonRestart, 'restartSimulation' ).name("Restart the simulation");
656 | folder2.add( buttonReset, 'resetParameters' ).name("Reset parameters");
657 | let buttonPauseController = folder2.add( buttonPause, 'pauseSimulation' ).name("Pause");
658 | buttonPauseController.onChange(function(){
659 | paused = !paused;
660 | if(paused){
661 | buttonPauseController.name("Resume");
662 | }else{
663 | buttonPauseController.name("Pause");
664 | }
665 | buttonPauseController.updateDisplay();
666 | });
667 |
668 | folder1.open();
669 | folder2.open();
670 | folderGraphicSettings.open();
671 | }
672 |
673 | function getCameraConstant( camera ) {
674 | return window.innerHeight / ( Math.tan( THREE.MathUtils.DEG2RAD * 0.5 * camera.fov ) / camera.zoom );
675 | }
676 |
677 | /**
678 | * Switch the current simulation
679 | */
680 | function switchSimulation(){
681 | paused = false;
682 | // Normal mode (small configuration)
683 | if (selectedChoice === 1){
684 | switch (effectController.typeOfSimulation.toString()) {
685 | // Single galaxy
686 | case "1":
687 | scene.remove(particles);
688 | bloom.strength = 1.0;
689 | effectController = {
690 | // Can be changed dynamically
691 | gravity: gravity,
692 | interactionRate: 0.5,
693 | timeStep: timeStep,
694 | blackHoleForce: blackHoleForce,
695 | luminosity: constLuminosity,
696 | maxAccelerationColor: 4.0,
697 | maxAccelerationColorPercent: 0.4,
698 | motionBlur: false,
699 | hideDarkMatter: false,
700 |
701 | // Must restart simulation
702 | numberOfStars: 10000,
703 | radius: 50,
704 | height: height,
705 | middleVelocity: middleVelocity,
706 | velocity: 7,
707 | typeOfSimulation: 1,
708 | autoRotation: false
709 | };
710 | material.dispose();
711 | geometry.dispose();
712 | document.getElementsByClassName('dg ac').item(0).removeChild(document.getElementsByClassName('dg main a').item(0));
713 |
714 | document.body.removeChild(document.querySelector('canvas').parentNode);
715 |
716 | PARTICLES = effectController.numberOfStars;
717 |
718 | init(effectController.typeOfSimulation.toString());
719 | break;
720 | // Universe
721 | case "2":
722 | scene.remove(particles);
723 | bloom.strength = 0.7;
724 | effectController = {
725 | // Can be changed dynamically
726 | gravity: 225.0,
727 | interactionRate: 0.05,
728 | timeStep: 0.0001,
729 | blackHoleForce: 100.0,
730 | luminosity: 0.25,
731 | maxAccelerationColor: 2.0,
732 | maxAccelerationColorPercent: 20,
733 | motionBlur: false,
734 | hideDarkMatter: false,
735 |
736 | // Must restart simulation
737 | numberOfStars: 100000,
738 | radius: 2,
739 | height: 5,
740 | middleVelocity: 2,
741 | velocity: 15,
742 | typeOfSimulation: 2,
743 | autoRotation: true
744 | };
745 | material.dispose();
746 | geometry.dispose();
747 | document.getElementsByClassName('dg ac').item(0).removeChild(document.getElementsByClassName('dg main a').item(0));
748 |
749 | document.body.removeChild(document.querySelector('canvas').parentNode);
750 |
751 | PARTICLES = effectController.numberOfStars;
752 |
753 | init(effectController.typeOfSimulation.toString());
754 | break;
755 | // Galaxies collision
756 | case "3":
757 | scene.remove(particles);
758 | bloom.strength = 1.0;
759 | effectController = {
760 | // Can be changed dynamically
761 | gravity: 40,
762 | interactionRate: 0.5,
763 | timeStep: timeStep,
764 | blackHoleForce: blackHoleForce,
765 | luminosity: constLuminosity,
766 | maxAccelerationColor: 15.0,
767 | maxAccelerationColorPercent: 1.5,
768 | motionBlur: false,
769 | hideDarkMatter: false,
770 |
771 | // Must restart simulation
772 | numberOfStars: 10000,
773 | radius: 50,
774 | height: height,
775 | middleVelocity: middleVelocity,
776 | velocity: 7,
777 | typeOfSimulation: 3,
778 | autoRotation: false
779 | };
780 | material.dispose();
781 | geometry.dispose();
782 | document.getElementsByClassName('dg ac').item(0).removeChild(document.getElementsByClassName('dg main a').item(0));
783 |
784 | document.body.removeChild(document.querySelector('canvas').parentNode);
785 |
786 | PARTICLES = effectController.numberOfStars;
787 |
788 | init(effectController.typeOfSimulation.toString());
789 | break;
790 | default:
791 | break;
792 | }
793 | } else {
794 | switch (effectController.typeOfSimulation.toString()) {
795 | // Single galaxy
796 | case "1":
797 | scene.remove(particles);
798 | bloom.strength = 1.0;
799 | effectController = {
800 | // Can be changed dynamically
801 | gravity: gravity,
802 | interactionRate: interactionRate,
803 | timeStep: timeStep,
804 | blackHoleForce: blackHoleForce,
805 | luminosity: constLuminosity,
806 | maxAccelerationColor: 50.0,
807 | maxAccelerationColorPercent: 5.0,
808 | motionBlur: false,
809 | hideDarkMatter: false,
810 |
811 | // Must restart simulation
812 | numberOfStars: numberOfStars,
813 | radius: radius,
814 | height: height,
815 | middleVelocity: middleVelocity,
816 | velocity: velocity,
817 | typeOfSimulation: 1,
818 | autoRotation: false
819 | };
820 | material.dispose();
821 | geometry.dispose();
822 | document.getElementsByClassName('dg ac').item(0).removeChild(document.getElementsByClassName('dg main a').item(0));
823 |
824 | document.body.removeChild(document.querySelector('canvas').parentNode);
825 |
826 | PARTICLES = effectController.numberOfStars;
827 |
828 | init(effectController.typeOfSimulation.toString());
829 | break;
830 | // Universe
831 | case "2":
832 | scene.remove(particles);
833 | bloom.strength = 0.7;
834 | effectController = {
835 | // Can be changed dynamically
836 | gravity: 20.0,
837 | interactionRate: 0.05,
838 | timeStep: 0.0001,
839 | blackHoleForce: 100.0,
840 | luminosity: 0.25,
841 | maxAccelerationColor: 2.0,
842 | maxAccelerationColorPercent: 20,
843 | motionBlur: false,
844 | hideDarkMatter: false,
845 |
846 | // Must restart simulation
847 | numberOfStars: 1000000,
848 | radius: 2,
849 | height: 5,
850 | middleVelocity: 2,
851 | velocity: 15,
852 | typeOfSimulation: 2,
853 | autoRotation: true
854 | };
855 | material.dispose();
856 | geometry.dispose();
857 | document.getElementsByClassName('dg ac').item(0).removeChild(document.getElementsByClassName('dg main a').item(0));
858 |
859 | document.body.removeChild(document.querySelector('canvas').parentNode);
860 |
861 | PARTICLES = effectController.numberOfStars;
862 |
863 | init(effectController.typeOfSimulation.toString());
864 | break;
865 | // Galaxies collision
866 | case "3":
867 | scene.remove(particles);
868 | bloom.strength = 1.0;
869 | effectController = {
870 | // Can be changed dynamically
871 | gravity: gravity,
872 | interactionRate: interactionRate,
873 | timeStep: timeStep,
874 | blackHoleForce: blackHoleForce,
875 | luminosity: constLuminosity,
876 | maxAccelerationColor: 19.0,
877 | maxAccelerationColorPercent: 1.9,
878 | motionBlur: false,
879 | hideDarkMatter: false,
880 |
881 | // Must restart simulation
882 | numberOfStars: numberOfStars,
883 | radius: radius,
884 | height: height,
885 | middleVelocity: middleVelocity,
886 | velocity: 12,
887 | typeOfSimulation: 3,
888 | autoRotation: false
889 | };
890 | material.dispose();
891 | geometry.dispose();
892 | document.getElementsByClassName('dg ac').item(0).removeChild(document.getElementsByClassName('dg main a').item(0));
893 |
894 | document.body.removeChild(document.querySelector('canvas').parentNode);
895 |
896 | PARTICLES = effectController.numberOfStars;
897 | init(effectController.typeOfSimulation.toString());
898 |
899 | break;
900 | default:
901 | break;
902 | }
903 | }
904 |
905 | }
906 |
907 | function animate() {
908 | controls.update();
909 | requestAnimationFrame(animate);
910 | render();
911 | stats.update();
912 | }
913 |
914 | function render() {
915 | if (!paused){
916 | gpuCompute.compute();
917 | particleUniforms[ 'texturePosition' ].value = gpuCompute.getCurrentRenderTarget( positionVariable ).texture;
918 | particleUniforms[ 'textureVelocity' ].value = gpuCompute.getCurrentRenderTarget( velocityVariable ).texture;
919 | material.uniforms.uMaxAccelerationColor.value = effectController.maxAccelerationColor;
920 | }
921 | if (effectController.motionBlur){
922 | composer.removePass(blendPass);
923 | composer.removePass(savePass);
924 | composer.removePass(outputPass);
925 | composer.addPass(blendPass);
926 | composer.addPass(savePass);
927 | composer.addPass(outputPass);
928 | } else {
929 | composer.removePass(blendPass);
930 | composer.removePass(savePass);
931 | composer.removePass(outputPass);
932 | }
933 | material.uniforms.uLuminosity.value = effectController.luminosity;
934 | material.uniforms.uHideDarkMatter.value = effectController.hideDarkMatter;
935 | composer.render(scene, camera);
936 |
937 | }
--------------------------------------------------------------------------------