From 9181b31a6d2c895958a970bd4bf4889f7cd18abb Mon Sep 17 00:00:00 2001 From: OneAvargeCoder193 <85588535+OneAvargeCoder193@users.noreply.github.com> Date: Sun, 27 Apr 2025 14:29:19 -0400 Subject: [PATCH] Add stars and a simple skybox (#1043) * really bad and inefficient star code i think * add stars that dont work during the day but look cool * remove galaxy shape and replace it with a simple sphere * add broken background * make stars affected by fog * make the stars additive * remove commented code in shaders * remove unused variables in the shader * make the stars spin * make stars not have bloom * fix formatting * temp changes * fix formatting (still temp changes) * switch to cubyz random numbers * multiply star matrix in cpu instead of gpu * changed star to be twice as big, not affect by resolution as much, and also be loaded from a texture * remove trailing whitespaces * remove unused constants * simplify the code by removing a log and power * optimizing the stars * remove debug code * make stars triangles instead of points * simplify the math more * give star ssbo unique id * fix formatting issues * make array multiline * fix formatting * remove files i accidentally added * remove random obj file * fix some issues * use square distance * fix formatting * small change * fix some stuff * fix other things * comp error * more stuff * fix some stuff with the stars * test * fix formatting * fix more formatting * test 2 * fixes * changes * changes * fix formatting * fixes * remove patch --- assets/cubyz/shaders/skybox/star.fs | 14 ++ assets/cubyz/shaders/skybox/star.vs | 29 +++ assets/cubyz/star.png | Bin 0 -> 1539 bytes src/game.zig | 2 +- src/gui/windows/gpu_performance_measuring.zig | 2 + src/random.zig | 11 ++ src/renderer.zig | 175 +++++++++++++++++- 7 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 assets/cubyz/shaders/skybox/star.fs create mode 100644 assets/cubyz/shaders/skybox/star.vs create mode 100644 assets/cubyz/star.png diff --git a/assets/cubyz/shaders/skybox/star.fs b/assets/cubyz/shaders/skybox/star.fs new file mode 100644 index 00000000..9adea8b8 --- /dev/null +++ b/assets/cubyz/shaders/skybox/star.fs @@ -0,0 +1,14 @@ +#version 430 + +in vec3 pos; +in flat vec3 centerPos; +in flat vec3 color; + +layout (location = 0, index = 0) out vec4 fragColor; + +void main() { + if (dot(pos - centerPos, pos - centerPos) > 1.0/12.0) + discard; + + fragColor = vec4(color, 1); +} diff --git a/assets/cubyz/shaders/skybox/star.vs b/assets/cubyz/shaders/skybox/star.vs new file mode 100644 index 00000000..e32cc15b --- /dev/null +++ b/assets/cubyz/shaders/skybox/star.vs @@ -0,0 +1,29 @@ +#version 430 + +struct star { + vec4 vertexPositions[3]; + + vec3 pos; + float padding1; + vec3 color; + float padding2; +}; + +layout (std430, binding = 12) buffer _starBuffer { + star starData[]; +}; + +uniform mat4 mvp; +uniform float starOpacity; + +out vec3 pos; +out flat vec3 centerPos; +out flat vec3 color; + +void main() { + gl_Position = mvp*vec4(starData[gl_VertexID/3].vertexPositions[gl_VertexID%3].xyz, 1); + + pos = starData[gl_VertexID/3].vertexPositions[gl_VertexID%3].xyz; + centerPos = starData[gl_VertexID/3].pos; + color = starData[gl_VertexID/3].color*starOpacity; +} diff --git a/assets/cubyz/star.png b/assets/cubyz/star.png new file mode 100644 index 0000000000000000000000000000000000000000..92efdfc543a3776b773e0b56ea17b293d15757bd GIT binary patch literal 1539 zcmZ9Mc{tQ-6vuytVeE59$llHn9x@0G`eRG4%%B_5hPd$v6`E zT#y&}qHCxpaMsl~kgQ0=SsgVsCfC(g1P$It> zybU1B#C({CfULp<0pOsw1Ld2{a+vo6`M_L29rXX_|92?B3;>yI01H^5tg5EWRQ7A? zK?k(#*Q8vK4-5j%fgDy^YHEP=yGun``CxZsp#|Bt;M@SdL_OXRV-?}FR`M?Ux-yqx z@%~S}&!<^P?e+3g_tP0xYg~DD0b@yh($`FzB&OLC;8++ETxfphfgg@BZXfPWFRnjK z&+u39>a(4!B>5_aD|E|`SguBX7hQDH>#b~FB=tv%n$<0msJgwDtD2?gBohZJ5|Qsl zOE**DN;l}6?i3z081ki0kFuH*8uV)N^LR-+Oe^DX+#XZm8Q4Syc$x=+?WgF|sq ze9qMvS4TWI>l;k9E+T6;fYbf$E+J*2wsJIvQr*_2F!HdzfGBih#>7cP&j2&68Pjz$ z%)9FpQf9e}MAVfG@s)rNNs0?H^c{F<%?b#;)Op1XeFTeQS7@|@dD^oQlTTd6o0oNu zKku5pn*EM02vu-?Ex6t4JLocK>^anxn9}oJYPG0B{zN7kBMXBaMGblXq~hXR6zvcM z2NR!qI`^KIb*+q&>|=Y=A?VS@Dz(H3URdMMD<5oo*#uosDskMy)-o7bzDD_>zcB8? zzb4&)(Spg&`E2~Ho?n=#%}&YWfw0SMS6SvPVUvF3dc6UlQ8YAmiDNZwLMNL8bzy&2~B%u7aAJbMU}TXf4TSF284Kt=Q_g(w5^H%;M zu+eczeX^97b08J7b+v$xc@cM`c_7crmCcBL<=Ixob%qO>_ts)^j=1s}iBz(-Q!x#Q z&f0jxDZ0bFbh38X3 z4jZJ)Pf9gI8moE+P)%XDtwywZO|PrjK=a*=51gf;k^BUkT;f3W$CwNCDEfG0$=tV` zxU2>SaV4q{eMG*dY;Nn(D4!tVeaL9Gwt2J%f>K=BR2EuyZN7ijZ^{Af?%t6%QU0mC zNE*=}IL68Omn${4f$`n_gu(8`rMb@!Mnii;#(X~QMdWG3;{`PF#b!hM=0zfiKwmbk zAV2TuS6R#Sz{g2b@%CFt@#MY-j%=lk?c2p}3^m$vKEB9;qGTlkA6$=MQ=M5jGpZVr z(!!uQ8dMw+pLf&H2xe1&Lu!JRVq#+b+ufxnmM`U8Xgcm8+xk>(+Ka*=etZwb9k|Xa z;30SZFSgjx88s9va6fdN#=dOW-Dz!M3KQbGF~vSpp2ZQtfNxp|pMKAM7lxc;)pI`0 z!&_j^5#)$9IVVcFt>Kpl$2xQj_H|Fk^36g{QxjTyl??fl$eocgwFf^L9!E5+#k$1( E3n(&#B>(^b literal 0 HcmV?d00001 diff --git a/src/game.zig b/src/game.zig index b681d525..6250c8cb 100644 --- a/src/game.zig +++ b/src/game.zig @@ -641,7 +641,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/gui/windows/gpu_performance_measuring.zig b/src/gui/windows/gpu_performance_measuring.zig index e9d62fa3..1033af5c 100644 --- a/src/gui/windows/gpu_performance_measuring.zig +++ b/src/gui/windows/gpu_performance_measuring.zig @@ -14,6 +14,7 @@ const GuiComponent = gui.GuiComponent; pub const Samples = enum(u8) { screenbuffer_clear, clear, + skybox, animation, chunk_rendering_preparation, chunk_rendering_previous_visible, @@ -33,6 +34,7 @@ pub const Samples = enum(u8) { const names = [_][]const u8{ "Screenbuffer clear", "Clear", + "Skybox", "Pre-processing Block Animations", "Chunk Rendering Preparation", "Chunk Rendering Previous Visible", diff --git a/src/random.zig b/src/random.zig index 3d91fbac..498be4cd 100644 --- a/src/random.zig +++ b/src/random.zig @@ -53,6 +53,17 @@ pub fn nextFloatSigned(seed: *u64) f32 { return @as(f32, @floatFromInt(@as(i24, @bitCast(nextInt(u24, seed)))))/(1 << 23); } +pub fn nextFloatExp(seed: *u64) f32 { + return -@log(nextFloat(seed)); +} + +pub fn nextFloatGauss(seed: *u64) f32 { + const a = nextFloat(seed); + const b = nextFloat(seed); + + return @sqrt(-2.0*@log(a))*@cos(2.0*std.math.pi*b); +} + pub fn nextFloatVector(len: comptime_int, seed: *u64) @Vector(len, f32) { var result: @Vector(len, f32) = undefined; inline for(0..len) |i| { diff --git a/src/renderer.zig b/src/renderer.zig index 71d4e40c..2f1ec264 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -72,6 +72,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 +87,7 @@ pub fn deinit() void { Bloom.deinit(); MeshSelection.deinit(); MenuBackGround.deinit(); + Skybox.deinit(); mesh_storage.deinit(); chunk_meshing.deinit(); reflectionCubeMap.deinit(); @@ -134,11 +136,10 @@ pub fn render(playerPosition: Vec3d, deltaTime: f64) void { ambient[0] = @max(0.1, world.ambientLight); ambient[1] = @max(0.1, world.ambientLight); ambient[2] = @max(0.1, world.ambientLight); - const skyColor = vec.xyz(world.clearColor); - game.fog.skyColor = skyColor; + game.fog.skyColor = vec.xyz(world.clearColor); itemdrop.ItemDisplayManager.update(deltaTime); - renderWorld(world, ambient, skyColor, playerPosition); + renderWorld(world, ambient, Skybox.getSkyColor(), playerPosition); const startTime = std.time.milliTimestamp(); mesh_storage.updateMeshes(startTime + maximumMeshTime); } else { @@ -183,6 +184,10 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo const time: u32 = @intCast(std.time.milliTimestamp() & std.math.maxInt(u32)); + gpu_performance_measuring.startQuery(.skybox); + Skybox.render(); + gpu_performance_measuring.stopQuery(); + gpu_performance_measuring.startQuery(.animation); blocks.meshes.preProcessAnimationData(time); gpu_performance_measuring.stopQuery(); @@ -611,6 +616,170 @@ pub const MenuBackGround = struct { } }; +pub const Skybox = struct { + var starShader: Shader = undefined; + var starUniforms: struct { + mvp: c_int, + starOpacity: c_int, + } = undefined; + + var starVao: c_uint = undefined; + + var starSsbo: graphics.SSBO = undefined; + + const numStars = 10000; + + fn getStarPos(seed: *u64) Vec3f { + const x: f32 = @floatCast(main.random.nextFloatGauss(seed)); + const y: f32 = @floatCast(main.random.nextFloatGauss(seed)); + const z: f32 = @floatCast(main.random.nextFloatGauss(seed)); + + const r = std.math.cbrt(main.random.nextFloat(seed))*5000.0; + + return vec.normalize(Vec3f{x, y, z})*@as(Vec3f, @splat(r)); + } + + fn getStarColor(temperature: f32, light: f32, image: graphics.Image) Vec3f { + const rgbCol = image.getRGB(@intFromFloat(std.math.clamp(temperature/15000.0*@as(f32, @floatFromInt(image.width)), 0.0, @as(f32, @floatFromInt(image.width - 1)))), 0); + var rgb: Vec3f = @floatFromInt(Vec3i{rgbCol.r, rgbCol.g, rgbCol.b}); + rgb /= @splat(255.0); + + rgb *= @as(Vec3f, @splat(light)); + + const m = @reduce(.Max, rgb); + if(m > 1.0) { + rgb /= @as(Vec3f, @splat(m)); + } + + return rgb; + } + + fn init() void { + const starColorImage = graphics.Image.readFromFile(main.stackAllocator, "assets/cubyz/star.png") catch |err| { + std.log.err("Failed to load star image: {s}", .{@errorName(err)}); + return; + }; + defer starColorImage.deinit(main.stackAllocator); + + starShader = Shader.initAndGetUniforms("assets/cubyz/shaders/skybox/star.vs", "assets/cubyz/shaders/skybox/star.fs", "", &starUniforms); + starShader.bind(); + + var starData: [numStars*20]f32 = undefined; + + const starDist = 200.0; + + const off: f32 = @sqrt(3.0)/6.0; + + const triVertA = Vec3f{0.5, starDist, -off}; + const triVertB = Vec3f{-0.5, starDist, -off}; + const triVertC = Vec3f{0.0, starDist, @sqrt(3.0)/2.0 - off}; + + var seed: u64 = 0; + + for(0..numStars) |i| { + var pos: Vec3f = undefined; + + var radius: f32 = undefined; + + var temperature: f32 = undefined; + + var light: f32 = 0; + + while(light < 0.1) { + pos = getStarPos(&seed); + + radius = @floatCast(main.random.nextFloatExp(&seed)*4 + 0.2); + + temperature = @floatCast(@abs(main.random.nextFloatGauss(&seed)*3000.0 + 5000.0) + 1000.0); + + // 3.6e-12 can be modified to change the brightness of the stars + light = (3.6e-12*radius*radius*temperature*temperature*temperature*temperature)/(vec.dot(pos, pos)); + } + + pos = vec.normalize(pos)*@as(Vec3f, @splat(starDist)); + + const normPos = vec.normalize(pos); + + const color = getStarColor(temperature, light, starColorImage); + + const latitude: f32 = @floatCast(std.math.asin(normPos[2])); + const longitude: f32 = @floatCast(std.math.atan2(-normPos[0], normPos[1])); + + const mat = Mat4f.rotationZ(longitude).mul(Mat4f.rotationX(latitude)); + + const posA = vec.xyz(mat.mulVec(.{triVertA[0], triVertA[1], triVertA[2], 1.0})); + const posB = vec.xyz(mat.mulVec(.{triVertB[0], triVertB[1], triVertB[2], 1.0})); + const posC = vec.xyz(mat.mulVec(.{triVertC[0], triVertC[1], triVertC[2], 1.0})); + + starData[i*20 ..][0..3].* = posA; + starData[i*20 + 4 ..][0..3].* = posB; + starData[i*20 + 8 ..][0..3].* = posC; + + starData[i*20 + 12 ..][0..3].* = pos; + starData[i*20 + 16 ..][0..3].* = color; + } + + starSsbo = graphics.SSBO.initStatic(f32, &starData); + + c.glGenVertexArrays(1, &starVao); + c.glBindVertexArray(starVao); + c.glEnableVertexAttribArray(0); + } + + pub fn deinit() void { + starShader.deinit(); + starSsbo.deinit(); + c.glDeleteVertexArrays(1, &starVao); + } + + pub fn getSkyColor() Vec3f { + return game.fog.skyColor*@as(Vec3f, @splat(@reduce(.Add, game.fog.skyColor)/3.0)); + } + + pub fn render() void { + const viewMatrix = game.camera.viewMatrix; + + const time = game.world.?.gameTime.load(.monotonic); + + var starOpacity: f32 = 0; + const dayTime = @abs(@mod(time, game.World.dayCycle) -% game.World.dayCycle/2); + if(dayTime < game.World.dayCycle/4 - game.World.dayCycle/16) { + starOpacity = 1; + } else if(dayTime > game.World.dayCycle/4 + game.World.dayCycle/16) { + starOpacity = 0; + } else { + starOpacity = 1 - @as(f32, @floatFromInt(dayTime - (game.World.dayCycle/4 - game.World.dayCycle/16)))/@as(f32, @floatFromInt(game.World.dayCycle/8)); + } + + if(starOpacity != 0) { + c.glDisable(c.GL_CULL_FACE); + c.glDisable(c.GL_DEPTH_TEST); + + c.glBlendFunc(c.GL_ONE, c.GL_ONE); + c.glEnable(c.GL_BLEND); + + starShader.bind(); + + const starMatrix = game.projectionMatrix.mul(viewMatrix.mul(Mat4f.rotationX(@as(f32, @floatFromInt(time))/@as(f32, @floatFromInt(main.game.World.dayCycle))))); + + starSsbo.bind(12); + + c.glUniform1f(starUniforms.starOpacity, starOpacity); + c.glUniformMatrix4fv(starUniforms.mvp, 1, c.GL_TRUE, @ptrCast(&starMatrix)); + + c.glBindVertexArray(starVao); + c.glDrawArrays(c.GL_TRIANGLES, 0, numStars*3); + + c.glBindBuffer(c.GL_SHADER_STORAGE_BUFFER, 0); + + c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); + + c.glEnable(c.GL_CULL_FACE); + c.glEnable(c.GL_DEPTH_TEST); + } + } +}; + pub const Frustum = struct { // MARK: Frustum const Plane = struct { pos: Vec3f,