Improve performance by storing the noise-based fake reflection in a cube map

This commit is contained in:
IntegratedQuantum 2023-09-16 12:13:54 +02:00
parent 1951416317
commit 1a3a16c852
6 changed files with 222 additions and 57 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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));

View File

@ -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,

View File

@ -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);