Handle the case that the player is inside the fog.

This commit is contained in:
IntegratedQuantum 2023-09-08 20:18:04 +02:00
parent 50946c352b
commit 2e5910a9cc
4 changed files with 147 additions and 8 deletions

View File

@ -6,8 +6,68 @@ in vec2 texCoords;
layout(binding = 3) uniform sampler2D color;
uniform sampler2D depthTexture;
uniform int blockType;
uniform float nearPlane;
struct TextureData {
int textureIndices[6];
uint absorption;
float reflectivity;
float fogStrength;
uint fogColor;
};
layout(std430, binding = 1) buffer _textureData
{
TextureData textureData[];
};
vec3 unpackColor(uint color) {
return vec3(
color>>16 & 255u,
color>>8 & 255u,
color & 255u
)/255.0;
}
float calculateFogDistance(float depthBufferValue) {
float fogStrength = textureData[blockType].fogStrength;
float distCameraTerrain = nearPlane*fogStrength/depthBufferValue;
float distFromCamera = 0;
float distFromTerrain = distFromCamera - distCameraTerrain;
if(distCameraTerrain < 10) { // Resolution range is sufficient.
return 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) {
return distFromTerrain;
} else if(distFromCamera < 5) {
return distFromCamera - 10;
} else {
return -5;
}
}
}
vec3 fetch(ivec2 pos) {
vec4 rgba = texelFetch(color, pos, 0);
if(blockType != 0) { // TODO: Handle air fog as well.
float fogDistance = calculateFogDistance(texelFetch(depthTexture, pos, 0).r);
vec3 fogColor = unpackColor(textureData[blockType].fogColor);
float fogFactor = exp(fogDistance);
vec4 sourceColor = vec4(fogColor, 1);
sourceColor.a = 1.0/fogFactor;
sourceColor.rgb *= sourceColor.a;
sourceColor.rgb -= fogColor;
vec3 source2Color = vec3(1);
rgba = vec4(
source2Color*rgba.rgb + rgba.a*sourceColor.rgb,
rgba.a*sourceColor.a
);
}
if(rgba.a < 1) return vec3(0); // Prevent t-junctions from transparency from making a huge mess.
return rgba.rgb/rgba.a;
}

View File

