Volumetric Fog. First draft.

This commit is contained in:
IntegratedQuantum 2023-09-01 10:42:10 +02:00
parent fb5b6768bc
commit 6aa11ec1b8
7 changed files with 133 additions and 42 deletions

View File

@ -6,12 +6,18 @@ in vec2 texCoords;
layout(binding = 3) uniform sampler2D color; layout(binding = 3) uniform sampler2D color;
vec3 fetch(ivec2 pos) {
vec4 rgba = texelFetch(color, pos, 0);
if(rgba.a < 1) return vec3(0); // Prevent t-junctions from transparency from making a huge mess.
return rgba.rgb/rgba.a;
}
vec3 linearSample(ivec2 start) { vec3 linearSample(ivec2 start) {
vec3 outColor = vec3(0); vec3 outColor = vec3(0);
outColor += texelFetch(color, start, 0).rgb; outColor += fetch(start);
outColor += texelFetch(color, start + ivec2(0, 1), 0).rgb; outColor += fetch(start + ivec2(0, 1));
outColor += texelFetch(color, start + ivec2(1, 0), 0).rgb; outColor += fetch(start + ivec2(1, 0));
outColor += texelFetch(color, start + ivec2(1, 1), 0).rgb; outColor += fetch(start + ivec2(1, 1));
return outColor*0.25; return outColor*0.25;
} }

View File

@ -7,5 +7,5 @@ in vec2 texCoords;
layout(binding = 3) uniform sampler2D color; layout(binding = 3) uniform sampler2D color;
void main() { void main() {
fragColor = vec4(texture(color, texCoords).rgb, 1); fragColor = vec4(texture(color, texCoords).rgb, 0);
} }

View File

