Sprite sheets, atlases, game UI, time of day, weather presets, headless captures

- BillboardInstance: per-instance uv sub-rect, feet anchor, turn, dithered alpha, flash;
  BillboardBatch::sharedTexture; Texture createEmpty/update/cellUV/cellAnchor/nearest
- SpriteAtlas: fixed-cell runtime atlas with gutters
- hd2d::ui: Octopath-style panels, fonts (Cinzel, EB Garamond), dialogue box with
  typewriter + portraits + choices, menus, nameplates, toasts, ImGui theme
- DayCycle (dawn/day/dusk/night keyframes, sun + moon arcs), Weather overcast/mist/
  lightning, night-aware presets, stars and moon in the sky
- Hidden-window offscreen captures (Window visible=false, Renderer output FBO,
  OffscreenTarget); --screenshot never opens a window
- Camera::worldToScreen; CMake usable via add_subdirectory (HD2D_BUILD_DEMO,
  HD2D_ENGINE_ASSET_DIR)
This commit is contained in:
Gabrieal Jimmy
2026-10-02 15:05:23 -05:00
parent 16c03afd16
commit 83a1f16806
44 changed files with 2835 additions and 226 deletions

View File

@@ -4,8 +4,65 @@
in vec2 vUV;
layout(location = 0) out vec4 fragColor;
// Star field: at most one star per cell of a grid around the view sphere,
// anti-aliased against the pixel footprint (also works in the half-res
// reflection), twinkling, mostly faint with a few bright ones that bloom.
// `px` = angular pixel footprint (radians), computed in uniform control flow.
vec3 stars(vec3 dir, float px) {
const float grid = 150.0;
vec3 q = dir * grid;
vec3 cell = floor(q);
float h = hash13(cell);
if (h < 0.955) return vec3(0.0);
vec3 jitter = vec3(hash13(cell + 11.3), hash13(cell + 27.1), hash13(cell + 3.7)) - 0.5;
vec3 center = normalize(cell + 0.5 + jitter * 0.7) * grid; // on the sphere
float d = length(q - center);
float size = 0.10 + 0.12 * hash13(cell + 5.9);
float r = max(size, px * grid * 0.6);
float s = exp(-d * d / (r * r)) * (size * size) / (r * r);
float bright = 0.35 + 3.5 * pow(hash13(cell + 41.7), 6.0);
float twinkle = 0.7 + 0.3 * sin(uTime * (1.5 + 4.0 * hash13(cell + 9.2)) + h * 400.0);
vec3 tint = mix(vec3(0.70, 0.80, 1.0), vec3(1.0, 0.86, 0.70), hash13(cell + 17.5));
return tint * s * bright * twinkle;
}
// Moon disc at the light direction (the sun acts as the moon at night):
// soft maria, limb darkening and a two-lobe halo. rgb = disc colour,
// a = disc coverage.
vec4 moon(vec3 dir, float px, out vec3 halo) {
const float R = 0.04; // angular radius (stylised, larger than real)
vec3 L = uSunDir.xyz;
vec3 T = normalize(cross(L, abs(L.y) > 0.99 ? vec3(1, 0, 0) : vec3(0, 1, 0)));
vec3 B = cross(T, L);
vec3 rel = dir - L * dot(dir, L);
float ang = length(rel); // sin of the angle to the moon centre
float front = step(0.0, dot(dir, L));
vec3 moonCol = mix(vec3(1.0, 0.98, 0.94), uSunColor.rgb, 0.25);
halo = moonCol * front * (exp(-ang / R * 1.3) * 0.35 + exp(-ang / (R * 7.0)) * 0.07);
vec2 uv = vec2(dot(rel, T), dot(rel, B)) / R;
float r = length(uv);
float aa = max(px / R, 1e-3) * 1.5;
float disc = (1.0 - smoothstep(1.0 - aa, 1.0, r)) * front;
if (disc <= 0.0) return vec4(0.0);
float maria = vnoise(uv * 1.8 + 3.0) * 0.6 + vnoise(uv * 4.5 + 7.0) * 0.4;
float shade = mix(0.68, 1.0, smoothstep(0.35, 0.65, maria)) * (1.0 - 0.25 * r * r);
return vec4(moonCol * 1.5 * shade, disc);
}
void main() {
vec3 far = worldFromDepth(vUV, 1.0);
vec3 dir = normalize(far - uCamPos.xyz);
fragColor = vec4(skyColor(dir), 1.0);
float px = length(fwidth(dir));
vec3 c = skyColor(dir);
float night = uSkyParams.x;
if (night > 0.0 && dir.y > 0.0) {
float cover = max(uWeather.x, uSkyParams.y);
float open = (1.0 - cover) * (1.0 - skyClouds(dir) / 0.55);
c += stars(dir, px) * night * open * smoothstep(0.0, 0.2, dir.y);
vec3 halo;
vec4 disc = moon(dir, px, halo);
c = mix(c, disc.rgb, disc.a * night * open);
c += halo * night * (1.0 - 0.7 * cover);
}
fragColor = vec4(c, 1.0);
}