@ -5,8 +5,68 @@ in vec2 texCoords;
uniform sampler2D color;
uniform sampler2D depthTexture;
uniform int blockType;
uniform float nearPlane;
struct TextureData {
int textureIndices[6];
uint absorption;
float reflectivity;
float fogStrength;
uint fogColor;
};
layout(std430, binding = 1) buffer _textureData
{
TextureData textureData[];
};
vec3 unpackColor(uint color) {
return vec3(
color>>16 & 255u,
color>>8 & 255u,
color & 255u
)/255.0;
}
float calculateFogDistance(float depthBufferValue) {
float fogStrength = textureData[blockType].fogStrength;
float distCameraTerrain = nearPlane*fogStrength/depthBufferValue;
float distFromCamera = 0;
float distFromTerrain = distFromCamera - distCameraTerrain;
if(distCameraTerrain < 10) { // Resolution range is sufficient.
return 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) {
return distFromTerrain;
} else if(distFromCamera < 5) {
return distFromCamera - 10;
} else {
return -5;
}
}
}
void main() {
fragColor = texture(color, texCoords);
if(blockType != 0) { // TODO: Handle air fog as well.
float fogDistance = calculateFogDistance(texelFetch(depthTexture, ivec2(gl_FragCoord.xy), 0).r);
vec3 fogColor = unpackColor(textureData[blockType].fogColor);
float fogFactor = exp(fogDistance);
vec4 sourceColor = vec4(fogColor, 1);
sourceColor.a = 1.0/fogFactor;
sourceColor.rgb *= sourceColor.a;
sourceColor.rgb -= fogColor;
vec3 source2Color = vec3(1);
fragColor = vec4(
source2Color*fragColor.rgb + fragColor.a*sourceColor.rgb,
fragColor.a*sourceColor.a
);
}
fragColor.rgb /= fragColor.a;
float maxColor = max(1.0, max(fragColor.r, max(fragColor.g, fragColor.b)));
fragColor.rgb = fragColor.rgb/maxColor;

View File

@ -18,7 +18,6 @@ pub const Samples = enum(u8) {
chunk_rendering,
entity_rendering,
transparent_rendering,
normalization,
bloom_extract_downsample,
bloom_first_pass,
bloom_second_pass,
@ -33,7 +32,6 @@ const names = [_][]const u8 {
"Chunk Rendering",
"Entity Rendering",
"Transparent Rendering",
"Normalization",
"Bloom - Extract color and downsample",
"Bloom - First Pass",
"Bloom - Second Pass",

View File

@ -40,6 +40,9 @@ var fogUniforms: struct {
var deferredRenderPassShader: graphics.Shader = undefined;
var deferredUniforms: struct {
color: c_int,
depthTexture: c_int,
blockType: c_int,
nearPlane: c_int,
} = undefined;
pub var activeFrameBuffer: c_uint = 0;
@ -193,6 +196,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE3);
gpu_performance_measuring.startQuery(.transparent_rendering);
c.glTextureBarrier();
chunk.meshing.bindTransparentShaderAndUniforms(game.projectionMatrix, ambientLight, time);
c.glBlendEquationSeparate(c.GL_FUNC_ADD, c.GL_FUNC_ADD);
@ -226,14 +230,20 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
fogShader.bind();
// TODO: Draw the water fog if the player is underwater.
const playerBlock = RenderStructure.getBlock(@intFromFloat(@floor(playerPos[0])), @intFromFloat(@floor(playerPos[1])), @intFromFloat(@floor(playerPos[2]))) orelse blocks.Block{.typ = 0, .data = 0};
if(settings.bloom) {
Bloom.render(lastWidth, lastHeight);
Bloom.render(lastWidth, lastHeight, playerBlock);
}
gpu_performance_measuring.startQuery(.final_copy);
worldFrameBuffer.unbind();
worldFrameBuffer.bindTexture(c.GL_TEXTURE3);
worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE4);
worldFrameBuffer.unbind();
deferredRenderPassShader.bind();
c.glUniform1i(deferredUniforms.color, 3);
c.glUniform1i(deferredUniforms.depthTexture, 4);
c.glUniform1i(deferredUniforms.blockType, playerBlock.typ);
c.glUniform1f(deferredUniforms.nearPlane, zNear);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, activeFrameBuffer);
@ -264,13 +274,18 @@ const Bloom = struct {
var secondPassShader: graphics.Shader = undefined;
var colorExtractAndDownsampleShader: graphics.Shader = undefined;
var upscaleShader: graphics.Shader = undefined;
var colorExtractUniforms: struct {
depthTexture: c_int,
blockType: c_int,
nearPlane: c_int,
} = undefined;
pub fn init() !void {
buffer1.init(false, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE);
buffer2.init(false, c.GL_NEAREST, c.GL_CLAMP_TO_EDGE);
firstPassShader = try graphics.Shader.init("assets/cubyz/shaders/bloom/first_pass.vs", "assets/cubyz/shaders/bloom/first_pass.fs");
secondPassShader = try graphics.Shader.init("assets/cubyz/shaders/bloom/second_pass.vs", "assets/cubyz/shaders/bloom/second_pass.fs");
colorExtractAndDownsampleShader = try graphics.Shader.init("assets/cubyz/shaders/bloom/color_extractor_downsample.vs", "assets/cubyz/shaders/bloom/color_extractor_downsample.fs");
colorExtractAndDownsampleShader = try graphics.Shader.initAndGetUniforms("assets/cubyz/shaders/bloom/color_extractor_downsample.vs", "assets/cubyz/shaders/bloom/color_extractor_downsample.fs", &colorExtractUniforms);
upscaleShader = try graphics.Shader.init("assets/cubyz/shaders/bloom/upscale.vs", "assets/cubyz/shaders/bloom/upscale.fs");
}
@ -282,10 +297,14 @@ const Bloom = struct {
upscaleShader.deinit();
}
fn extractImageDataAndDownsample() void {
fn extractImageDataAndDownsample(playerBlock: blocks.Block) void {
colorExtractAndDownsampleShader.bind();
worldFrameBuffer.bindTexture(c.GL_TEXTURE3);
worldFrameBuffer.bindDepthTexture(c.GL_TEXTURE4);
buffer1.bind();
c.glUniform1i(colorExtractUniforms.depthTexture, 4);
c.glUniform1i(colorExtractUniforms.blockType, playerBlock.typ);
c.glUniform1f(colorExtractUniforms.nearPlane, zNear);
c.glBindVertexArray(graphics.draw.rectVAO);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
}
@ -314,7 +333,7 @@ const Bloom = struct {
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
}
pub fn render(currentWidth: u31, currentHeight: u31) void {
pub fn render(currentWidth: u31, currentHeight: u31, playerBlock: blocks.Block) void {
if(width != currentWidth or height != currentHeight) {
width = currentWidth;
height = currentHeight;
@ -326,9 +345,10 @@ const Bloom = struct {
gpu_performance_measuring.startQuery(.bloom_extract_downsample);
c.glDisable(c.GL_DEPTH_TEST);
c.glDisable(c.GL_CULL_FACE);
c.glDepthMask(c.GL_FALSE);
c.glViewport(0, 0, width/2, height/2);
extractImageDataAndDownsample();
extractImageDataAndDownsample(playerBlock);
gpu_performance_measuring.stopQuery();
gpu_performance_measuring.startQuery(.bloom_first_pass);
firstPass();
@ -341,6 +361,7 @@ const Bloom = struct {
c.glBlendFunc(c.GL_ONE, c.GL_ONE);
upscale();
c.glDepthMask(c.GL_TRUE);
c.glEnable(c.GL_DEPTH_TEST);
c.glEnable(c.GL_CULL_FACE);
c.glBlendFunc(c.GL_SRC_ALPHA, c.GL_ONE_MINUS_SRC_ALPHA);