@ -12,6 +12,8 @@ uniform vec3 ambientLight;
uniform sampler2DArray texture_sampler; uniform sampler2DArray texture_sampler;
uniform sampler2DArray emissionSampler; uniform sampler2DArray emissionSampler;
layout(binding = 3) uniform sampler2D depthTexture;
layout (location = 0, index = 0) out vec4 fragColor; layout (location = 0, index = 0) out vec4 fragColor;
layout (location = 0, index = 1) out vec4 blendColor; layout (location = 0, index = 1) out vec4 blendColor;
@ -59,6 +61,7 @@ const vec3[6] normals = vec3[6](
vec3(0, 0, -1) vec3(0, 0, -1)
); );
uniform float nearPlane;
uniform Fog fog; uniform Fog fog;
uniform Fog waterFog; // TODO: Select fog from texture uniform Fog waterFog; // TODO: Select fog from texture
@ -170,30 +173,92 @@ void main() {
float normalVariation = normalVariations[faceNormal]; float normalVariation = normalVariations[faceNormal];
float lod = getLod(ivec3(startPosition), faceNormal, direction, variance); float lod = getLod(ivec3(startPosition), faceNormal, direction, variance);
ivec2 textureCoords = getTextureCoords(ivec3(startPosition), faceNormal); ivec2 textureCoords = getTextureCoords(ivec3(startPosition), faceNormal);
fragColor = mipMapSample(texture_sampler, textureCoords, textureIndex, lod)*vec4(ambientLight*normalVariation, 1); float depthBufferValue = texelFetch(depthTexture, ivec2(gl_FragCoord.xy), 0).r;
float fogFactor = 1.0/32.0; // TODO: This should be configurable by the block.
float fogDistance;
{
float distCameraTerrain = nearPlane*fogFactor/depthBufferValue;
float distFromCamera = abs(mvVertexPos.z)*fogFactor;
float distFromTerrain = distFromCamera - distCameraTerrain;
if(distCameraTerrain < 10) { // Resolution range is sufficient.
fogDistance = distFromTerrain;
} else {
// Here we have a few options to deal with this. We could for example weaken the fog effect to fit the entire range.
// I decided to keep the fog strength close to the camera and far away, with a fog-free region in between.
// I decided to this because I want far away fog to work (e.g. a distant ocean) as well as close fog(e.g. the top surface of the water when the player is under it)
if(distFromTerrain > -5 && depthBufferValue != 0) {
fogDistance = distFromTerrain;
} else if(distFromCamera < 5) {
fogDistance = distFromCamera - 10;
} else {
fogDistance = -5;
}
}
if (fragColor.a == 1) discard;
if (fog.activ) {
// TODO: Underwater fog if possible.
} }
fragColor.rgb *= fragColor.a; /*if(depthBufferValue == 0) {
blendColor.rgb = unpackColor(textureData[blockType].absorption); fogDistance = abs(mvVertexPos.z);
} else {
fogDistance = abs(mvVertexPos.z) - nearPlane/depthBufferValue;
}
fogDistance /= 16.0; // TODO: This should be configurable by the block.*/
bool opaqueFrontFace = false;
bool emptyBackFace = false;
//fogDistance = min(5, max(-5, fogDistance));
//if(fogDistance > 0) fogDistance -= 10;
if(gl_FrontFacing) {
fragColor = mipMapSample(texture_sampler, textureCoords, textureIndex, lod)*vec4(ambientLight*normalVariation, 1);
// Fake reflection: if (fragColor.a == 1) discard;
// TODO: Also allow this for opaque pixels.
// TODO: Change this when it rains.
// TODO: Normal mapping.
// TODO: Allow textures to contribute to this term.
fragColor.rgb += (textureData[blockType].reflectivity/2*vec3(snoise(normalize(reflect(direction, normals[faceNormal])))) + vec3(textureData[blockType].reflectivity))*ambientLight*normalVariation;
fragColor.rgb += mipMapSample(emissionSampler, textureCoords, textureIndex, lod).rgb;
blendColor.rgb *= 1 - fragColor.a;
fragColor.a = 1;
if (fog.activ) { if (fog.activ) {
fragColor = calcFog(mvVertexPos, fragColor, fog); // TODO: Underwater fog if possible.
blendColor.rgb *= fragColor.a; }
fragColor.rgb *= fragColor.a;
blendColor.rgb = unpackColor(textureData[blockType].absorption);
// Fake reflection:
// TODO: Also allow this for opaque pixels.
// TODO: Change this when it rains.
// TODO: Normal mapping.
// TODO: Allow textures to contribute to this term.
fragColor.rgb += (textureData[blockType].reflectivity/2*vec3(snoise(normalize(reflect(direction, normals[faceNormal])))) + vec3(textureData[blockType].reflectivity))*ambientLight*normalVariation;
fragColor.rgb += mipMapSample(emissionSampler, textureCoords, textureIndex, lod).rgb;
blendColor.rgb *= 1 - fragColor.a;
fragColor.a = 1; fragColor.a = 1;
if (fog.activ) {
fragColor = calcFog(mvVertexPos, fragColor, fog);
blendColor.rgb *= fragColor.a;
fragColor.a = 1;
}
// TODO: Consider the case that the terrain is too far away, which would case infinity/nan.
float fogFactor = exp(fogDistance);
fragColor = vec4(0.5, 1.0, 0.0, 1);
fragColor.a = 1.0/fogFactor;
fragColor.rgb *= fragColor.a;
if(opaqueFrontFace) {
blendColor.rgb = vec3(0);
} else {
fragColor.rgb -= vec3(0.5, 1.0, 0.0);
blendColor.rgb = vec3(1);
}
//blendColor.rgb = vec3(0);
//fragColor = vec4(1, 1, 1, 1);
} else {
if(emptyBackFace) {
fragColor = vec4(0, 0, 0, 1);
} else {
float fogFactor = exp(fogDistance);
fragColor.a = fogFactor;
fragColor.rgb -= vec3(0.5, 1.0, 0.0);
fragColor.rgb += fogFactor*vec3(0.5, 1.0, 0.0);
}
blendColor.rgb = vec3(1);
//blendColor.rgb = vec3(0);
//fragColor = vec4(1, 0, 0, 1);
} }
// TODO: Update the depth.
} }

View File

@ -7,6 +7,7 @@ uniform sampler2D color;
void main() { void main() {
fragColor = texture(color, texCoords); fragColor = texture(color, texCoords);
fragColor.rgb /= fragColor.a;
float maxColor = max(1.0, max(fragColor.r, max(fragColor.g, fragColor.b))); float maxColor = max(1.0, max(fragColor.r, max(fragColor.g, fragColor.b)));
fragColor.rgb = fragColor.rgb/maxColor; fragColor.rgb = fragColor.rgb/maxColor;
} }

