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>
35 lines
1.1 KiB
GLSL
35 lines
1.1 KiB
GLSL
#version 430 core
|
|
|
|
// Per-vertex attributes
|
|
layout(location = 0) in vec3 aPosition;
|
|
layout(location = 1) in vec3 aNormal;
|
|
layout(location = 2) in vec2 aTexCoord;
|
|
|
|
// Per-instance model matrix, split across four vec4 attribute slots.
|
|
// A mat4 consumes 4 consecutive attribute locations, so locations 3-6 are
|
|
// all occupied by this single logical matrix. The C# side must call
|
|
// VertexAttribPointer four times (one per row) and VertexAttribDivisor(loc, 1)
|
|
// on each of the four slots.
|
|
layout(location = 3) in vec4 aInstanceRow0;
|
|
layout(location = 4) in vec4 aInstanceRow1;
|
|
layout(location = 5) in vec4 aInstanceRow2;
|
|
layout(location = 6) in vec4 aInstanceRow3;
|
|
|
|
uniform mat4 uViewProjection;
|
|
|
|
out vec2 vTex;
|
|
out vec3 vWorldNormal;
|
|
out vec3 vWorldPos;
|
|
|
|
void main() {
|
|
// Reconstruct the per-instance model matrix from its four row vectors.
|
|
mat4 model = mat4(aInstanceRow0, aInstanceRow1, aInstanceRow2, aInstanceRow3);
|
|
|
|
vec4 worldPos = model * vec4(aPosition, 1.0);
|
|
gl_Position = uViewProjection * worldPos;
|
|
|
|
vWorldPos = worldPos.xyz;
|
|
// Transform normal into world space.
|
|
vWorldNormal = normalize(mat3(model) * aNormal);
|
|
vTex = aTexCoord;
|
|
}
|