mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-05 12:17:53 -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;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
@ -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.
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user