diff --git a/assets/cubyz/shaders/deferred_render_pass.fs b/assets/cubyz/shaders/deferred_render_pass.fs index f35924a0d..919b0e83e 100644 --- a/assets/cubyz/shaders/deferred_render_pass.fs +++ b/assets/cubyz/shaders/deferred_render_pass.fs @@ -82,10 +82,12 @@ vec3 applyFrontfaceFog(float fogDistance, vec3 fogColor, vec3 inColor) { void main() { fragColor = texture(color, texCoords); fragColor += texture(bloomColor, texCoords); + float densityAdjustment = sqrt(dot(tanXY*(texCoords*2 - 1), tanXY*(texCoords*2 - 1)) + 1); float dist = zFromDepth(texture(depthTexture, texCoords).r); float fogDistance = calculateFogDistance(dist, densityAdjustment, playerPositionInteger.z + playerPositionFraction.z, normalize(direction).z, fog.density, fog.fogLower, fog.fogHigher); fragColor.rgb = applyFrontfaceFog(fogDistance, fog.color, fragColor.rgb); + float maxColor = max(1.0, max(fragColor.r, max(fragColor.g, fragColor.b))); fragColor.rgb = fragColor.rgb/maxColor; } \ No newline at end of file diff --git a/assets/cubyz/shaders/skybox/fragment.fs b/assets/cubyz/shaders/skybox/fragment.fs new file mode 100644 index 000000000..de769dd71 --- /dev/null +++ b/assets/cubyz/shaders/skybox/fragment.fs @@ -0,0 +1,179 @@ +#version 330 + +layout (location=0) out vec4 fragColor; + +in vec3 pos; + +#define PI 3.141592 +#define iSteps 16 +#define jSteps 8 +#define ATMOS_SIZE 6371e3 + +uniform mat4 lightDir; +uniform mat4 invLightDir; +uniform float altitude; + +vec2 rsi(vec3 r0, vec3 rd, float sr) { + float a = dot(rd, rd); + float b = 2.0 * dot(rd, r0); + float c = dot(r0, r0) - (sr * sr); + float d = (b*b) - 4.0*a*c; + if (d < 0.0) return vec2(1e5,-1e5); + return vec2( + (-b - sqrt(d))/(2.0*a), + (-b + sqrt(d))/(2.0*a) + ); +} + +vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) { + pSun = normalize(pSun); + r = normalize(r); + + vec2 p = rsi(r0, r, rAtmos); + if (p.x > p.y) return vec3(0,0,0); + p.y = min(p.y, rsi(r0, r, rPlanet).x); + float iStepSize = (p.y - p.x) / float(iSteps); + + float iTime = 0.0; + + vec3 totalRlh = vec3(0,0,0); + vec3 totalMie = vec3(0,0,0); + + float iOdRlh = 0.0; + float iOdMie = 0.0; + + float mu = dot(r, pSun); + float mumu = mu * mu; + float gg = g * g; + float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu); + float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg)); + + for (int i = 0; i < iSteps; i++) { + + vec3 iPos = r0 + r * (iTime + iStepSize * 0.5); + + float iHeight = length(iPos) - rPlanet; + + float odStepRlh = exp(-iHeight / shRlh) * iStepSize; + float odStepMie = exp(-iHeight / shMie) * iStepSize; + + iOdRlh += odStepRlh; + iOdMie += odStepMie; + + float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps); + + float jTime = 0.0; + + float jOdRlh = 0.0; + float jOdMie = 0.0; + + for (int j = 0; j < jSteps; j++) { + + vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5); + + float jHeight = length(jPos) - rPlanet; + + jOdRlh += exp(-jHeight / shRlh) * jStepSize; + jOdMie += exp(-jHeight / shMie) * jStepSize; + + jTime += jStepSize; + } + + vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh))); + + totalRlh += odStepRlh * attn; + totalMie += odStepMie * attn; + + iTime += iStepSize; + + } + + return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie); +} + +vec3 hash( vec3 x ) +{ + x = vec3( + dot(x,vec3(127.1,311.7, 74.7)), + dot(x,vec3(269.5,183.3,246.1)), + dot(x,vec3(113.5,271.9,124.6)) + ); + + return fract(sin(x)*43758.5453123); +} + +vec3 voronoi_sphere( in vec3 x ) +{ + vec3 p = floor( x ); + vec3 f = fract( x ); + + float sphere_radius = length(x); + + float id = 0.0; + vec2 res = vec2( 100.0 ); + for( int k=-1; k<=1; k++ ) + for( int j=-1; j<=1; j++ ) + for( int i=-1; i<=1; i++ ) + { + vec3 b = vec3( float(i), float(j), float(k) ); + vec3 r = vec3( b ) - f + hash( p + b ); + + vec3 cell_center_in_os = p + b + vec3(0.5); + float dist_between_cell_center_and_sphere_surface = abs(length(cell_center_in_os) - sphere_radius); + + float max_cell_dist = 0.5; + if (dist_between_cell_center_and_sphere_surface < max_cell_dist) + { + + vec3 r_in_os = x + r; + r_in_os = normalize(r_in_os) * sphere_radius; + r = r_in_os - x; + + float d = dot( r, r ); + if( d < res.x ) + { + id = dot( p+b, vec3(1.0,57.0,113.0 ) ); + res = vec2( d, res.x ); + } + else if( d < res.y ) + { + res.y = d; + } + } + } + + return vec3( sqrt( res ), abs(id) ); +} + +void main() { + vec3 rayDir = normalize(pos); + + vec3 sunDir = (lightDir * vec4(1, 0, 0, 1)).xyz; + + vec3 color = atmosphere( + rayDir, + vec3(0,0,6372e3 + altitude), + sunDir, + 22.0, + 6371e3, + 6471e3, + vec3(5.5e-6, 13.0e-6, 22.4e-6), + 21e-6, + 8e3, + 1.2e3, + 0.758 + ); + + color += smoothstep(0.998, 0.999, dot(rayDir, sunDir)) * 20; + + fragColor = vec4(color, 1); + + float brightness = dot(color, vec3(0.2126, 0.7152, 0.0722)); + if (brightness < 0.02) { + vec3 starColor = vec3(smoothstep(0.07, 0.05, voronoi_sphere((invLightDir * vec4(rayDir, 1)).xyz * 50).x)); + + fragColor.rgb += (1 - 50 * brightness) * starColor; + } + + fragColor.rgb = 1 - exp(-fragColor.rgb); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/skybox/vertex.vs b/assets/cubyz/shaders/skybox/vertex.vs new file mode 100644 index 000000000..4acafd41f --- /dev/null +++ b/assets/cubyz/shaders/skybox/vertex.vs @@ -0,0 +1,14 @@ +#version 330 + +layout (location=0) in vec3 vertexPos; + +out vec3 pos; + +uniform mat4 viewMatrix; +uniform mat4 projectionMatrix; + +void main() { + gl_Position = projectionMatrix*viewMatrix*vec4(vertexPos, 1); + + pos = vertexPos; +} \ No newline at end of file diff --git a/src/game.zig b/src/game.zig index c3361aa62..10c793cb9 100644 --- a/src/game.zig +++ b/src/game.zig @@ -572,7 +572,7 @@ pub const Player = struct { // MARK: Player }; pub const World = struct { // MARK: World - const dayCycle: u63 = 12000; // Length of one in-game day in 100ms. Midnight is at DAY_CYCLE/2. Sunrise and sunset each take about 1/16 of the day. Currently set to 20 minutes + pub const dayCycle: u63 = 12000; // Length of one in-game day in 100ms. Midnight is at DAY_CYCLE/2. Sunrise and sunset each take about 1/16 of the day. Currently set to 20 minutes conn: *Connection, manager: *ConnectionManager, diff --git a/src/renderer.zig b/src/renderer.zig index 26d117d02..a3dee0d34 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -65,6 +65,7 @@ var reflectionCubeMap: graphics.CubeMapTexture = undefined; pub fn init() void { deferredRenderPassShader = Shader.initAndGetUniforms("assets/cubyz/shaders/deferred_render_pass.vs", "assets/cubyz/shaders/deferred_render_pass.fs", "", &deferredUniforms); fakeReflectionShader = Shader.initAndGetUniforms("assets/cubyz/shaders/fake_reflection.vs", "assets/cubyz/shaders/fake_reflection.fs", "", &fakeReflectionUniforms); + worldFrameBuffer.init(true, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE); worldFrameBuffer.updateSize(Window.width, Window.height, c.GL_RGB16F); Bloom.init(); @@ -72,6 +73,7 @@ pub fn init() void { MenuBackGround.init() catch |err| { std.log.err("Failed to initialize the Menu Background: {s}", .{@errorName(err)}); }; + Skybox.init(); chunk_meshing.init(); mesh_storage.init(); reflectionCubeMap = .init(); @@ -86,6 +88,7 @@ pub fn deinit() void { Bloom.deinit(); MeshSelection.deinit(); MenuBackGround.deinit(); + Skybox.deinit(); mesh_storage.deinit(); chunk_meshing.deinit(); reflectionCubeMap.deinit(); @@ -182,6 +185,8 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const time: u32 = @intCast(std.time.milliTimestamp() & std.math.maxInt(u32)); + Skybox.render(); + gpu_performance_measuring.startQuery(.animation); blocks.meshes.preProcessAnimationData(time); gpu_performance_measuring.stopQuery(); @@ -606,6 +611,90 @@ pub const MenuBackGround = struct { } }; +pub const Skybox = struct { + var shader: Shader = undefined; + var uniforms: struct { + time: c_int, + altitude: c_int, + lightDir: c_int, + invLightDir: c_int, + viewMatrix: c_int, + projectionMatrix: c_int, + } = undefined; + + var vao: c_uint = undefined; + var vbos: [2]c_uint = undefined; + + fn init() void { + shader = Shader.initAndGetUniforms("assets/cubyz/shaders/skybox/vertex.vs", "assets/cubyz/shaders/skybox/fragment.fs", "", &uniforms); + shader.bind(); + + const rawData = [_]f32 { + -1, -1, -1, + 1, -1, -1, + 1, 1, -1, + -1, 1, -1, + -1, -1, 1, + 1, -1, 1, + 1, 1, 1, + -1, 1, 1 + }; + + const indices = [_]c_int { + 0, 3, 1, 1, 3, 2, + 5, 6, 4, 4, 6, 7, + 3, 7, 2, 2, 7, 6, + 1, 5, 0, 0, 5, 4, + 4, 7, 0, 0, 7, 3, + 1, 2, 5, 5, 2, 6, + }; + + c.glGenVertexArrays(1, &vao); + c.glBindVertexArray(vao); + c.glGenBuffers(2, &vbos); + c.glBindBuffer(c.GL_ARRAY_BUFFER, vbos[0]); + c.glBufferData(c.GL_ARRAY_BUFFER, @intCast(rawData.len*@sizeOf(f32)), &rawData, c.GL_STATIC_DRAW); + c.glVertexAttribPointer(0, 3, c.GL_FLOAT, c.GL_FALSE, 3*@sizeOf(f32), null); + c.glEnableVertexAttribArray(0); + c.glBindBuffer(c.GL_ELEMENT_ARRAY_BUFFER, vbos[1]); + c.glBufferData(c.GL_ELEMENT_ARRAY_BUFFER, @intCast(indices.len*@sizeOf(c_int)), &indices, c.GL_STATIC_DRAW); + } + + pub fn deinit() void { + shader.deinit(); + c.glDeleteVertexArrays(1, &vao); + c.glDeleteBuffers(2, &vbos); + } + + pub fn render() void { + c.glDisable(c.GL_CULL_FACE); + c.glDisable(c.GL_DEPTH_TEST); + + const viewMatrix = game.camera.viewMatrix; + shader.bind(); + + const time = @as(f32, @floatFromInt(@mod(main.game.world.?.gameTime.load(.monotonic), main.game.World.dayCycle))) / (main.game.World.dayCycle); + + const lightMatrix = Mat4f.rotationY(-std.math.pi / 2.0 - time * std.math.pi * 2); + + const invLightMatrix = Mat4f.rotationY(time * std.math.pi * 2 + std.math.pi / 2.0); + + c.glUniform1f(uniforms.time, time); + c.glUniform1f(uniforms.altitude, @floatCast(game.Player.super.pos[2])); + c.glUniformMatrix4fv(uniforms.lightDir, 1, c.GL_TRUE, @ptrCast(&lightMatrix)); + c.glUniformMatrix4fv(uniforms.invLightDir, 1, c.GL_TRUE, @ptrCast(&invLightMatrix)); + + c.glUniformMatrix4fv(uniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&viewMatrix)); + c.glUniformMatrix4fv(uniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&game.projectionMatrix)); + + c.glBindVertexArray(vao); + c.glDrawElements(c.GL_TRIANGLES, 36, c.GL_UNSIGNED_INT, null); + + c.glEnable(c.GL_CULL_FACE); + c.glEnable(c.GL_DEPTH_TEST); + } +}; + pub const Frustum = struct { // MARK: Frustum const Plane = struct { pos: Vec3f,