The V2 shader system is a complete rewrite of the procedural planet and star shaders, designed for increased variety, realism, and physics-grounded visuals.
-
Shared Utility Libraries - Common noise, color, lighting, and seed functions are shared via
#includedirectives, eliminating code duplication. -
Increased Variety - 10 planet shader types (vs 6 in V1) plus improved seed-based variation ensures no two planets look identical.
-
Physics-Grounded - Planet properties (temperature, density, insolation) directly influence appearance in scientifically accurate ways.
-
Higher Realism - Domain-warped noise, proper limb darkening, atmospheric scattering, and detailed surface features.
-
Burning Stars - Stars now show actual flame patterns, granulation, and temperature-accurate colors instead of simple glows.
To avoid conflicts with THREE.js built-in functions and other GLSL definitions, V2 utility functions use a v2_ prefix:
v2_luminance()instead ofluminance()(THREE.js has a built-in luminance function)
When adding new utility functions, always check if the name conflicts with WebGL/THREE.js built-ins.
public/shaders/v2/
├── common/
│ ├── noise.glsl # Simplex, value noise, FBM, domain warping
│ ├── color.glsl # HSV/RGB, temperature-to-color, palettes
│ ├── lighting.glsl # Fresnel, limb darkening, specular, atmosphere
│ └── seed.glsl # Deterministic variation from planet seed
├── planet/
│ ├── planet.vert # Shared vertex shader
│ ├── rocky.frag # Terrestrial worlds with biomes
│ ├── gasGiant.frag # Jupiter/Saturn-like with storms
│ ├── hotJupiter.frag # Tidally locked ultra-hot giants
│ ├── iceGiant.frag # Neptune/Uranus-like
│ ├── subNeptune.frag # Mini-Neptunes with thick haze
│ ├── lavaWorld.frag # Volcanic hellscapes
│ ├── icyWorld.frag # Europa/Enceladus-like frozen moons
│ ├── oceanWorld.frag # Water worlds (NEW)
│ ├── desertWorld.frag # Arid/dune worlds (NEW)
│ └── tidallyLocked.frag # Day/night divided worlds (NEW)
└── star/
├── surface.vert
├── surface.frag # Burning star surface with flames
├── corona.vert
└── corona.frag # Outer glow with flare structures
import { shaderService } from '@/services/shaderService';
import { getV2PlanetShaderType, getV2ShaderFileName, getPlanetVertexShader } from '@/utils/planetUniforms';
// Load all shaders (includes V2)
await shaderService.loadShaders();
// Get shader type for a planet
const shaderType = getV2PlanetShaderType(planet); // e.g., 'oceanWorld'
// Get shader names
const vertName = getPlanetVertexShader('v2'); // 'v2PlanetVert'
const fragName = getV2ShaderFileName(shaderType); // 'v2OceanWorldFrag'
// Create material
const material = new THREE.ShaderMaterial({
vertexShader: shaderService.get(vertName),
fragmentShader: shaderService.get(fragName),
uniforms: createPlanetUniforms({ planet, detailLevel: 'detailed' }),
});import { getStarSurfaceShaders, getStarCoronaShaders } from '@/utils/planetUniforms';
const surface = getStarSurfaceShaders('v2');
const corona = getStarCoronaShaders('v2');
const starMaterial = new THREE.ShaderMaterial({
vertexShader: shaderService.get(surface.vert),
fragmentShader: shaderService.get(surface.frag),
uniforms: {
uStarColor: { value: new THREE.Color('#ffdd88') },
uTime: { value: 0 },
uTemperature: { value: 5778 }, // Kelvin
uSeed: { value: 0.5 },
},
});V2 shaders use #include directives to share code:
#include "v2/common/noise.glsl"
#include "v2/common/color.glsl"
#include "v2/common/lighting.glsl"
#include "v2/common/seed.glsl"The shaderService resolves these at load time, replacing directives with actual file contents.
To add a utility file:
- Create the
.glslfile inpublic/shaders/v2/common/ - Add to
manifest.jsonwith"utility": true:{ "name": "v2NewUtilGlsl", "path": "v2/common/newUtil.glsl", "utility": true } - Use
#include "v2/common/newUtil.glsl"in other shaders
| Shader | Description | Selection Criteria |
|---|---|---|
gasGiant |
Jupiter/Saturn-like with bands and storms | Gas Giant type, temp < 1000K |
hotJupiter |
Ultra-hot, tidally locked giants | Hot Jupiter subtype or temp > 1000K |
iceGiant |
Neptune/Uranus-like with haze | Neptune-like type |
subNeptune |
Mini-Neptunes with thick atmospheres | Sub-Neptune type |
| Shader | Description | Selection Criteria |
|---|---|---|
rocky |
Earth/Mars-like with biomes | Default rocky, temperate |
lavaWorld |
Volcanic with molten surface | Lava World subtype or temp > 800K |
icyWorld |
Europa-like with fractures | Ice World subtype or temp < 200K |
oceanWorld |
Water-dominated worlds | Low density + temperate |
desertWorld |
Arid with dunes and canyons | High temp (400-800K) + rocky density |
tidallyLocked |
Permanent day/night division | Short orbital period around M-dwarf |
All planet shaders use the same uniform interface:
| Uniform | Type | Description |
|---|---|---|
uBaseColor |
vec3 | Base color from planet type |
uTime |
float | Animation time (update each frame) |
uTemperature |
float | Equilibrium temperature in Kelvin |
uHasAtmosphere |
float | Atmosphere factor (0-1) |
uSeed |
float | Deterministic seed from planet name (0-1) |
uDensity |
float | Normalized density (0-1) |
uInsolation |
float | Normalized stellar flux (0-1) |
uStarTemp |
float | Host star temperature in Kelvin |
uDetailLevel |
float | LOD: 0 = simple, 1 = detailed |
Star shaders use:
| Uniform | Type | Description |
|---|---|---|
uStarColor |
vec3 | Base star color |
uTime |
float | Animation time |
uTemperature |
float | Star temperature in Kelvin |
uSeed |
float | Variation seed (0-1) |
The seed system ensures:
- Same planet always looks the same (deterministic)
- Different planets look visually distinct
Seed-derived properties:
float seedScale = seedScale(uSeed); // Feature scale variation
float seedRotation = seedRotation(uSeed); // UV rotation angle
float seedPhase = seedPhase(uSeed); // Noise phase offset
float seedHue = seedHueShift(uSeed); // Color hue variation
bool hasFeature = seedHasCommonFeature(uSeed, 0.0); // Feature toggle| Range | Effect |
|---|---|
| < 200K | Ice surfaces, frozen features |
| 200-320K | Temperate, possible life |
| 320-500K | Hot, arid, desert conditions |
| 500-800K | Volcanic activity begins |
| > 800K | Lava surfaces, thermal glow |
| > 1500K | White-hot, atmospheric escape |
| Range | Effect |
|---|---|
| < 2 g/cm³ | Volatile-rich, smooth surfaces, bluer tones |
| 2-4 g/cm³ | Mixed composition, Earth-like |
| 4-7 g/cm³ | Rocky, rugged terrain |
| > 7 g/cm³ | Iron-rich, reddish tones, cratered |
| Range | Effect |
|---|---|
| < 0.3 | Thick atmospheres, frost, ice caps |
| 0.3-2 | Earth-like conditions |
| 2-10 | Thin atmospheres, surface glow |
| > 10 | Atmospheric escape visible |
| Star Type | Effect |
|---|---|
| M-dwarf (< 4000K) | Warm orange-red lighting tint |
| K-dwarf (4000-5200K) | Yellow-orange tint |
| G-type (5200-6000K) | Neutral (Sun-like) |
| F-type (6000-7500K) | Slightly cool tint |
| A/B-type (> 7500K) | Blue-white tint |
- Use
uDetailLevel = 0for thumbnails and distant views - Use
uDetailLevel = 1for close-up planet pages - FBM octaves are reduced in simple mode
- Domain warping is skipped in simple mode
- Cloud rendering is skipped in simple mode
-
Create fragment shader in
public/shaders/v2/planet/:#include "v2/common/noise.glsl" #include "v2/common/color.glsl" #include "v2/common/lighting.glsl" #include "v2/common/seed.glsl" uniform vec3 uBaseColor; // ... other uniforms void main() { // Your shader code }
-
Add to
manifest.json:{ "name": "v2NewPlanetFrag", "path": "v2/planet/newPlanet.frag" } -
Add type to
planetUniforms.ts:- Add to
V2ShaderTypeunion - Add mapping in
getV2PlanetShaderType() - Add to
getV2ShaderFileName()map
- Add to
-
Add selection logic in
getV2PlanetShaderType()based on planet properties
Shader techniques based on:
- Noise functions: Morgan McGuire, Inigo Quilez
- Star flames: trisomie21 (Shadertoy)
- Lava worlds: Morgan McGuire's "Vulcan"
- Atmospheric scattering: Various PBR references
- ShaderService API - How shaders are loaded and managed
- Planetary Rings - Ring rendering with shader system
- Planet Rotation & Axial Tilt - How rotation affects visuals
- Star Surface Shader - Star-specific rendering techniques
- Planet Catalog - Where these shaders are displayed