mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 11:17:05 -04:00
Volumetric Fog. First draft.
This commit is contained in:
parent
fb5b6768bc
commit
6aa11ec1b8
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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.
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user