bättre ringworld

This commit is contained in:
2026-11-03 20:32:59 +01:00
parent f447fe8e51
commit 6ea397a9e5
39 changed files with 2141 additions and 54 deletions

View File

@@ -14,6 +14,7 @@
| `saturn` | Saturnus |
| `uranus` | Uranus |
| `neptune` | Neptunus |
| `ring-world` | Ring World (konstruerad värld) |
`sun` är Solen. `moon` definieras som en satellit under Jordens `moons` och räknas inte som en av de åtta planeterna. Id:n är stabila och unika inom respektive solsystem, oberoende av ordningen i JSON-listan.
@@ -25,7 +26,7 @@ Projektets `../config.json` väljer startmålet:
"startup": {
"systemId": "sol",
"planetId": "earth",
"planetHotkeys": ["mercury", "venus", "earth", "mars", "jupiter", "saturn", "uranus", "neptune"],
"planetHotkeys": ["mercury", "venus", "earth", "mars", "jupiter", "saturn", "uranus", "neptune", "ring-world"],
"distanceInRadii": 3.0,
"longitudeDegrees": 20,
"latitudeDegrees": 20,
@@ -359,3 +360,48 @@ dotnet run -c Release --project src/Universe.App -- --scene-report
```
`graphics-report/` bredvid den byggda appen får PNG-bilder. Det första kommandot kontrollerar den tätaste asteroidmeshen, giltiga shaderresultat, två solars glöd vid 1 respektive 30 AU samt att framförliggande djup släcker glöden. Det andra renderar den riktiga Saturnusscenen, en närliggande ringsten och Solen från 30 AU genom hela renderingskedjan.
## Ring World
[ring-world.json](ring-world.json) definierar en separat beboelig ring i solsystemet. Den läses via systemets `planetFiles` i `solar-systems.json`; sökvägarna är relativa till samma katalog. Tryck **9** för översikten, **F2** för en vy över insidans land, **F6** för havet och **F7** för molnlagret. Välj sedan **P → Play** för att falla ned och gå. Startmålet kan också sättas till `ring-world` i `config.json`.
```json
"radiusMeters": 2500000,
"ringWorld": {
"diameterMeters": 5000000,
"widthMeters": 500000,
"thicknessMeters": 50000,
"wallThicknessMeters": 5000,
"gravityMetersPerSecondSquared": 9.81,
"proceduralSurface": {
"metersPerTexel": 1000,
"maxTextureSize": 16384,
"continentScaleMeters": 180000,
"biomeScaleMeters": 90000,
"mountainScaleMeters": 30000,
"oceanCoverage": 0.55
}
}
```
En svensk mil är 10 km: ringen är **50 mil bred, 500 mil i diameter och 5 mil tjock**. Diametern avser den invändiga nollnivån. `radiusMeters` ska vara halva `diameterMeters`. Bredden är den fria terrängytan mellan väggarnas insidor; väggarnas bredd tillkommer. Stommen bygger utåt från nollnivån. Berg, moln och atmosfär bygger inåt mot ringens centrum. Båda kantväggarna har alltid höjden `atmosphere.heightMeters` (Jordens befintliga 220 000 m), så det finns inget separat vägghöjdsfält att hålla synkroniserat.
Ringens axel är lokal Y. `ringWorld.proceduralSurface` genererar egna färg-, höjd- och vattenkartor från planetens `seed`. Samma seed och inställningar ger samma landskap vid varje start; ändra `seed` och starta om för en ny värld. Kontinenter, kustlinjer, berg, gröna skogar, gulaktiga torra områden, sand och snö beräknas i cylindriska koordinater med fysisk skala i meter. Kartorna omsluter ringen utan skarv och delar geografi med terräng, kollision och vegetation. Molnen använder fysisk 3D-noise. Markmaterial, växtmodeller och atmosfärens färg, täthet och sammansättning återanvänds från Jorden; jordens geografiska kartor används inte.
`metersPerTexel` anger önskad geografisk upplösning åt båda hållen. Standardringen får **15 708 × 500 texlar**, cirka 1 km per texel både runt och tvärs över ringen. Närbilder kompletteras med befintlig terräng-noise och markmaterial i meterskala. `maxTextureSize` begränsar längsta bildsidan (64–16 384); kartorna begränsas dessutom till 8 388 608 texlar. Vid större ringar eller högre önskad upplösning sänks upplösningen gemensamt åt båda hållen så att proportionerna bevaras. Använd ett lägre `maxTextureSize` om grafikkortet inte stöder 16k-texturer.
`continentScaleMeters`, `biomeScaleMeters` och `mountainScaleMeters` styr landskapets fysiska storlek oberoende av ringens bredd. `oceanCoverage` anger ungefärlig andel vatten (0–1); övergången till havsbotten är mjuk. `terrain.heightmapMaxMeters` styr maximal höjd, `terrain.proceduralNoiseAmplitudeMeters` finare höjdvariation och `ocean.depthMeters` havsdjup. Höjdintervallet måste omfatta havsnivån och ha ett maximum ovanför den. Ta bort externa `surfaceTexture`, `terrain.heightmap` och `terrain.regionalHeight` när generatorn används. Havsmasken genereras direkt och kräver inte `ocean.maskFromSurfaceTexture`. Utan `proceduralSurface` kan ringvärldar fortfarande använda egna bildkartor.
Gravitationen är ett konfigurerbart artificiellt fält som pekar radiellt utåt från ringaxeln mot den invändiga cirkeln. Spelarens uppåtriktning pekar mot ringaxeln, oberoende av terrängens lutning och avståndet till kantväggarna. Gång, hopp och hukning använder denna cylindriska referensram; marknormalen används för kollisionsavstånd och bedömning av gångbara sluttningar. Kantväggarna är kollisionshinder och drar inte spelaren i sidled. Spelaren följer ringens omlopp och axelrotation. Mitten är öppen och kan flygas igenom; svept kollision stoppar även snabba passager genom stommen. Havet har en kolliderbar botten, som på Jorden; simfysik och flytkraft ingår inte.
Spelarens position sparas i ringens lokala koordinater så att avrundningen i universumets stora koordinater inte ackumuleras under omlopp och rotation. Justering av ögonhöjd sker längs gravitationen, vilket förhindrar att upprepade markkorrigeringar flyttar en stillastående spelare i sidled på ojämn terräng. Terrängens LOD återanvänder avståndsberäkningar vid små kameraförflyttningar och använder en skärmberoende detaljnivå anpassad för den konkava ringens stora synliga yta.
Ringens atmosfär och moln använder cylindriska volymer begränsade av kanterna. Havet har animerade normalvågor, Fresnelreflektion och solljusglitter. Det första ringmaterialet använder analytisk himmelsreflektion; Jordens skärmrymdsreflektioner används inte på ringen. Det artificiella gravitationsfältet är oberoende av rotationshastigheten och beräknar inte centrifugalkraft eller Corioliseffekter.
Terräng och hav tar emot trädens kaskadskuggor, styrda av `vegetation.shadowDistancesMeters`, `shadowResolution`, `shadowSoftness` och `shadowBiasMeters`. Stommen och båda kantväggarna skymmer direkt solljus enligt sina faktiska cylindriska volymer, inklusive väggarnas ovan- och utsidor. Mark, vatten, växter och atmosfär använder samma skugggeometri i ringens lokala koordinater, så skuggorna följer solen när ringen roterar. Molnskuggorna integrerar tätheten längs solstrålen genom det invändiga molnlagret. De följer de synliga molnens seed, vind och täckning; `clouds.shadowStrength` styr styrkan (0 stänger av molnskuggorna).
Verifiera med `dotnet test tests/Universe.Tests/Universe.Tests.csproj -c Release -m:1` och `dotnet run -c Release --project src/Universe.App -- --ring-world-report`. Grafikrapporten kör de riktiga OpenGL-shadrarna och sparar översikt, insida, mark, fötter och hav i `graphics-report/ring-world/` bredvid appen. `surface-map.png` visar hela färgkartan som fyra efterföljande remsor med bevarade proportioner. Rapporten skriver ut faktisk upplösning i meter och kontrollerar även att vegetation laddas och att markdjup finns under spelaren.
`performance.txt` redovisar median och 95:e percentilen för bildrutetid, kostnad per renderingspass samt en separat mätning med spelare, trädkollisioner, omlopp och rotation aktiva. Den mäter också oavsiktlig rörelse relativt ringen; högst 5 mm tillåts. Testsviten kontrollerar dessutom 60 sekunders stillastående på ojämn mark med både frusen och rörlig ring. FPS-värdena gäller aktuell dator, upplösning och inläst testscen.
Grafikrapporten testar även solstrålar över och under båda väggkanterna, stommens in- och utsida samt det öppna centrumet direkt i produktionsshadern. Den kontrollerar molnskuggornas täckning, vindrörelse och koordinattransformation, och sparar jämförelsebilderna `tree-shadows-on/off.png` och `cloud-shadows-on/off.png`. Trädjämförelsen isolerar skuggorna på ringens ytor; molnjämförelsen använder en kontrollerad mulen himmel med samma träd och terräng.

View File

@@ -0,0 +1,164 @@
{
"id": "ring-world",
"name": "Ring World",
"hostStarId": "sun",
"seed": 110,
"description": "Konstruerad ringvärld med beboelig insida, hav och atmosfär.",
"radiusMeters": 2500000,
"axialTiltDegrees": 35,
"rotationDegrees": 0,
"orbit": {
"semiMajorAxisAu": 1.15,
"eccentricity": 0,
"phaseDegrees": 155,
"inclinationDegrees": 5
},
"surfaceType": "terrestrial",
"surfaceColor": [
0.25,
0.39,
0.2
],
"materials": {
"enabled": true,
"flatMaterial": "leafy-grass",
"slopeMaterial": "rock-ground",
"scaleMeters": 8,
"fadeDistanceMeters": 2000,
"strength": 0.8
},
"clouds": {
"enabled": true,
"densityTextureStrength": 0.0,
"baseAltitudeMeters": 2100,
"thicknessMeters": 4300,
"coverage": 0.4,
"density": 0.85,
"shapeScaleMeters": 18000,
"detailScaleMeters": 2400,
"erosion": 0.45,
"cloudType": 0.9,
"windSpeedMetersPerSecond": 18,
"windDirectionDegrees": 25,
"extinctionPerMeter": 0.0007,
"scatteringAlbedo": 0.96,
"anisotropy": 0.62,
"silverLiningStrength": 0.7,
"ambientStrength": 0.6,
"sunlightStrength": 1.0,
"sunsetStrength": 1.4,
"shadowStrength": 0.7,
"viewSteps": 48,
"lightSteps": 4,
"resolutionScale": 0.4,
"maxViewDistanceMeters": 220000
},
"ocean": {
"enabled": true,
"screenSpaceReflections": {
"enabled": false,
"reflectSky": true,
"strength": 1,
"resolutionScale": 0.5,
"maxSteps": 48,
"stridePixels": 2,
"maxDistanceMeters": 3000,
"maxSurfaceDistanceMeters": 4000,
"thicknessMeters": 1.5,
"normalBiasMeters": 0.12,
"edgeFade": 0.07
},
"maskFromSurfaceTexture": false,
"seaLevelMeters": 0,
"depthMeters": 4000,
"waveAmplitudeMeters": 0.6,
"waveLengthMeters": 36,
"windDirectionDegrees": 35,
"windSpreadDegrees": 110,
"waveSpeed": 1.0,
"choppiness": 0.4,
"rippleStrength": 0.65,
"waveGeometryDistanceMeters": 4000,
"whitecapThreshold": 0.22,
"roughness": 0.26,
"reflectionStrength": 1.0,
"refractionStrength": 0.018,
"foamStrength": 0.3,
"deepColor": [
0.008,
0.07,
0.14
],
"shallowColor": [
0.025,
0.28,
0.24
],
"absorption": [
0.12,
0.045,
0.022
]
},
"terrain": {
"heightmapMinMeters": 0,
"heightmapMaxMeters": 8000,
"heightmapGamma": 1.0,
"proceduralNoiseAmplitudeMeters": 120,
"proceduralNoiseFrequency": 24,
"noiseMask": "heightmapLand",
"landThreshold": 0.02,
"landBlend": 0.04
},
"atmosphere": {
"heightMeters": 220000,
"density": 1.0,
"color": [
0.13,
0.32,
1.0
],
"composition": "Kväve och syre"
},
"moons": [],
"rotationPeriodHours": 24,
"massKg": 1e+23,
"poleLongitudeDegrees": 0,
"vegetation": {
"enabled": true,
"densityMultiplier": 0.7,
"treeDistanceMeters": 2000,
"treeLodMeters": [
40,
150,
400
],
"grassDistanceMeters": 80,
"shrubDistanceMeters": 150,
"affectGroundMaterials": true,
"uploadBudgetMilliseconds": 1,
"shadowResolution": 2048,
"shadowSoftness": 1.0,
"shadowCascadeBlend": 0.2,
"shadowBiasMeters": 0.035,
"shadowDistancesMeters": [
100,
400
]
},
"ringWorld": {
"diameterMeters": 5000000,
"widthMeters": 500000,
"thicknessMeters": 50000,
"wallThicknessMeters": 5000,
"gravityMetersPerSecondSquared": 9.81,
"proceduralSurface": {
"metersPerTexel": 1000,
"maxTextureSize": 16384,
"continentScaleMeters": 180000,
"biomeScaleMeters": 90000,
"mountainScaleMeters": 30000,
"oceanCoverage": 0.55
}
}
}

View File

@@ -54,6 +54,7 @@
"massKg": 1.98847e+30
}
],
"planetFiles": ["ring-world.json"],
"planets": [
{
"id": "mercury",