feat(rendering): distance fog and config-derived far plane

This commit is contained in:
MDietermann
2026-06-01 18:46:02 +02:00
parent 89ac70f1ab
commit 9daa9f8267
7 changed files with 42 additions and 4 deletions

View File

@@ -2,6 +2,7 @@
in vec2 vUv; in vec2 vUv;
in float vShade; in float vShade;
in float vWorldY; in float vWorldY;
in vec3 vWorldPos;
out vec4 FragColor; out vec4 FragColor;
@@ -10,11 +11,25 @@ uniform float u_caveDarkStartY;
uniform float u_caveDarkEndY; uniform float u_caveDarkEndY;
uniform float u_caveMinBrightness; uniform float u_caveMinBrightness;
uniform vec3 u_cameraPos;
uniform float u_fogStart;
uniform float u_fogEnd;
uniform vec3 u_skyColor;
void main() void main()
{ {
vec4 texel = texture(atlas, vUv); vec4 texel = texture(atlas, vUv);
// Y-based cave darkness.
float t = clamp((u_caveDarkStartY - vWorldY) / float t = clamp((u_caveDarkStartY - vWorldY) /
(u_caveDarkStartY - u_caveDarkEndY), 0.0, 1.0); (u_caveDarkStartY - u_caveDarkEndY), 0.0, 1.0);
float brightness = mix(1.0, u_caveMinBrightness, t); float brightness = mix(1.0, u_caveMinBrightness, t);
FragColor = vec4(texel.rgb * vShade * brightness, 1.0); vec3 color = texel.rgb * vShade * brightness;
// Distance fog to the sky color, applied after cave darkness.
float dist = length(vWorldPos - u_cameraPos);
float fog = clamp((dist - u_fogStart) / (u_fogEnd - u_fogStart), 0.0, 1.0);
color = mix(color, u_skyColor, fog);
FragColor = vec4(color, 1.0);
} }

View File

@@ -6,6 +6,7 @@ layout(location = 2) in float aShade;
out vec2 vUv; out vec2 vUv;
out float vShade; out float vShade;
out float vWorldY; out float vWorldY;
out vec3 vWorldPos;
uniform mat4 model; uniform mat4 model;
uniform mat4 view; uniform mat4 view;
@@ -17,6 +18,7 @@ void main()
vShade = aShade; vShade = aShade;
// World-space position uses row-vector convention (matches gl_Position below). // World-space position uses row-vector convention (matches gl_Position below).
vec4 worldPos = vec4(aPos, 1.0) * model; vec4 worldPos = vec4(aPos, 1.0) * model;
vWorldPos = worldPos.xyz;
vWorldY = worldPos.y; vWorldY = worldPos.y;
gl_Position = worldPos * view * projection; gl_Position = worldPos * view * projection;
} }

View File

