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;
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 outColor = vec3(0);
outColor += texelFetch(color, start, 0).rgb;
outColor += texelFetch(color, start + ivec2(0, 1), 0).rgb;
outColor += texelFetch(color, start + ivec2(1, 0), 0).rgb;
outColor += texelFetch(color, start + ivec2(1, 1), 0).rgb;
outColor += fetch(start);
outColor += fetch(start + ivec2(0, 1));
outColor += fetch(start + ivec2(1, 0));
outColor += fetch(start + ivec2(1, 1));
return outColor*0.25;
}

View File

@ -7,5 +7,5 @@ in vec2 texCoords;
layout(binding = 3) uniform sampler2D color;
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 emissionSampler;
layout(binding = 3) uniform sampler2D depthTexture;
layout (location = 0, index = 0) out vec4 fragColor;
layout (location = 0, index = 1) out vec4 blendColor;
@ -59,6 +61,7 @@ const vec3[6] normals = vec3[6](
vec3(0, 0, -1)
);
uniform float nearPlane;
uniform Fog fog;
uniform Fog waterFog; // TODO: Select fog from texture
@ -170,30 +173,92 @@ void main() {
float normalVariation = normalVariations[faceNormal];
float lod = getLod(ivec3(startPosition), faceNormal, direction, variance);
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;
blendColor.rgb = unpackColor(textureData[blockType].absorption);
/*if(depthBufferValue == 0) {
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:
// 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 (fragColor.a == 1) discard;
if (fog.activ) {
fragColor = calcFog(mvVertexPos, fragColor, fog);
blendColor.rgb *= fragColor.a;
if (fog.activ) {
// TODO: Underwater fog if possible.
}
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;
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() {
fragColor = texture(color, texCoords);
fragColor.rgb /= fragColor.a;
float maxColor = max(1.0, max(fragColor.r, max(fragColor.g, fragColor.b)));
fragColor.rgb = fragColor.rgb/maxColor;
}

View File

@ -404,6 +404,7 @@ pub const meshing = struct {
time: c_int,
visibilityMask: c_int,
voxelSize: c_int,
nearPlane: c_int,
};
pub var uniforms: UniformStruct = undefined;
pub var transparentUniforms: UniformStruct = undefined;
@ -462,6 +463,8 @@ pub const meshing = struct {
c.glUniform1i(uniforms.time, @as(u31, @truncate(time)));
c.glUniform1f(uniforms.nearPlane, renderer.zNear);
c.glBindVertexArray(vao);
}
@ -483,6 +486,8 @@ pub const meshing = struct {
c.glUniform1i(transparentUniforms.time, @as(u31, @truncate(time)));
c.glUniform1f(transparentUniforms.nearPlane, renderer.zNear);
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.
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();

View File

@ -1264,22 +1264,26 @@ pub const LargeBuffer = struct {
pub const FrameBuffer = struct {
frameBuffer: c_uint,
texture: c_uint,
hasDepthBuffer: bool,
depthBuffer: c_uint,
hasDepthTexture: bool,
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{
.frameBuffer = undefined,
.texture = undefined,
.depthBuffer = undefined,
.hasDepthBuffer = hasDepthBuffer,
.depthTexture = undefined,
.hasDepthTexture = hasDepthTexture,
};
c.glGenFramebuffers(1, &self.frameBuffer);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer);
if(hasDepthBuffer) {
c.glGenRenderbuffers(1, &self.depthBuffer);
c.glBindRenderbuffer(c.GL_RENDERBUFFER, self.depthBuffer);
c.glFramebufferRenderbuffer(c.GL_FRAMEBUFFER, c.GL_DEPTH_ATTACHMENT, c.GL_RENDERBUFFER, self.depthBuffer);
if(hasDepthTexture) {
c.glGenTextures(1, &self.depthTexture);
c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture);
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.glBindTexture(c.GL_TEXTURE_2D, self.texture);
@ -1294,17 +1298,17 @@ pub const FrameBuffer = struct {
pub fn deinit(self: *FrameBuffer) void {
c.glDeleteFramebuffers(1, &self.frameBuffer);
if(self.hasDepthBuffer) {
c.glDeleteRenderbuffers(1, &self.depthBuffer);
if(self.hasDepthTexture) {
c.glDeleteRenderbuffers(1, &self.depthTexture);
}
c.glDeleteTextures(1, &self.texture);
}
pub fn updateSize(self: *FrameBuffer, width: u31, height: u31, internalFormat: c_int) void {
c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.frameBuffer);
if(self.hasDepthBuffer) {
c.glBindRenderbuffer(c.GL_RENDERBUFFER, self.depthBuffer);
c.glRenderbufferStorage(c.GL_RENDERBUFFER, c.GL_DEPTH_COMPONENT32F, width, height);
if(self.hasDepthTexture) {
c.glBindTexture(c.GL_TEXTURE_2D, self.depthTexture);
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);
@ -1331,6 +1335,12 @@ pub const FrameBuffer = struct {
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 {
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.
const maximumMeshTime = 12;
const zNear = 1e-10;
pub const zNear = 1e-10;
var fogShader: graphics.Shader = undefined;
var fogUniforms: struct {
@ -196,6 +196,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
gpu_performance_measuring.stopQuery();
// Render transparent chunk meshes:
worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE3);
gpu_performance_measuring.startQuery(.transparent_rendering);
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.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.glDepthMask(c.GL_FALSE);
c.glDisable(c.GL_CULL_FACE);
{
var i: usize = meshes.len;
while(true) {
@ -214,6 +217,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
try meshes[i].renderTransparent(playerPos);
}
}
c.glEnable(c.GL_CULL_FACE);
c.glDepthMask(c.GL_TRUE);
c.glDepthFunc(c.GL_GREATER);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);