From e9c39a650bca196f478c5410398deb8ea426b898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9lemy?= <31370477+BarthPaleologue@users.noreply.github.com> Date: Sat, 15 Feb 2025 20:37:51 +0100 Subject: [PATCH 1/8] add neutron star playground --- src/ts/playground.ts | 4 ++ src/ts/playgrounds/neutronStar.ts | 72 +++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/ts/playgrounds/neutronStar.ts diff --git a/src/ts/playground.ts b/src/ts/playground.ts index b32a9c80e..5cb22863e 100644 --- a/src/ts/playground.ts +++ b/src/ts/playground.ts @@ -30,6 +30,7 @@ import { createDebugAssetsScene } from "./playgrounds/debugAssets"; import { createSpaceStationScene } from "./playgrounds/spaceStation"; import { createXrScene } from "./playgrounds/xr"; import { createFlightDemoScene } from "./playgrounds/flightDemo"; +import { createNeutronStarScene } from "./playgrounds/neutronStar"; const canvas = document.getElementById("renderer") as HTMLCanvasElement; canvas.width = window.innerWidth; @@ -65,6 +66,9 @@ switch (requestedScene) { case "flightDemo": scene = await createFlightDemoScene(engine); break; + case "neutronStar": + scene = await createNeutronStarScene(engine); + break; default: scene = await createAutomaticLandingScene(engine); } diff --git a/src/ts/playgrounds/neutronStar.ts b/src/ts/playgrounds/neutronStar.ts new file mode 100644 index 000000000..ba14ec2be --- /dev/null +++ b/src/ts/playgrounds/neutronStar.ts @@ -0,0 +1,72 @@ +// This file is part of Cosmos Journeyer +// +// Copyright (C) 2024 Barthélemy Paléologue +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { AbstractEngine } from "@babylonjs/core/Engines/abstractEngine"; +import { Scene } from "@babylonjs/core/scene"; +import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { enablePhysics } from "./utils"; +import { DefaultControls } from "../defaultControls/defaultControls"; +import { Assets } from "../assets/assets"; +import { NeutronStar } from "../stellarObjects/neutronStar/neutronStar"; +import { newSeededNeutronStarModel } from "../stellarObjects/neutronStar/neutronStarModelGenerator"; +import { MatterJetPostProcess } from "../postProcesses/matterJetPostProcess"; +import { VolumetricLight } from "../volumetricLight/volumetricLight"; +import { translate } from "../uberCore/transforms/basicTransform"; + +export async function createNeutronStarScene(engine: AbstractEngine): Promise { + const scene = new Scene(engine); + scene.useRightHandedSystem = true; + + await enablePhysics(scene); + + await Assets.Init(scene); + + const defaultControls = new DefaultControls(scene); + defaultControls.speed = 2000; + + const camera = defaultControls.getActiveCamera(); + camera.attachControl(); + + scene.activeCamera = camera; + + scene.enableDepthRenderer(camera, false, true); + + const neutronStarModel = newSeededNeutronStarModel(456, "Neutron Star Demo", []); + const neutronStar = new NeutronStar(neutronStarModel, scene); + neutronStar.getTransform().position = new Vector3(0, 0, 1).scaleInPlace(neutronStar.getRadius() * 10); + + const volumetricLight = new VolumetricLight(neutronStar.mesh, neutronStar.volumetricLightUniforms, [], scene); + camera.attachPostProcess(volumetricLight); + + const matterJets = new MatterJetPostProcess(neutronStar.getTransform(), neutronStar.getRadius(), scene); + camera.attachPostProcess(matterJets); + + camera.maxZ = neutronStar.getRadius() * 100; + defaultControls.getTransform().lookAt(neutronStar.getTransform().position); + + scene.onBeforePhysicsObservable.add(() => { + const deltaSeconds = engine.getDeltaTime() / 1000; + const displacement = defaultControls.update(deltaSeconds); + + translate(defaultControls.getTransform(), displacement.negate()); + translate(neutronStar.getTransform(), displacement.negate()); + + matterJets.update(deltaSeconds); + }); + + return scene; +} From 779d0b2e97123a002649d46327bd068d56d5e683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9lemy?= <31370477+BarthPaleologue@users.noreply.github.com> Date: Sat, 15 Feb 2025 20:38:03 +0100 Subject: [PATCH 2/8] Fix bug with volumetric light --- src/ts/volumetricLight/volumetricLight.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ts/volumetricLight/volumetricLight.ts b/src/ts/volumetricLight/volumetricLight.ts index 2a54444d6..8f0a5f526 100644 --- a/src/ts/volumetricLight/volumetricLight.ts +++ b/src/ts/volumetricLight/volumetricLight.ts @@ -29,7 +29,7 @@ export class VolumetricLight extends VolumetricLightScatteringPostProcess { excludedMeshes: AbstractMesh[], scene: Scene ) { - if (scene.activeCameras === null || scene.activeCameras.length === 0) throw new Error("no camera"); + if (scene.activeCamera === null) throw new Error("no camera"); super( `${starMesh.name}VolumetricLight`, 1, From f351650779468baf39ed25087344a4263769be81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9lemy?= <31370477+BarthPaleologue@users.noreply.github.com> Date: Sun, 16 Feb 2025 12:41:47 +0100 Subject: [PATCH 3/8] Make matterjets volumetric cleaning Update matterjet.glsl --- src/shaders/matterjet.glsl | 283 +++++++++++++----- src/ts/playgrounds/neutronStar.ts | 21 +- src/ts/postProcesses/matterJetPostProcess.ts | 29 +- src/ts/stellarObjects/star/starMaterialLut.ts | 2 +- 4 files changed, 236 insertions(+), 99 deletions(-) diff --git a/src/shaders/matterjet.glsl b/src/shaders/matterjet.glsl index 42b9ed115..f40d23cd8 100644 --- a/src/shaders/matterjet.glsl +++ b/src/shaders/matterjet.glsl @@ -24,6 +24,8 @@ uniform sampler2D depthSampler;// the depth map of the camera uniform float time; +uniform mat4 inverseRotation; + #include "./utils/camera.glsl"; #include "./utils/object.glsl"; @@ -36,79 +38,189 @@ uniform float time; #include "./utils/removeAxialTilt.glsl"; -// from https://www.shadertoy.com/view/MtcXWr -bool rayIntersectCone(vec3 rayOrigin, vec3 rayDir, vec3 tipPosition, vec3 orientation, float coneAngle, out float t1, out float t2) { - vec3 co = rayOrigin - tipPosition; - - float a = dot(rayDir, orientation)*dot(rayDir, orientation) - coneAngle*coneAngle; - float b = 2. * (dot(rayDir, orientation)*dot(co, orientation) - dot(rayDir, co)*coneAngle*coneAngle); - float c = dot(co, orientation)*dot(co, orientation) - dot(co, co)*coneAngle*coneAngle; - - float det = b*b - 4.*a*c; - if (det < 0.) return false; - - det = sqrt(det); - t1 = (-b - det) / (2. * a); - t2 = (-b + det) / (2. * a); - - // This is a bit messy; there ought to be a more elegant solution. - float t = t1; - if (t < 0. || t2 > 0. && t2 < t) t = t2; - if (t < 0.) return false; - - vec3 cp = rayOrigin + t*rayDir - tipPosition; - float h = dot(cp, orientation); - - vec3 n = normalize(cp * dot(orientation, cp) / dot(cp, cp) - orientation); - - return true; +float sdSpiral(vec3 p, float coneTheta, float frequency) { + // Y-axis is now the spiral direction + float spiralPos = p.y; // Using Y instead of Z + float radius = spiralPos * tan(coneTheta); + float theta = frequency * spiralPos - sign(spiralPos) * time * 20.0; + + // Spiral now wraps around Y-axis + vec3 spiralPoint = vec3( + radius * cos(theta), + spiralPos, // Y position is our spiral progress + radius * sin(theta) // Z component completes the circular motion + ); + + return length(p - spiralPoint); } -// see https://www.shadertoy.com/view/tslcW4 -const float a=1.0; -const float b=.1759; -const float PI=3.14159265359; - -float spiralSDF(float theta, float radius) { +float spiralDensity(vec3 p, float coneTheta, float coneHeight) { + float frequency = 0.1; + float dist = sdSpiral(p, coneTheta, frequency); + + float heightFraction = abs(p.y) / coneHeight; - float t=theta; - // t=(t+PI)/(2.*PI); - float r=radius; - - float n=(log(r/a)/b-t)/(2.*PI); - - // Cap the spiral - // float nm = (log(0.11)/b-t)/(2.0*PI); - // n = min(n,nm); - // return (n+1.0)/100.0; - float upper_r=a*exp(b*(t+2.*PI*ceil(n))); - float lower_r=a*exp(b*(t+2.*PI*floor(n))); - // float lower_r = 0.0; - - return min(abs(upper_r-r), abs(r-lower_r)); -} - -float spiralDensity(vec3 pointOnCone, vec3 coneAxis, float coneMaxHeight) { - // Then we rotate that point so that we eliminate the axial tilt of the star from the equation - vec3 pointOnYCone = removeAxialTilt(pointOnCone, coneAxis); - - vec2 pointOnXZPlane = vec2(pointOnYCone.x, pointOnYCone.z); - float theta = atan(pointOnXZPlane.y, pointOnXZPlane.x) + 3.14 * min(0.0, sign(dot(pointOnCone, coneAxis))); - float heightFraction = abs(pointOnYCone.y) / coneMaxHeight; + dist /= heightFraction; float density = 1.0; - // smoothstep fadeout when the height is too much (outside of cone) + density *= exp(-0.1 * dist * dist); + density *= 1.0 - smoothstep(0.0, 1.0, heightFraction); + + return density; +} - float d = spiralSDF(theta + time, 0.2 + sqrt(heightFraction) / 2.0) / (0.3 + heightFraction * 2.0); - //d = pow(d, 4.0); - - density *= smoothstep(0.6, 1.0, pow(1.0 - d, 8.0)) * 2.0; //smoothstep(0.85, 1.0, 1.0 - d) * 2.0; +// from https://www.shadertoy.com/view/MtcXWr +// Returns true if the ray intersects the closed cone. +// If so, 't' is set to the first hit along the ray (entry if outside, exit if inside) +// and 'distTrough' is set to the chord length of the ray inside the cone. +bool rayIntersectCone(vec3 rayOrigin, vec3 rayDir, + float coneHeight, float cosTheta, + out float t, out float distTrough) +{ + vec3 coneUp = vec3(0.0, 1.0, 0.0); + + float cosTheta2 = cosTheta * cosTheta; + + // Compute tangent once. + float sinTheta = sqrt(max(0.0, 1.0 - cosTheta2)); + float tanTheta = sinTheta / cosTheta; + + // --- Determine if ray origin is inside the cone volume --- + bool inside = false; + { + vec3 v = rayOrigin; + float h = v.y; + if (h >= 0.0 && h <= coneHeight) { + float rAtH = h * tanTheta; + float d = length(v - coneUp * h); + if (d <= rAtH) + inside = true; + } + } + + // --- Collect candidate intersection t's --- + // We will add valid intersections from the lateral surface and the base. + float tCandidates[3]; + int count = 0; + + // Intersection with the infinite cone's lateral surface. + vec3 co = rayOrigin; + float A = rayDir.y * rayDir.y - cosTheta2; + float B = 2.0 * (rayDir.y * co.y - dot(rayDir, co) * cosTheta2); + float C = co.y * co.y - dot(co, co) * cosTheta2; + + float det = B * B - 4.0 * A * C; + if (det >= 0.0) + { + float sqrtDet = sqrt(det); + float t1 = (-B - sqrtDet) / (2.0 * A); + float t2 = (-B + sqrtDet) / (2.0 * A); + + // Check t1 for validity (only consider if t>=0) + if (t1 >= 0.0) + { + vec3 cp1 = rayOrigin + t1 * rayDir; + float h1 = cp1.y; + if (h1 >= 0.0 && h1 <= coneHeight) + tCandidates[count++] = t1; + } + // Check t2 + if (t2 >= 0.0) + { + vec3 cp2 = rayOrigin + t2 * rayDir; + float h2 = cp2.y; + if (h2 >= 0.0 && h2 <= coneHeight) + tCandidates[count++] = t2; + } + } + + // Intersection with the base plane. + vec3 baseCenter = coneUp * coneHeight; + float denom = rayDir.y; + if (abs(denom) > 1e-6) + { + float tBase = (baseCenter - rayOrigin).y / denom; + if (tBase >= 0.0) + { + vec3 hitPoint = rayOrigin + tBase * rayDir; + // Check if hitPoint is within the circular base. + float baseRadius = coneHeight * tanTheta; + if (length(hitPoint - baseCenter) <= baseRadius) + tCandidates[count++] = tBase; + } + } + + // If no valid intersections were found, return false. + if (count == 0) + return false; + + // --- Sort the candidate intersections in increasing order --- + for (int i = 0; i < count - 1; i++) + { + for (int j = i + 1; j < count; j++) + { + if (tCandidates[j] < tCandidates[i]) + { + float tmp = tCandidates[i]; + tCandidates[i] = tCandidates[j]; + tCandidates[j] = tmp; + } + } + } + + // --- Determine entry and exit t values --- + float tEntry, tExit; + if (inside) + { + // If the ray origin is inside, entry is at t = 0 and the exit is the first candidate. + tEntry = 0.0; + tExit = tCandidates[0]; + } + else + { + // Outside: entry is the first hit, exit is the second (if present). + tEntry = tCandidates[0]; + if (count > 1) + tExit = tCandidates[1]; + else + tExit = tEntry; // Tangential hit: chord length is zero. + } + + // Set the outputs. + // We return tEntry as the “hit” point (for an outside ray, where the ray first enters the cone; + // for an inside ray, tEntry=0 is the starting point so we return the exit instead). + if (inside) + t = 0.0; + else + t = tEntry; + + distTrough = tExit - tEntry; + + return true; +} - //density *= d * 500.0; +vec3 rayMarchSpiral(vec3 rayOrigin, vec3 rayDir, float distThrough, int nbSteps, float coneTheta, float coneHeight, out float transmittance) { + vec3 col = vec3(0.0); + + transmittance = 1.0; + + float stepSize = distThrough / float(nbSteps); + + for(int i = 0; i < nbSteps; i++) { + vec3 p = rayOrigin + float(i) * stepSize * rayDir; + + float density = spiralDensity(p, coneTheta * 0.2, coneHeight); + + vec3 emission = 10.0 * vec3(0.3, 0.6, 1.0) * density; + float absorption = 0.2 * density; + + transmittance *= exp(-absorption * stepSize); + col += emission * transmittance * stepSize; + } - return density; + return col; } void main() { @@ -123,29 +235,42 @@ void main() { vec3 rayDir = normalize(pixelWorldPosition - camera_position);// normalized direction of the ray - vec4 finalColor = screenColor; + float scaling_factor = object_radius * 10000.0; - const float jetHeight = 10000000e3; - const vec3 jetColor = vec3(0.5, 0.5, 1.0); + // move in object's local space to simplify the calculations + vec3 rayOriginLocalSpace = mat3(inverseRotation) * (camera_position - object_position) / scaling_factor; + vec3 rayDirLocalSpace = mat3(inverseRotation) * rayDir; + float coneTheta = 0.2; + float coneHeight = 100.0; - float t1, t2; - if (rayIntersectCone(camera_position, rayDir, object_position, object_rotationAxis, 0.95, t1, t2)) { - if (t2 > 0.0 && t2 < maximumDistance) { - vec3 jetPointPosition2 = camera_position + t2 * rayDir - object_position; + vec3 color = screenColor.rgb; + float finalAlpha = screenColor.a; - float density2 = spiralDensity(jetPointPosition2, object_rotationAxis, jetHeight); + float t, distThrough; + if(rayIntersectCone(rayOriginLocalSpace, rayDirLocalSpace, coneHeight, cos(coneTheta), t, distThrough) && t * scaling_factor < maximumDistance) { + vec3 startPoint = rayOriginLocalSpace + t * rayDirLocalSpace; - finalColor.rgb = mix(finalColor.rgb, jetColor, density2); - } - if (t1 > 0.0 && t1 < maximumDistance) { - vec3 jetPointPosition1 = camera_position + t1 * rayDir - object_position; + float transmittance; + vec3 jetColor = rayMarchSpiral(startPoint, rayDirLocalSpace, distThrough, 100, coneTheta, coneHeight, transmittance); - float density1 = spiralDensity(jetPointPosition1, object_rotationAxis, jetHeight); + color = mix(jetColor, color, transmittance); + finalAlpha = max(transmittance, finalAlpha); + } - finalColor.rgb = mix(finalColor.rgb, jetColor, density1); - } + // flip the coordinates to display the other cone + rayOriginLocalSpace *= -1.0; + rayDirLocalSpace *= -1.0; + + if(rayIntersectCone(rayOriginLocalSpace, rayDirLocalSpace, coneHeight, cos(coneTheta), t, distThrough) && t * scaling_factor < maximumDistance) { + vec3 startPoint = rayOriginLocalSpace + t * rayDirLocalSpace; + + float transmittance; + vec3 jetColor = rayMarchSpiral(startPoint, rayDirLocalSpace, distThrough, 100, coneTheta, coneHeight, transmittance); + + color = mix(jetColor, color, transmittance); + finalAlpha = max(transmittance, finalAlpha); } - gl_FragColor = finalColor;// displaying the final color + gl_FragColor = vec4(color, finalAlpha); } \ No newline at end of file diff --git a/src/ts/playgrounds/neutronStar.ts b/src/ts/playgrounds/neutronStar.ts index ba14ec2be..f6dc964b6 100644 --- a/src/ts/playgrounds/neutronStar.ts +++ b/src/ts/playgrounds/neutronStar.ts @@ -20,12 +20,15 @@ import { Scene } from "@babylonjs/core/scene"; import { Vector3 } from "@babylonjs/core/Maths/math.vector"; import { enablePhysics } from "./utils"; import { DefaultControls } from "../defaultControls/defaultControls"; -import { Assets } from "../assets/assets"; import { NeutronStar } from "../stellarObjects/neutronStar/neutronStar"; import { newSeededNeutronStarModel } from "../stellarObjects/neutronStar/neutronStarModelGenerator"; import { MatterJetPostProcess } from "../postProcesses/matterJetPostProcess"; import { VolumetricLight } from "../volumetricLight/volumetricLight"; import { translate } from "../uberCore/transforms/basicTransform"; +import { Textures } from "../assets/textures"; +import { AssetsManager } from "@babylonjs/core"; +import { LensFlarePostProcess } from "../postProcesses/lensFlarePostProcess"; +import { getRgbFromTemperature } from "../utils/specrend"; export async function createNeutronStarScene(engine: AbstractEngine): Promise { const scene = new Scene(engine); @@ -33,7 +36,9 @@ export async function createNeutronStarScene(engine: AbstractEngine): Promise { diff --git a/src/ts/postProcesses/matterJetPostProcess.ts b/src/ts/postProcesses/matterJetPostProcess.ts index 09114c5f4..d86ca060b 100644 --- a/src/ts/postProcesses/matterJetPostProcess.ts +++ b/src/ts/postProcesses/matterJetPostProcess.ts @@ -27,11 +27,11 @@ import { Constants } from "@babylonjs/core/Engines/constants"; import { Camera } from "@babylonjs/core/Cameras/camera"; import { Scene } from "@babylonjs/core/scene"; import { TransformNode } from "@babylonjs/core/Meshes/transformNode"; +import { Matrix } from "@babylonjs/core/Maths/math.vector"; export type MatterJetUniforms = { - // the rotation period in seconds of the matter jet - rotationPeriod: number; - time: number; + elapsedSeconds: number; + inverseRotation: Matrix; }; /** @@ -49,14 +49,13 @@ export class MatterJetPostProcess extends PostProcess implements UpdatablePostPr } const settings: MatterJetUniforms = { - rotationPeriod: 1.5, - time: 0 + elapsedSeconds: 0, + inverseRotation: Matrix.Identity() }; const MatterJetUniformNames = { TIME: "time", - ROTATION_PERIOD: "rotationPeriod", - ROTATION_AXIS: "rotationAxis" + INVERSE_ROTATION: "inverseRotation" }; const uniforms: string[] = [ @@ -95,18 +94,18 @@ export class MatterJetPostProcess extends PostProcess implements UpdatablePostPr setCameraUniforms(effect, this.activeCamera); setObjectUniforms(effect, stellarTransform, boundingRadius); - effect.setFloat( - MatterJetUniformNames.TIME, - this.matterJetUniforms.time % (this.matterJetUniforms.rotationPeriod * 10000) - ); - effect.setFloat(MatterJetUniformNames.ROTATION_PERIOD, this.matterJetUniforms.rotationPeriod); - effect.setVector3(MatterJetUniformNames.ROTATION_AXIS, stellarTransform.up); + effect.setFloat(MatterJetUniformNames.TIME, this.matterJetUniforms.elapsedSeconds % 10000); + + stellarTransform.getWorldMatrix().getRotationMatrixToRef(this.matterJetUniforms.inverseRotation); + this.matterJetUniforms.inverseRotation.transposeToRef(this.matterJetUniforms.inverseRotation); + + effect.setMatrix(MatterJetUniformNames.INVERSE_ROTATION, this.matterJetUniforms.inverseRotation); setSamplerUniforms(effect, this.activeCamera, scene); }); } - public update(deltaTime: number): void { - this.matterJetUniforms.time += deltaTime; + public update(deltaSeconds: number): void { + this.matterJetUniforms.elapsedSeconds += deltaSeconds; } } diff --git a/src/ts/stellarObjects/star/starMaterialLut.ts b/src/ts/stellarObjects/star/starMaterialLut.ts index 7ea9e1582..573b81521 100644 --- a/src/ts/stellarObjects/star/starMaterialLut.ts +++ b/src/ts/stellarObjects/star/starMaterialLut.ts @@ -27,7 +27,7 @@ export class StarMaterialLut { Effect.ShadersStore["starLutFragmentShader"] = lutFragment; } - this.lut = new ProceduralTexture(`StarMaterialLut`, 4096, "starLut", scene, null, true, false); + this.lut = new ProceduralTexture(`StarMaterialLut`, 256, "starLut", scene, null, true, false); this.lut.refreshRate = 0; } From fdc02d54a7a3fb108c04c66b88b4e9870a8e4be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9lemy?= <31370477+BarthPaleologue@users.noreply.github.com> Date: Sun, 16 Feb 2025 17:23:55 +0100 Subject: [PATCH 4/8] Parametrize dipole tilt for neutron star's matter jets --- src/shaders/matterjet.glsl | 4 +++- src/ts/playgrounds/neutronStar.ts | 7 ++++++- src/ts/postProcesses/matterJetPostProcess.ts | 13 +++++++++---- src/ts/postProcesses/postProcessManager.ts | 1 + .../neutronStar/neutronStarModel.ts | 12 +++++++++--- .../neutronStar/neutronStarModelGenerator.ts | 17 ++++++++++------- src/ts/utils/generationSteps.ts | 4 +++- 7 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/shaders/matterjet.glsl b/src/shaders/matterjet.glsl index f40d23cd8..2af1588c1 100644 --- a/src/shaders/matterjet.glsl +++ b/src/shaders/matterjet.glsl @@ -26,6 +26,8 @@ uniform float time; uniform mat4 inverseRotation; +uniform float dipoleTilt; + #include "./utils/camera.glsl"; #include "./utils/object.glsl"; @@ -241,7 +243,7 @@ void main() { vec3 rayOriginLocalSpace = mat3(inverseRotation) * (camera_position - object_position) / scaling_factor; vec3 rayDirLocalSpace = mat3(inverseRotation) * rayDir; - float coneTheta = 0.2; + float coneTheta = dipoleTilt; float coneHeight = 100.0; vec3 color = screenColor.rgb; diff --git a/src/ts/playgrounds/neutronStar.ts b/src/ts/playgrounds/neutronStar.ts index f6dc964b6..97780efa9 100644 --- a/src/ts/playgrounds/neutronStar.ts +++ b/src/ts/playgrounds/neutronStar.ts @@ -57,7 +57,12 @@ export async function createNeutronStarScene(engine: AbstractEngine): Promise. import { HasSeed } from "../../architecture/hasSeed"; -import { OrbitalObjectModelBase } from "../../architecture/orbitalObjectModelBase"; +import { CelestialBodyModelBase } from "../../architecture/orbitalObjectModelBase"; import { OrbitalObjectType } from "../../architecture/orbitalObjectType"; import { RingsModel } from "../../rings/ringsModel"; -export type NeutronStarModel = OrbitalObjectModelBase & +export type NeutronStarModel = CelestialBodyModelBase & HasSeed & { readonly blackBodyTemperature: number; - readonly radius: number; + + /** + * The angle between the magnetic dipole axis and the rotation axis. + * If the magnetic field were perfectly aligned with the rotation axis, this angle would be 0. + */ + readonly dipoleTilt: number; + readonly rings: RingsModel | null; }; diff --git a/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts b/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts index ac912b13b..0303bb7f9 100644 --- a/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts +++ b/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts @@ -16,7 +16,7 @@ // along with this program. If not, see . import { Orbit } from "../../orbit/orbit"; -import { normalRandom, randRangeInt, uniformRandBool } from "extended-random"; +import { normalRandom, randRange, randRangeInt, uniformRandBool } from "extended-random"; import { clamp } from "../../utils/math"; import { newSeededRingsModel } from "../../rings/ringsModel"; import { GenerationSteps } from "../../utils/generationSteps"; @@ -40,6 +40,8 @@ export function newSeededNeutronStarModel( const radius = clamp(normalRandom(10e3, 1e3, rng, GenerationSteps.RADIUS), 2e3, 50e3); + const dipoleTilt = randRange(-Math.PI / 3, Math.PI / 3, rng, GenerationSteps.DIPOLE_TILT); + // Todo: do not hardcode const orbitRadius = rng(GenerationSteps.ORBIT) * 5000000e3; @@ -58,15 +60,16 @@ export function newSeededNeutronStarModel( const rings = uniformRandBool(ringProportion, rng, GenerationSteps.RINGS) ? newSeededRingsModel(rng) : null; return { - name: name, - seed: seed, type: OrbitalObjectType.NEUTRON_STAR, - blackBodyTemperature: blackBodyTemperature, + name, + seed, + blackBodyTemperature, + dipoleTilt, mass, siderealDaySeconds, axialTilt, - radius: radius, - orbit: orbit, - rings: rings + radius, + orbit, + rings }; } diff --git a/src/ts/utils/generationSteps.ts b/src/ts/utils/generationSteps.ts index 4f64ca2bb..e2477c638 100644 --- a/src/ts/utils/generationSteps.ts +++ b/src/ts/utils/generationSteps.ts @@ -31,5 +31,7 @@ export const enum GenerationSteps { PRESSURE = 1800, WATER_AMOUNT = 1700, - TERRAIN = 1500 + TERRAIN = 1500, + + DIPOLE_TILT = 1300 } From 47efce7ee6e010a02137c38dfb9841e20233bf53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9lemy?= <31370477+BarthPaleologue@users.noreply.github.com> Date: Sun, 16 Feb 2025 18:08:28 +0100 Subject: [PATCH 5/8] Use observation data to determine neutron's stars rotation periods it's quite damn fast --- src/shaders/matterjet.glsl | 2 +- src/ts/playgrounds/neutronStar.ts | 4 +++- .../neutronStar/neutronStarModelGenerator.ts | 16 +++++++++++++--- src/ts/utils/generationSteps.ts | 4 +++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/shaders/matterjet.glsl b/src/shaders/matterjet.glsl index 2af1588c1..bebc8c650 100644 --- a/src/shaders/matterjet.glsl +++ b/src/shaders/matterjet.glsl @@ -44,7 +44,7 @@ float sdSpiral(vec3 p, float coneTheta, float frequency) { // Y-axis is now the spiral direction float spiralPos = p.y; // Using Y instead of Z float radius = spiralPos * tan(coneTheta); - float theta = frequency * spiralPos - sign(spiralPos) * time * 20.0; + float theta = frequency * spiralPos; // Spiral now wraps around Y-axis vec3 spiralPoint = vec3( diff --git a/src/ts/playgrounds/neutronStar.ts b/src/ts/playgrounds/neutronStar.ts index 97780efa9..8aabd4086 100644 --- a/src/ts/playgrounds/neutronStar.ts +++ b/src/ts/playgrounds/neutronStar.ts @@ -26,7 +26,7 @@ import { MatterJetPostProcess } from "../postProcesses/matterJetPostProcess"; import { VolumetricLight } from "../volumetricLight/volumetricLight"; import { translate } from "../uberCore/transforms/basicTransform"; import { Textures } from "../assets/textures"; -import { AssetsManager } from "@babylonjs/core"; +import { AssetsManager, Axis } from "@babylonjs/core"; import { LensFlarePostProcess } from "../postProcesses/lensFlarePostProcess"; import { getRgbFromTemperature } from "../utils/specrend"; @@ -80,6 +80,8 @@ export async function createNeutronStarScene(engine: AbstractEngine): Promise Date: Mon, 17 Feb 2025 17:30:53 +0100 Subject: [PATCH 6/8] Make neutron star mass more in line with reality In 2.0, a better calculation will be introduced --- src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts b/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts index 41720a027..4c0129aff 100644 --- a/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts +++ b/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts @@ -40,7 +40,7 @@ export function newSeededNeutronStarModel( ): NeutronStarModel { const rng = getRngFromSeed(seed); - const mass = 1000; + const mass = 1.9885e30; //TODO: compute mass from physical properties // https://arxiv.org/pdf/2402.14030 and https://en.wikipedia.org/wiki/Neutron_star#:~:text=Because%20it%20has%20only%20a,1.4%20ms%20to%2030%20s. const siderealDaySeconds = clamp(1.4e-3, 30, normalRandom(0.5e-2, 5e-3, rng, GenerationSteps.SIDEREAL_DAY_SECONDS)); From 5d9f5e5abe6c10ad90711b370f388ec6ac0fa380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9lemy?= <31370477+BarthPaleologue@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:40:35 +0100 Subject: [PATCH 7/8] Fix cone aperture The margin to avoid cone surface clamping was way too much --- src/shaders/matterjet.glsl | 2 +- src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shaders/matterjet.glsl b/src/shaders/matterjet.glsl index bebc8c650..4730c02d5 100644 --- a/src/shaders/matterjet.glsl +++ b/src/shaders/matterjet.glsl @@ -213,7 +213,7 @@ vec3 rayMarchSpiral(vec3 rayOrigin, vec3 rayDir, float distThrough, int nbSteps, for(int i = 0; i < nbSteps; i++) { vec3 p = rayOrigin + float(i) * stepSize * rayDir; - float density = spiralDensity(p, coneTheta * 0.2, coneHeight); + float density = spiralDensity(p, coneTheta * 0.7, coneHeight); vec3 emission = 10.0 * vec3(0.3, 0.6, 1.0) * density; float absorption = 0.2 * density; diff --git a/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts b/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts index 4c0129aff..804b11417 100644 --- a/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts +++ b/src/ts/stellarObjects/neutronStar/neutronStarModelGenerator.ts @@ -50,7 +50,7 @@ export function newSeededNeutronStarModel( const radius = clamp(normalRandom(10e3, 1e3, rng, GenerationSteps.RADIUS), 2e3, 50e3); - const dipoleTilt = randRange(-Math.PI / 3, Math.PI / 3, rng, GenerationSteps.DIPOLE_TILT); + const dipoleTilt = randRange(-Math.PI / 6, Math.PI / 6, rng, GenerationSteps.DIPOLE_TILT); // Todo: do not hardcode const orbitRadius = rng(GenerationSteps.ORBIT) * 5000000e3; From 832c7b2ac815c2de78fe71c1a6d3ca45bba3b103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barth=C3=A9lemy?= <31370477+BarthPaleologue@users.noreply.github.com> Date: Mon, 17 Feb 2025 17:55:15 +0100 Subject: [PATCH 8/8] tweak visual Make the jet thicker --- src/shaders/matterjet.glsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shaders/matterjet.glsl b/src/shaders/matterjet.glsl index 4730c02d5..a4a4f0e31 100644 --- a/src/shaders/matterjet.glsl +++ b/src/shaders/matterjet.glsl @@ -66,7 +66,7 @@ float spiralDensity(vec3 p, float coneTheta, float coneHeight) { float density = 1.0; - density *= exp(-0.1 * dist * dist); + density *= exp(-0.02 * dist * dist); density *= 1.0 - smoothstep(0.0, 1.0, heightFraction); @@ -215,7 +215,7 @@ vec3 rayMarchSpiral(vec3 rayOrigin, vec3 rayDir, float distThrough, int nbSteps, float density = spiralDensity(p, coneTheta * 0.7, coneHeight); - vec3 emission = 10.0 * vec3(0.3, 0.6, 1.0) * density; + vec3 emission = 3.0 * vec3(0.3, 0.6, 1.0) * density; float absorption = 0.2 * density; transmittance *= exp(-absorption * stepSize);