@@ -43,7 +43,7 @@ public sealed class Game : GameWindow
Log.Information("Game starting with seed={Seed} renderDistanceH={H} renderDistanceV={V}", Log.Information("Game starting with seed={Seed} renderDistanceH={H} renderDistanceV={V}",
this._cfg.Seed, this._cfg.RenderDistanceHorizontal, this._cfg.RenderDistanceVertical); this._cfg.Seed, this._cfg.RenderDistanceHorizontal, this._cfg.RenderDistanceVertical);
GL.ClearColor(0.45f, 0.65f, 0.95f, 1.0f); GL.ClearColor(this._cfg.SkyColor.X, this._cfg.SkyColor.Y, this._cfg.SkyColor.Z, 1.0f);
GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.DepthTest);
GL.Enable(EnableCap.CullFace); GL.Enable(EnableCap.CullFace);
@@ -119,6 +119,11 @@ public sealed class Game : GameWindow
this._shader.SetFloat("u_caveDarkStartY", this._cfg.CaveDarkStartY); this._shader.SetFloat("u_caveDarkStartY", this._cfg.CaveDarkStartY);
this._shader.SetFloat("u_caveDarkEndY", this._cfg.CaveDarkEndY); this._shader.SetFloat("u_caveDarkEndY", this._cfg.CaveDarkEndY);
this._shader.SetFloat("u_caveMinBrightness", this._cfg.CaveMinBrightness); this._shader.SetFloat("u_caveMinBrightness", this._cfg.CaveMinBrightness);
float fogEnd = this._cfg.RenderDistanceHorizontal * Chunk.Size;
this._shader.SetFloat("u_fogEnd", fogEnd);
this._shader.SetFloat("u_fogStart", fogEnd * this._cfg.FogStartFraction);
this._shader.SetVector3("u_cameraPos", this._player.Camera.Position);
this._shader.SetVector3("u_skyColor", this._cfg.SkyColor);
this._texture.Use(TextureUnit.Texture0); this._texture.Use(TextureUnit.Texture0);
this._streamer.DrawAll((pos, renderer) => this._streamer.DrawAll((pos, renderer) =>

View File

@@ -29,7 +29,11 @@ public sealed class Player
public Player(Configuration cfg, Vector3 spawnPosition, float aspectRatio) public Player(Configuration cfg, Vector3 spawnPosition, float aspectRatio)
{ {
this._cfg = cfg; this._cfg = cfg;
this.Camera = new Camera(spawnPosition, aspectRatio) { Fov = cfg.Fov }; this.Camera = new Camera(spawnPosition, aspectRatio)
{
Fov = cfg.Fov,
FarPlane = (cfg.RenderDistanceHorizontal + 1) * VoxelChunk.Size,
};
} }
public void Move(Vector3 delta) => this.Camera.Move(delta); public void Move(Vector3 delta) => this.Camera.Move(delta);

View File

@@ -10,6 +10,7 @@ public sealed class Camera
public float Yaw { get; private set; } = -90f; // degrees; -90 looks down -Z public float Yaw { get; private set; } = -90f; // degrees; -90 looks down -Z
public float Pitch { get; private set; } // degrees public float Pitch { get; private set; } // degrees
public float Fov { get; set; } = 60f; // degrees public float Fov { get; set; } = 60f; // degrees
public float FarPlane { get; set; } = 100f; // world units
public float AspectRatio { get; set; } public float AspectRatio { get; set; }
public Vector3 Front { get; private set; } = -Vector3.UnitZ; public Vector3 Front { get; private set; } = -Vector3.UnitZ;
@@ -36,7 +37,7 @@ public sealed class Camera
public Matrix4 GetProjectionMatrix() => public Matrix4 GetProjectionMatrix() =>
Matrix4.CreatePerspectiveFieldOfView( Matrix4.CreatePerspectiveFieldOfView(
MathHelper.DegreesToRadians(this.Fov), this.AspectRatio, 0.1f, 100f); MathHelper.DegreesToRadians(this.Fov), this.AspectRatio, 0.1f, this.FarPlane);
private void UpdateVectors() private void UpdateVectors()
{ {

View File

@@ -39,6 +39,9 @@ public sealed class Shader : IDisposable
public void SetFloat(string name, float value) => public void SetFloat(string name, float value) =>
GL.Uniform1(this.Location(name), value); GL.Uniform1(this.Location(name), value);
public void SetVector3(string name, Vector3 value) =>
GL.Uniform3(this.Location(name), value.X, value.Y, value.Z);
private int Location(string name) private int Location(string name)
{ {
if (this._uniforms.TryGetValue(name, out int location)) return location; if (this._uniforms.TryGetValue(name, out int location)) return location;

View File

@@ -40,4 +40,12 @@ public class CameraTests
var camera = new Camera(Vector3.Zero, 1.5f); var camera = new Camera(Vector3.Zero, 1.5f);
Assert.NotEqual(Matrix4.Identity, camera.GetProjectionMatrix()); Assert.NotEqual(Matrix4.Identity, camera.GetProjectionMatrix());
} }
[Fact]
public void FarPlane_ChangesProjection()
{
var near = new Camera(Vector3.Zero, 1f) { FarPlane = 100f };
var far = new Camera(Vector3.Zero, 1f) { FarPlane = 200f };
Assert.NotEqual(near.GetProjectionMatrix(), far.GetProjectionMatrix());
}
} }