View File

@ -404,6 +404,7 @@ pub const meshing = struct {
time: c_int, time: c_int,
visibilityMask: c_int, visibilityMask: c_int,
voxelSize: c_int, voxelSize: c_int,
nearPlane: c_int,
}; };
pub var uniforms: UniformStruct = undefined; pub var uniforms: UniformStruct = undefined;
pub var transparentUniforms: UniformStruct = undefined; pub var transparentUniforms: UniformStruct = undefined;
@ -462,6 +463,8 @@ pub const meshing = struct {
c.glUniform1i(uniforms.time, @as(u31, @truncate(time))); c.glUniform1i(uniforms.time, @as(u31, @truncate(time)));
c.glUniform1f(uniforms.nearPlane, renderer.zNear);
c.glBindVertexArray(vao); c.glBindVertexArray(vao);
} }
@ -483,6 +486,8 @@ pub const meshing = struct {
c.glUniform1i(transparentUniforms.time, @as(u31, @truncate(time))); c.glUniform1i(transparentUniforms.time, @as(u31, @truncate(time)));
c.glUniform1f(transparentUniforms.nearPlane, renderer.zNear);
c.glBindVertexArray(vao); c.glBindVertexArray(vao);
} }
@ -906,7 +911,7 @@ pub const meshing = struct {
}; };
} }
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void { pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void { // TODO: The way neighboring chunks are handled doesn't work well with transparency.
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function. std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
const chunk = self.chunk.load(.Monotonic) orelse return; // In the mean-time the mesh was discarded and recreated and all the data was lost. const chunk = self.chunk.load(.Monotonic) orelse return; // In the mean-time the mesh was discarded and recreated and all the data was lost.
self.opaqueMesh.resetToCore(); self.opaqueMesh.resetToCore();

View File

@ -1264,22 +1264,26 @@ pub const LargeBuffer = struct {
pub const FrameBuffer = struct { pub const FrameBuffer = struct {
frameBuffer: c_uint, frameBuffer: c_uint,
texture: c_uint, texture: c_uint,
hasDepthBuffer: bool, hasDepthTexture: bool,
depthBuffer: c_uint, depthTexture: c_uint,
pub fn init(self: *FrameBuffer, hasDepthBuffer: bool, textureFilter: c_int, textureWrap: c_int) void { pub fn init(self: *FrameBuffer, hasDepthTexture: bool, textureFilter: c_int, textureWrap: c_int) void {
self.* = FrameBuffer{ self.* = FrameBuffer{
.frameBuffer = undefined, .frameBuffer = undefined,
.texture = undefined, .texture = undefined,
.depthBuffer = undefined, .depthTexture = undefined,
.hasDepthBuffer = hasDepthBuffer, .hasDepthTexture = hasDepthTexture,
}; };
c.glGenFramebuffers(1, &self.frameBuffer); c.glGenFramebuffers(1, &self.frameBuffer);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer);
if(hasDepthBuffer) { if(hasDepthTexture) {
c.glGenRenderbuffers(1, &self.depthBuffer); c.glGenTextures(1, &self.depthTexture);
c.glBindRenderbuffer(c.GL_RENDERBUFFER, self.depthBuffer); c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture);
c.glFramebufferRenderbuffer(c.GL_FRAMEBUFFER, c.GL_DEPTH_ATTACHMENT, c.GL_RENDERBUFFER, self.depthBuffer); c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, textureFilter);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, textureFilter);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_S, textureWrap);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_WRAP_T, textureWrap);
c.glFramebufferTexture2D(c.GL_FRAMEBUFFER, c.GL_DEPTH_ATTACHMENT, c.GL_TEXTURE_2D, self.depthTexture, 0);
} }
c.glGenTextures(1, &self.texture); c.glGenTextures(1, &self.texture);
c.glBindTexture(c.GL_TEXTURE_2D, self.texture); c.glBindTexture(c.GL_TEXTURE_2D, self.texture);
@ -1294,17 +1298,17 @@ pub const FrameBuffer = struct {
pub fn deinit(self: *FrameBuffer) void { pub fn deinit(self: *FrameBuffer) void {
c.glDeleteFramebuffers(1, &self.frameBuffer); c.glDeleteFramebuffers(1, &self.frameBuffer);
if(self.hasDepthBuffer) { if(self.hasDepthTexture) {
c.glDeleteRenderbuffers(1, &self.depthBuffer); c.glDeleteRenderbuffers(1, &self.depthTexture);
} }
c.glDeleteTextures(1, &self.texture); c.glDeleteTextures(1, &self.texture);
} }
pub fn updateSize(self: *FrameBuffer, width: u31, height: u31, internalFormat: c_int) void { pub fn updateSize(self: *FrameBuffer, width: u31, height: u31, internalFormat: c_int) void {
c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer);
if(self.hasDepthBuffer) { if(self.hasDepthTexture) {
c.glBindRenderbuffer(c.GL_RENDERBUFFER, self.depthBuffer); c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture);
c.glRenderbufferStorage(c.GL_RENDERBUFFER, c.GL_DEPTH_COMPONENT32F, width, height); c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_DEPTH_COMPONENT32F, width, height, 0, c.GL_DEPTH_COMPONENT, c.GL_FLOAT, null);
} }
c.glBindTexture(c.GL_TEXTURE_2D, self.texture); c.glBindTexture(c.GL_TEXTURE_2D, self.texture);
@ -1331,6 +1335,12 @@ pub const FrameBuffer = struct {
c.glBindTexture(c.GL_TEXTURE_2D, self.texture); c.glBindTexture(c.GL_TEXTURE_2D, self.texture);
} }
pub fn bindDepthTexture(self: *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: *FrameBuffer) void {
c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer); c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer);
} }

