diff --git a/assets/cubyz/shaders/chunks/transparent_fragment.fs b/assets/cubyz/shaders/chunks/transparent_fragment.fs index 804b7f4d..8b763c62 100644 --- a/assets/cubyz/shaders/chunks/transparent_fragment.fs +++ b/assets/cubyz/shaders/chunks/transparent_fragment.fs @@ -12,6 +12,8 @@ uniform int time; uniform vec3 ambientLight; uniform sampler2DArray texture_sampler; uniform sampler2DArray emissionSampler; +uniform samplerCube reflectionMap; +uniform float reflectionMapSize; layout(binding = 3) uniform sampler2D depthTexture; @@ -68,51 +70,6 @@ uniform float zFar; uniform Fog fog; -ivec3 random3to3(ivec3 v) { - v &= 15; - ivec3 fac = ivec3(11248723, 105436839, 45399083); - int seed = v.x*fac.x ^ v.y*fac.y ^ v.z*fac.z; - v = seed*fac; - return v; -} - -float snoise(vec3 v){ // TODO: Maybe use a cubemap. - const vec2 C = vec2(1.0/6.0, 1.0/3.0); - - // First corner - vec3 i = floor(v + dot(v, C.yyy)); - vec3 x0 = v - i + dot(i, C.xxx); - - // Other corners - vec3 g = step(x0.yzx, x0.xyz); - vec3 l = 1.0 - g; - vec3 i1 = min(g.xyz, l.zxy); - vec3 i2 = max(g.xyz, l.zxy); - - // x0 = x0 - 0. + 0.0 * C - vec3 x1 = x0 - i1 + 1.0*C.xxx; - vec3 x2 = x0 - i2 + 2.0*C.xxx; - vec3 x3 = x0 - 1. + 3.0*C.xxx; - - // Get gradients: - ivec3 rand = random3to3(ivec3(i)); - vec3 p0 = vec3(rand); - - rand = random3to3((ivec3(i + i1))); - vec3 p1 = vec3(rand); - - rand = random3to3((ivec3(i + i2))); - vec3 p2 = vec3(rand); - - rand = random3to3((ivec3(i + 1))); - vec3 p3 = vec3(rand); - - // Mix final noise value - vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); - m = m*m; - return 42.0*dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)))/(1 << 31); -} - vec3 unpackColor(uint color) { return vec3( color>>16 & 255u, @@ -177,6 +134,15 @@ vec2 getTextureCoordsNormal(vec3 voxelPosition, int textureDir) { } } +vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012/02/seamless-cube-map-filtering/ + float M = max(max(abs(v.x), abs(v.y)), abs(v.z)); + float scale = (reflectionMapSize - 1)/reflectionMapSize; + if (abs(v.x) != M) v.x *= scale; + if (abs(v.y) != M) v.y *= scale; + if (abs(v.z) != M) v.z *= scale; + return texture(reflectionMap, v); +} + void main() { int textureIndex = textureData[blockType].textureIndices[faceNormal]; textureIndex = textureIndex + time / animation[textureIndex].time % animation[textureIndex].frames; @@ -199,7 +165,7 @@ void main() { // TODO: Change this when it rains. // TODO: Normal mapping. // TODO: Allow textures to contribute to this term. - textureColor.rgb += (textureData[blockType].reflectivity/2*vec3(snoise(normalize(reflect(direction, normals[faceNormal])))) + vec3(textureData[blockType].reflectivity))*ambientLight*normalVariation; + textureColor.rgb += (textureData[blockType].reflectivity*fixedCubeMapLookup(reflect(direction, normals[faceNormal])).xyz)*ambientLight*normalVariation; textureColor.rgb += texture(emissionSampler, vec3(getTextureCoordsNormal(startPosition/16, faceNormal), textureIndex)).rgb; blendColor.rgb *= 1 - textureColor.a; textureColor.a = 1; diff --git a/assets/cubyz/shaders/fake_reflection.fs b/assets/cubyz/shaders/fake_reflection.fs new file mode 100644 index 00000000..7c814d2d --- /dev/null +++ b/assets/cubyz/shaders/fake_reflection.fs @@ -0,0 +1,62 @@ +#version 430 + +in vec3 coords; + +out vec4 fragColor; + +uniform vec3 normalVector; +uniform vec3 upVector; +uniform vec3 rightVector; +uniform float frequency; + +ivec3 random3to3(ivec3 v) { + v &= 15; + ivec3 fac = ivec3(11248723, 105436839, 45399083); + int seed = v.x*fac.x ^ v.y*fac.y ^ v.z*fac.z; + v = seed*fac; + return v; +} + +float snoise(vec3 v) { + const vec2 C = vec2(1.0/6.0, 1.0/3.0); + + // First corner + vec3 i = floor(v + dot(v, C.yyy)); + vec3 x0 = v - i + dot(i, C.xxx); + + // Other corners + vec3 g = step(x0.yzx, x0.xyz); + vec3 l = 1.0 - g; + vec3 i1 = min(g.xyz, l.zxy); + vec3 i2 = max(g.xyz, l.zxy); + + // x0 = x0 - 0. + 0.0 * C + vec3 x1 = x0 - i1 + 1.0*C.xxx; + vec3 x2 = x0 - i2 + 2.0*C.xxx; + vec3 x3 = x0 - 1. + 3.0*C.xxx; + + // Get gradients: + ivec3 rand = random3to3(ivec3(i)); + vec3 p0 = vec3(rand); + + rand = random3to3((ivec3(i + i1))); + vec3 p1 = vec3(rand); + + rand = random3to3((ivec3(i + i2))); + vec3 p2 = vec3(rand); + + rand = random3to3((ivec3(i + 1))); + vec3 p3 = vec3(rand); + + // Mix final noise value + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); + m = m*m; + return 42.0*dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)))/(1 << 31); +} + +void main() { + vec3 position = normalize(coords); + position = position.x*rightVector + position.y*upVector + position.z*normalVector; + position *= frequency; + fragColor = vec4(vec3(snoise(position)*0.5 + 0.5), 1); +} \ No newline at end of file diff --git a/assets/cubyz/shaders/fake_reflection.vs b/assets/cubyz/shaders/fake_reflection.vs new file mode 100644 index 00000000..51920ce8 --- /dev/null +++ b/assets/cubyz/shaders/fake_reflection.vs @@ -0,0 +1,12 @@ +#version 430 + +layout (location=0) in vec2 inTexCoords; + +out vec3 coords; + +uniform float reflectionMapSize; + +void main() { + coords = vec3((inTexCoords*2 - vec2(1, 1))*(reflectionMapSize + 1)/reflectionMapSize, 1); + gl_Position = vec4(inTexCoords*2 - vec2(1, 1), 0, 1); +} \ No newline at end of file diff --git a/src/chunk.zig b/src/chunk.zig index 2fb13faf..24e97c40 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -397,6 +397,8 @@ pub const meshing = struct { @"fog.density": c_int, texture_sampler: c_int, emissionSampler: c_int, + reflectionMap: c_int, + reflectionMapSize: c_int, time: c_int, visibilityMask: c_int, voxelSize: c_int, @@ -449,6 +451,8 @@ pub const meshing = struct { c.glUniform1i(uniforms.texture_sampler, 0); c.glUniform1i(uniforms.emissionSampler, 1); + c.glUniform1i(uniforms.reflectionMap, 2); + c.glUniform1f(uniforms.reflectionMapSize, renderer.reflectionCubeMapSize); c.glUniformMatrix4fv(uniforms.viewMatrix, 1, c.GL_FALSE, @ptrCast(&game.camera.viewMatrix)); @@ -472,6 +476,8 @@ pub const meshing = struct { c.glUniform1i(transparentUniforms.texture_sampler, 0); c.glUniform1i(transparentUniforms.emissionSampler, 1); + c.glUniform1i(transparentUniforms.reflectionMap, 2); + c.glUniform1f(transparentUniforms.reflectionMapSize, renderer.reflectionCubeMapSize); c.glUniformMatrix4fv(transparentUniforms.viewMatrix, 1, c.GL_FALSE, @ptrCast(&game.camera.viewMatrix)); diff --git a/src/graphics.zig b/src/graphics.zig index 0447399b..83ad04d3 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -6,12 +6,13 @@ const std = @import("std"); const freetype = @import("freetype"); const harfbuzz = @import("harfbuzz"); -const Mat4f = @import("vec.zig").Mat4f; -const Vec4i = @import("vec.zig").Vec4i; -const Vec4f = @import("vec.zig").Vec4f; -const Vec2f = @import("vec.zig").Vec2f; -const Vec2i = @import("vec.zig").Vec2i; -const Vec3f = @import("vec.zig").Vec3f; +const vec = @import("vec.zig"); +const Mat4f = vec.Mat4f; +const Vec4i = vec.Vec4i; +const Vec4f = vec.Vec4f; +const Vec2f = vec.Vec2f; +const Vec2i = vec.Vec2i; +const Vec3f = vec.Vec3f; const main = @import("main.zig"); const Window = main.Window; @@ -1323,7 +1324,7 @@ pub const FrameBuffer = struct { c.glClear(c.GL_COLOR_BUFFER_BIT | c.GL_DEPTH_BUFFER_BIT); } - pub fn validate(self: *FrameBuffer) bool { + pub fn validate(self: *const FrameBuffer) bool { c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); defer c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); if(c.glCheckFramebufferStatus(c.GL_FRAMEBUFFER) != c.GL_FRAMEBUFFER_COMPLETE) { @@ -1333,22 +1334,22 @@ pub const FrameBuffer = struct { return true; } - pub fn bindTexture(self: *FrameBuffer, target: c_uint) void { + pub fn bindTexture(self: *const FrameBuffer, target: c_uint) void { c.glActiveTexture(target); c.glBindTexture(c.GL_TEXTURE_2D, self.texture); } - pub fn bindDepthTexture(self: *FrameBuffer, target: c_uint) void { + pub fn bindDepthTexture(self: *const FrameBuffer, target: c_uint) void { std.debug.assert(self.hasDepthTexture); c.glActiveTexture(target); c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture); } - pub fn bind(self: *FrameBuffer) void { + pub fn bind(self: *const FrameBuffer) void { c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); } - pub fn unbind(_: *FrameBuffer) void { + pub fn unbind(_: *const FrameBuffer) void { c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); } }; @@ -1533,6 +1534,85 @@ pub const Texture = struct { } }; +pub const CubeMapTexture = struct { + textureID: c_uint, + + pub fn init() CubeMapTexture { + var self: CubeMapTexture = undefined; + c.glGenTextures(1, &self.textureID); + return self; + } + + pub fn deinit(self: CubeMapTexture) void { + c.glDeleteTextures(1, &self.textureID); + } + + pub fn bindTo(self: CubeMapTexture, binding: u5) void { + c.glActiveTexture(@intCast(c.GL_TEXTURE0 + binding)); + c.glBindTexture(c.GL_TEXTURE_CUBE_MAP, self.textureID); + } + + pub fn bind(self: CubeMapTexture) void { + c.glBindTexture(c.GL_TEXTURE_CUBE_MAP, self.textureID); + } + + /// (Re-)Generates the GPU buffer. + pub fn generate(self: CubeMapTexture, width: u31, height: u31) void { + self.bind(); + + c.glTexImage2D(c.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, c.GL_RGBA8, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + c.glTexImage2D(c.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, c.GL_RGBA8, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + c.glTexImage2D(c.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, c.GL_RGBA8, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + c.glTexImage2D(c.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, c.GL_RGBA8, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + c.glTexImage2D(c.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, c.GL_RGBA8, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + c.glTexImage2D(c.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, c.GL_RGBA8, width, height, 0, c.GL_RGBA, c.GL_UNSIGNED_BYTE, null); + c.glTexParameteri(c.GL_TEXTURE_CUBE_MAP, c.GL_TEXTURE_MIN_FILTER, c.GL_LINEAR); + c.glTexParameteri(c.GL_TEXTURE_CUBE_MAP, c.GL_TEXTURE_MAG_FILTER, c.GL_LINEAR); + c.glTexParameteri(c.GL_TEXTURE_CUBE_MAP, c.GL_TEXTURE_WRAP_S, c.GL_CLAMP_TO_EDGE); + c.glTexParameteri(c.GL_TEXTURE_CUBE_MAP, c.GL_TEXTURE_WRAP_T, c.GL_CLAMP_TO_EDGE); + c.glTexParameteri(c.GL_TEXTURE_CUBE_MAP, c.GL_TEXTURE_WRAP_R, c.GL_CLAMP_TO_EDGE); + c.glTexParameteri(c.GL_TEXTURE_CUBE_MAP, c.GL_TEXTURE_BASE_LEVEL, 0); + c.glTexParameteri(c.GL_TEXTURE_CUBE_MAP, c.GL_TEXTURE_MAX_LEVEL, 0); + } + + pub fn faceNormal(face: usize) Vec3f { + const normals = [_]Vec3f { + .{ 1, 0, 0}, // +x + .{-1, 0, 0}, // -x + .{0, 1, 0}, // +y + .{0, -1, 0}, // -y + .{0, 0, 1}, // +z + .{0, 0, -1}, // -z + }; + return normals[face]; + } + + pub fn faceUp(face: usize) Vec3f { + const ups = [_]Vec3f { + .{0, -1, 0}, // +x + .{0, -1, 0}, // -x + .{0, 0, 1}, // +y + .{0, 0, -1}, // -y + .{0, -1, 0}, // +z + .{0, -1, 0}, // -z + }; + return ups[face]; + } + + pub fn faceRight(face: usize) Vec3f { + comptime var rights: [6]Vec3f = undefined; + inline for(0..6) |i| { + rights[i] = comptime vec.cross(faceNormal(i), faceUp(i)); + } + return rights[face]; + } + + pub fn bindToFramebuffer(self: CubeMapTexture, fb: FrameBuffer, face: c_uint) void { + fb.bind(); + c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_COLOR_ATTACHMENT0, @as(c_uint, c.GL_TEXTURE_CUBE_MAP_POSITIVE_X) + face, self.textureID, 0); + } +}; + pub const Color = extern struct { r: u8, g: u8, diff --git a/src/renderer.zig b/src/renderer.zig index 3a3e9401..56561bb4 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -40,24 +40,62 @@ var deferredUniforms: struct { zNear: c_int, zFar: c_int, } = undefined; +var fakeReflectionShader: graphics.Shader = undefined; +var fakeReflectionUniforms: struct { + normalVector: c_int, + upVector: c_int, + rightVector: c_int, + frequency: c_int, + reflectionMapSize: c_int, +} = undefined; pub var activeFrameBuffer: c_uint = 0; +pub const reflectionCubeMapSize = 64; +var reflectionCubeMap: graphics.CubeMapTexture = undefined; + pub fn init() !void { deferredRenderPassShader = try Shader.initAndGetUniforms("assets/cubyz/shaders/deferred_render_pass.vs", "assets/cubyz/shaders/deferred_render_pass.fs", &deferredUniforms); + fakeReflectionShader = try 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_RGBA16F); try Bloom.init(); try MeshSelection.init(); try MenuBackGround.init(); + reflectionCubeMap = graphics.CubeMapTexture.init(); + reflectionCubeMap.generate(reflectionCubeMapSize, reflectionCubeMapSize); + initReflectionCubeMap(); } pub fn deinit() void { deferredRenderPassShader.deinit(); + fakeReflectionShader.deinit(); worldFrameBuffer.deinit(); Bloom.deinit(); MeshSelection.deinit(); MenuBackGround.deinit(); + reflectionCubeMap.deinit(); +} + +fn initReflectionCubeMap() void { + c.glViewport(0, 0, reflectionCubeMapSize, reflectionCubeMapSize); + var framebuffer: graphics.FrameBuffer = undefined; + framebuffer.init(false, c.GL_LINEAR, c.GL_CLAMP_TO_EDGE); + defer framebuffer.deinit(); + framebuffer.bind(); + fakeReflectionShader.bind(); + c.glUniform1f(fakeReflectionUniforms.frequency, 1); + c.glUniform1f(fakeReflectionUniforms.reflectionMapSize, reflectionCubeMapSize); + for(0..6) |face| { + c.glUniform3fv(fakeReflectionUniforms.normalVector, 1, @ptrCast(&graphics.CubeMapTexture.faceNormal(face))); + c.glUniform3fv(fakeReflectionUniforms.upVector, 1, @ptrCast(&graphics.CubeMapTexture.faceUp(face))); + c.glUniform3fv(fakeReflectionUniforms.rightVector, 1, @ptrCast(&graphics.CubeMapTexture.faceRight(face))); + reflectionCubeMap.bindToFramebuffer(framebuffer, @intCast(face)); + c.glBindVertexArray(graphics.draw.rectVAO); + c.glDisable(c.GL_DEPTH_TEST); + c.glDisable(c.GL_CULL_FACE); + c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4); + } } var worldFrameBuffer: graphics.FrameBuffer = undefined; @@ -138,6 +176,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo // Update the uniforms. The uniforms are needed to render the replacement meshes. chunk.meshing.bindShaderAndUniforms(game.projectionMatrix, ambientLight, time); + reflectionCubeMap.bindTo(2); c.glActiveTexture(c.GL_TEXTURE0); blocks.meshes.blockTextureArray.bind(); c.glActiveTexture(c.GL_TEXTURE1);