acdream/src/AcDream.App/Rendering/Shaders/sky.frag
Erik 9957070cab feat(render): Phase G.1/G.2 — SceneLighting UBO + sky renderer + shader integration
Wire the existing LightManager + WorldTimeService state into visible
rendering. Every draw call (terrain, static mesh, instanced mesh, sky)
now shares one SceneLighting UBO at binding=1 carrying:
  - 8 Light slots (Directional / Point / Spot, retail hard-cutoff)
  - Ambient RGB + active light count
  - Fog start/end/mode + color + lightning flash scalar
  - Camera world position + day fraction

The CPU side (SceneLightingUbo in Core.Lighting) is a POD struct that
gets BufferSubData'd once per frame from GameWindow.OnRender. Shaders
read the block via `layout(std140, binding = 1) uniform SceneLighting`
— no per-program uniform uploads.

Shader changes:
  - mesh.frag + mesh_instanced.frag accumulate 8 dynamic lights per
    fragment using the retail no-attenuation hard-cutoff model
    (r13 §10.2 / §13.1). Sun reads slot 0; spots use hard cos-cone test.
    Additive lightning flash + linear fog layered on top. Saturate
    clamps per-channel to 1.0.
  - terrain.vert bakes AdjustPlanes sun+ambient per vertex using the
    retail MIN_FACTOR = 0.08 ambient floor (r13 §7). terrain.frag adds
    fog + flash on top of the baked vertex color.
  - mesh.vert + mesh_instanced.vert emit vWorldPos so the fragment
    stage can do per-pixel lighting against world-space positions.
  - New sky.vert / sky.frag pair — unlit, scroll-UV, camera-centered,
    with its own 0.1..1e6 far plane. Ports WorldBuilder's skybox.

SkyRenderer (new file in App/Rendering/Sky/) ports WorldBuilder's
SkyboxRenderManager verbatim for the C# idiom: zeroed view translation,
dedicated projection, depth mask off, iterate each visible SkyObject
in the day group, apply arc transform (Z rot for heading + Y rot for
arc sweep), feed TexVelocityX/Y as a scrolling UV offset, apply
per-keyframe SkyObjectReplace overrides (mesh swap + transparency +
luminosity) for overcast / dusk cloud variants.

GameWindow integration:
  - OnLoad parses Region (0x13000000) into LoadedSkyDesc and hot-swaps
    WorldTime's provider to the dat-accurate keyframes. Seeds to noon
    for offline rendering. Creates the SceneLightingUboBinding and the
    SkyRenderer.
  - OnRender: set clear color from atmosphere fog, tick WeatherSystem,
    spawn/stop rain/snow camera-local emitters on kind change, feed
    sun to LightManager (zero intensity indoors — r13 §13.7), tick
    LightManager against viewer pos, build + upload the UBO, draw
    sky before terrain, draw terrain + static + instanced using the
    shared UBO.

5 new UBO packing tests (struct sizes, slot population, 8-light cap,
directional slot 0).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 10:39:48 +02:00

51 lines
1.7 KiB
GLSL

#version 430 core
// Sky mesh fragment shader — sample the object's diffuse texture with
// the scrolled UVs from the vertex stage. Unlit: sky meshes ARE the
// gradient (r12 §2.2), not a surface lit by the sun.
//
// The per-keyframe replace override can dim the mesh (Transparent) or
// brighten it (Luminosity); those two floats arrive as uTransparency /
// uLuminosity uniforms.
in vec2 vTex;
out vec4 fragColor;
uniform sampler2D uDiffuse;
uniform float uTransparency; // 0 = fully visible, 1 = invisible
uniform float uLuminosity; // 1 = normal, >1 makes the mesh glow
uniform vec4 uTint; // per-object color tint (default white)
// Shared SceneLighting UBO — we only need the fog parameters to let the
// horizon band of the sky blend smoothly into the scene's fog color at
// the far edge, and the lightning flash to give storms their signature
// strobe.
struct Light {
vec4 posAndKind;
vec4 dirAndRange;
vec4 colorAndIntensity;
vec4 coneAngleEtc;
};
layout(std140, binding = 1) uniform SceneLighting {
Light uLights[8];
vec4 uCellAmbient;
vec4 uFogParams;
vec4 uFogColor;
vec4 uCameraAndTime;
};
void main() {
vec4 sampled = texture(uDiffuse, vTex);
// Apply tint + luminosity. Retail's SkyObjReplace.Luminosity can push
// above 1 to make the sun mesh brighter than its texture; r12 §2.3.
vec3 rgb = sampled.rgb * uTint.rgb * uLuminosity;
// Lightning additive bump — makes the sky itself flash during storms.
rgb += uFogParams.z * vec3(0.5, 0.5, 0.55);
rgb = min(rgb, vec3(1.2)); // soft clamp to let luminosity over-bright mildly
float a = sampled.a * (1.0 - uTransparency) * uTint.a;
if (a < 0.01) discard;
fragColor = vec4(rgb, a);
}