View File

@ -28,7 +28,7 @@ const Mat4f = vec.Mat4f;
/// The number of milliseconds after which no more chunk meshes are created. This allows the game to run smoother on movement. /// The number of milliseconds after which no more chunk meshes are created. This allows the game to run smoother on movement.
const maximumMeshTime = 12; const maximumMeshTime = 12;
const zNear = 1e-10; pub const zNear = 1e-10;
var fogShader: graphics.Shader = undefined; var fogShader: graphics.Shader = undefined;
var fogUniforms: struct { var fogUniforms: struct {
@ -196,6 +196,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
gpu_performance_measuring.stopQuery(); gpu_performance_measuring.stopQuery();
// Render transparent chunk meshes: // Render transparent chunk meshes:
worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE3);
gpu_performance_measuring.startQuery(.transparent_rendering); gpu_performance_measuring.startQuery(.transparent_rendering);
chunk.meshing.bindTransparentShaderAndUniforms(game.projectionMatrix, ambientLight, time); chunk.meshing.bindTransparentShaderAndUniforms(game.projectionMatrix, ambientLight, time);
@ -203,9 +204,11 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
c.glUniform3fv(chunk.meshing.transparentUniforms.@"waterFog.color", 1, @ptrCast(&waterFog.color)); c.glUniform3fv(chunk.meshing.transparentUniforms.@"waterFog.color", 1, @ptrCast(&waterFog.color));
c.glUniform1f(chunk.meshing.transparentUniforms.@"waterFog.density", waterFog.density); c.glUniform1f(chunk.meshing.transparentUniforms.@"waterFog.density", waterFog.density);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_SRC1_COLOR); c.glBlendEquationSeparate(c.GL_FUNC_ADD, c.GL_FUNC_ADD);
c.glBlendFuncSeparate(c.GL_DST_ALPHA, c.GL_SRC1_COLOR, c.GL_DST_ALPHA, c.GL_ZERO);
c.glDepthFunc(c.GL_GEQUAL); c.glDepthFunc(c.GL_GEQUAL);
c.glDepthMask(c.GL_FALSE); c.glDepthMask(c.GL_FALSE);
c.glDisable(c.GL_CULL_FACE);
{ {
var i: usize = meshes.len; var i: usize = meshes.len;
while(true) { while(true) {
@ -214,6 +217,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
try meshes[i].renderTransparent(playerPos); try meshes[i].renderTransparent(playerPos);
} }
} }
c.glEnable(c.GL_CULL_FACE);
c.glDepthMask(c.GL_TRUE); c.glDepthMask(c.GL_TRUE);
c.glDepthFunc(c.GL_GREATER); c.glDepthFunc(c.GL_GREATER);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA); c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);