Реймарчинг в уменьшеном разрешении

This commit is contained in:
2026-08-05 18:54:37 +04:00
parent 8ac636d114
commit 4a057468e3
4 changed files with 418 additions and 265 deletions
@@ -22,55 +22,61 @@ import java.nio.FloatBuffer;
public class PostProcessingManager {
private static GLProgram program;
private static GLProgram raymarchProgram;
private static GLProgram compositeProgram;
private static GLProgram depthDownsampleProgram;
/*
* Матрицы
*/
private static int dd_depthSamplerLocation;
private static int dd_screenSizeLocation;
// Фреймбуфер для scaled raymarching
private static int scaledFBO;
private static int scaledTexture;
private static int scaledWidth;
private static int scaledHeight;
private static final float scale = 0.5f;
private static final Matrix4f projection = new Matrix4f();
private static final Matrix4f inverseProjection = new Matrix4f();
private static final FloatBuffer projectionBuffer =
BufferUtils.createFloatBuffer(16);
private static final FloatBuffer inverseProjectionBuffer =
BufferUtils.createFloatBuffer(16);
private static final FloatBuffer projectionBuffer = BufferUtils.createFloatBuffer(16);
private static final FloatBuffer inverseProjectionBuffer = BufferUtils.createFloatBuffer(16);
/*
* Uniform locations
* Uniform locations - Raymarch Program
*/
private static int rm_projectionLocation;
private static int rm_inverseProjectionLocation;
private static int rm_depthSamplerLocation;
private static int rm_fogVolumeLocation;
private static int rm_lightVolumeLocation;
private static int rm_fogOriginLocation;
private static int rm_ringOffsetLocation;
private static int rm_cameraPositionLocation;
private static int rm_screenSizeLocation;
private static int rm_viewLocation;
private static int rm_inverseViewLocation;
private static int rm_fogColorLocation;
private static int rm_skyBrightnessLocation;
private static int projectionLocation;
private static int inverseProjectionLocation;
/*
* Uniform locations - Composite Program (JBU)
*/
private static int comp_diffuseSamplerLocation;
private static int comp_fogSamplerLocation;
private static int comp_depthSamplerLocation;
private static int comp_scaledDepthSamplerLocation;
private static int comp_screenSizeLocation;
private static int comp_scaledScreenSizeLocation;
private static int comp_inverseProjectionLocation;
private static int diffuseSamplerLocation;
private static int depthSamplerLocation;
private static int cameraPositionLocation;
private static int screenSizeLocation;
private static int scaledDepthTexture;
private static final Matrix4f view = new Matrix4f();
private static final Matrix4f inverseView = new Matrix4f();
private static final FloatBuffer viewBuffer =
BufferUtils.createFloatBuffer(16);
private static final FloatBuffer inverseViewBuffer =
BufferUtils.createFloatBuffer(16);
private static int viewLocation;
private static int inverseViewLocation;
private static int fogVolumeLocation;
private static int lightVolumeLocation;
private static int fogOriginLocation;
private static int ringOffsetLocation;
private static final FloatBuffer viewBuffer = BufferUtils.createFloatBuffer(16);
private static final FloatBuffer inverseViewBuffer = BufferUtils.createFloatBuffer(16);
private static float fogR, fogG, fogB;
private static int fogColorLocation;
private static int skyBrightnessLocation;
public static void setFogColor(float r, float g, float b) {
fogR = r;
@@ -79,56 +85,116 @@ public class PostProcessingManager {
}
public static void init() {
if (program != null)
if (raymarchProgram != null)
return;
setFogColor(0.75f, 0.80f, 0.90f);
GLShader vertex = new GLShader(
// Инициализация raymarch программы
GLShader rmVertex = new GLShader(
GL20.GL_VERTEX_SHADER,
ResourceUtil.load("shaders/fullscreen.vert")
);
GLShader fragment = new GLShader(
GLShader rmFragment = new GLShader(
GL20.GL_FRAGMENT_SHADER,
ResourceUtil.load("shaders/fog_raymarching.frag")
);
raymarchProgram = new GLProgram(rmVertex, rmFragment);
program = new GLProgram(vertex, fragment);
rm_projectionLocation = raymarchProgram.uniform("Projection");
rm_inverseProjectionLocation = raymarchProgram.uniform("InverseProjection");
rm_depthSamplerLocation = raymarchProgram.uniform("DepthSampler");
rm_fogVolumeLocation = raymarchProgram.uniform("FogVolume");
rm_lightVolumeLocation = raymarchProgram.uniform("LightVolume");
rm_fogOriginLocation = raymarchProgram.uniform("FogOrigin");
rm_ringOffsetLocation = raymarchProgram.uniform("RingBlockOffset");
rm_cameraPositionLocation = raymarchProgram.uniform("CameraPosition");
rm_screenSizeLocation = raymarchProgram.uniform("ScreenSize");
rm_viewLocation = raymarchProgram.uniform("View");
rm_inverseViewLocation = raymarchProgram.uniform("InverseView");
rm_fogColorLocation = raymarchProgram.uniform("FogColor");
rm_skyBrightnessLocation = raymarchProgram.uniform("SkyBrightness");
projectionLocation = program.uniform("Projection");
inverseProjectionLocation = program.uniform("InverseProjection");
rmVertex.delete();
rmFragment.delete();
diffuseSamplerLocation = program.uniform("DiffuseSampler");
depthSamplerLocation = program.uniform("DepthSampler");
// Инициализация composite программы с JBU
GLShader compVertex = new GLShader(
GL20.GL_VERTEX_SHADER,
ResourceUtil.load("shaders/fullscreen.vert")
);
GLShader compFragment = new GLShader(
GL20.GL_FRAGMENT_SHADER,
ResourceUtil.load("shaders/fog_composite.frag")
);
compositeProgram = new GLProgram(compVertex, compFragment);
fogVolumeLocation = program.uniform("FogVolume");
lightVolumeLocation = program.uniform("LightVolume");
comp_diffuseSamplerLocation = compositeProgram.uniform("DiffuseSampler");
comp_fogSamplerLocation = compositeProgram.uniform("FogSampler");
comp_depthSamplerLocation = compositeProgram.uniform("DepthSampler");
comp_scaledDepthSamplerLocation = compositeProgram.uniform("ScaledDepthSampler");
comp_screenSizeLocation = compositeProgram.uniform("ScreenSize");
comp_scaledScreenSizeLocation = compositeProgram.uniform("ScaledScreenSize");
comp_inverseProjectionLocation = compositeProgram.uniform("InverseProjection");
fogOriginLocation = program.uniform("FogOrigin");
ringOffsetLocation = program.uniform("RingBlockOffset");
compVertex.delete();
compFragment.delete();
cameraPositionLocation = program.uniform("CameraPosition");
screenSizeLocation = program.uniform("ScreenSize");
GLShader ddVertex = new GLShader(
GL20.GL_VERTEX_SHADER,
ResourceUtil.load("shaders/fullscreen.vert")
);
GLShader ddFragment = new GLShader(
GL20.GL_FRAGMENT_SHADER,
ResourceUtil.load("shaders/depth_downsample.frag")
);
depthDownsampleProgram = new GLProgram(ddVertex, ddFragment);
viewLocation = program.uniform("View");
inverseViewLocation = program.uniform("InverseView");
dd_depthSamplerLocation = depthDownsampleProgram.uniform("DepthSampler");
dd_screenSizeLocation = depthDownsampleProgram.uniform("ScreenSize");
fogColorLocation = program.uniform("FogColor");
skyBrightnessLocation = program.uniform("SkyBrightness");
ddVertex.delete();
ddFragment.delete();
FullscreenQuad.init();
vertex.delete();
fragment.delete();
System.out.println("VEILA shaders compiled!");
}
System.out.println("VEILA shader compiled!");
private static void initScaledFramebuffer(int width, int height) {
scaledWidth = (int)(width * scale);
scaledHeight = (int)(height * scale);
scaledFBO = GL30.glGenFramebuffers();
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, scaledFBO);
scaledTexture = GL11.glGenTextures();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, scaledTexture);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_RGBA16F, scaledWidth, scaledHeight, 0, GL11.GL_RGBA, GL11.GL_FLOAT, 0);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, scaledTexture, 0);
// Half-res depth buffer
scaledDepthTexture = GL11.glGenTextures();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, scaledDepthTexture);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL30.GL_R32F, scaledWidth, scaledHeight, 0, GL11.GL_RED, GL11.GL_FLOAT, 0);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT1, GL11.GL_TEXTURE_2D, scaledDepthTexture, 0);
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
}
public static void render(float tickDelta) {
if (program == null)
if (raymarchProgram == null)
return;
MinecraftClient client = MinecraftClient.getInstance();
@@ -141,174 +207,176 @@ public class PostProcessingManager {
int width = framebuffer.textureWidth;
int height = framebuffer.textureHeight;
GL30.glBindFramebuffer(
GL30.GL_READ_FRAMEBUFFER,
framebuffer.fbo
);
// Инициализация scaled фреймбуфера
if (scaledFBO == 0 || scaledWidth != (int)(width * scale) || scaledHeight != (int)(height * scale)) {
if (scaledFBO != 0) {
GL30.glDeleteFramebuffers(scaledFBO);
GL11.glDeleteTextures(scaledTexture);
GL11.glDeleteTextures(scaledDepthTexture);
}
initScaledFramebuffer(width, height);
}
GL30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, framebuffer.fbo);
DepthCopy.init(width, height);
DepthCopy.copy(width, height);
program.bind();
renderDepthDownsample(width, height);
/*
* Diffuse
*/
// === Этап 1: Raymarching в scaled разрешении ===
renderRaymarching(tickDelta, client);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(
GL11.GL_TEXTURE_2D,
framebuffer.getColorAttachment()
);
GL20.glUniform1i(diffuseSamplerLocation, 0);
/*
* Depth
*/
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(
GL11.GL_TEXTURE_2D,
DepthCopy.getTexture()
);
GL20.glUniform1i(depthSamplerLocation, 1);
GL13.glActiveTexture(GL13.GL_TEXTURE2);
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
FogSimulationManager.getCurrentDensityTexture()
);
GL20.glUniform1i(
fogVolumeLocation,
2
);
GL13.glActiveTexture(GL13.GL_TEXTURE3);
GL11.glBindTexture(
GL12.GL_TEXTURE_3D,
FogLightVolume.getTexture()
);
GL20.glUniform1i(lightVolumeLocation, 3);
GL20.glUniform2i(
fogOriginLocation,
FogSystem.originChunkX() * FogWorldVolume.CHUNK_SIZE,
FogSystem.originChunkZ() * FogWorldVolume.CHUNK_SIZE
);
GL20.glUniform2i(
ringOffsetLocation,
FogSystem.ringChunkOffsetX() * FogWorldVolume.CHUNK_SIZE,
FogSystem.ringChunkOffsetZ() * FogWorldVolume.CHUNK_SIZE
);
/*
* Матрицы
*/
updateProjectionMatrices();
GL20.glUniformMatrix4fv(
projectionLocation,
false,
projectionBuffer
);
GL20.glUniformMatrix4fv(
inverseProjectionLocation,
false,
inverseProjectionBuffer
);
GL20.glUniformMatrix4fv(
viewLocation,
false,
viewBuffer
);
GL20.glUniformMatrix4fv(
inverseViewLocation,
false,
inverseViewBuffer
);
/*
* Камера
*/
Camera camera = client.gameRenderer.getCamera();
Vec3d pos = camera.getPos();
GL20.glUniform3f(
cameraPositionLocation,
(float) pos.x,
(float) pos.y,
(float) pos.z
);
/*
* Размер экрана
*/
GL20.glUniform2f(
screenSizeLocation,
width,
height
);
/*
* Цвет тумана
*/
GL20.glUniform3f(
fogColorLocation,
fogR,
fogG,
fogB
);
/*
* Яркость тумана
*/
GL20.glUniform1f(
skyBrightnessLocation,
client.world.getSkyBrightness(tickDelta)
);
FullscreenQuad.draw();
GL13.glActiveTexture(GL13.GL_TEXTURE3);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE2);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
program.unbind();
GL30.glBindFramebuffer(
GL30.GL_READ_FRAMEBUFFER,
0
);
// === Этап 2: Композитинг с JBU в полном разрешении ===
renderComposite(framebuffer, width, height);
GL30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
}
private static void renderDepthDownsample(int width, int height) {
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, scaledFBO);
// Настраиваем draw buffers для MRT
GL30.glDrawBuffer(GL30.GL_COLOR_ATTACHMENT1);
GL11.glViewport(0, 0, scaledWidth, scaledHeight);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
depthDownsampleProgram.bind();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, DepthCopy.getTexture());
GL20.glUniform1i(dd_depthSamplerLocation, 0);
GL20.glUniform2f(dd_screenSizeLocation, width, height);
FullscreenQuad.draw();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
depthDownsampleProgram.unbind();
}
private static void renderRaymarching(float tickDelta, MinecraftClient client) {
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, scaledFBO);
// Настраиваем draw buffer для raymarching
GL30.glDrawBuffer(GL30.GL_COLOR_ATTACHMENT0);
GL11.glViewport(0, 0, scaledWidth, scaledHeight);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
raymarchProgram.bind();
// Depth
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, scaledDepthTexture);
GL20.glUniform1i(rm_depthSamplerLocation, 0);
// Fog Volume
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, FogSimulationManager.getCurrentDensityTexture());
GL20.glUniform1i(rm_fogVolumeLocation, 1);
// Light Volume
GL13.glActiveTexture(GL13.GL_TEXTURE2);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, FogLightVolume.getTexture());
GL20.glUniform1i(rm_lightVolumeLocation, 2);
GL20.glUniform2i(rm_fogOriginLocation,
FogSystem.originChunkX() * FogWorldVolume.CHUNK_SIZE,
FogSystem.originChunkZ() * FogWorldVolume.CHUNK_SIZE);
GL20.glUniform2i(rm_ringOffsetLocation,
FogSystem.ringChunkOffsetX() * FogWorldVolume.CHUNK_SIZE,
FogSystem.ringChunkOffsetZ() * FogWorldVolume.CHUNK_SIZE);
// Матрицы
updateProjectionMatrices();
GL20.glUniformMatrix4fv(rm_projectionLocation, false, projectionBuffer);
GL20.glUniformMatrix4fv(rm_inverseProjectionLocation, false, inverseProjectionBuffer);
GL20.glUniformMatrix4fv(rm_viewLocation, false, viewBuffer);
GL20.glUniformMatrix4fv(rm_inverseViewLocation, false, inverseViewBuffer);
// Камера
Camera camera = client.gameRenderer.getCamera();
Vec3d pos = camera.getPos();
GL20.glUniform3f(rm_cameraPositionLocation, (float) pos.x, (float) pos.y, (float) pos.z);
// Размер экрана (scaled)
GL20.glUniform2f(rm_screenSizeLocation, scaledWidth, scaledHeight);
// Цвет тумана
GL20.glUniform3f(rm_fogColorLocation, fogR, fogG, fogB);
// Яркость неба
GL20.glUniform1f(rm_skyBrightnessLocation, client.world.getSkyBrightness(tickDelta));
FullscreenQuad.draw();
// Очистка текстур
GL13.glActiveTexture(GL13.GL_TEXTURE2);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(GL12.GL_TEXTURE_3D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
raymarchProgram.unbind();
}
private static void renderComposite(Framebuffer framebuffer, int width, int height) {
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer.fbo);
GL11.glViewport(0, 0, width, height);
compositeProgram.bind();
// Original scene (Diffuse)
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, framebuffer.getColorAttachment());
GL20.glUniform1i(comp_diffuseSamplerLocation, 0);
// Scaled fog results
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, scaledTexture);
GL20.glUniform1i(comp_fogSamplerLocation, 1);
// Full-res depth для JBU
GL13.glActiveTexture(GL13.GL_TEXTURE2);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, DepthCopy.getTexture());
GL20.glUniform1i(comp_depthSamplerLocation, 2);
// Half-res depth для JBU
GL13.glActiveTexture(GL13.GL_TEXTURE3);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, scaledDepthTexture);
GL20.glUniform1i(comp_scaledDepthSamplerLocation, 3);
// Матрицы для линеаризации depth
updateProjectionMatrices();
GL20.glUniformMatrix4fv(comp_inverseProjectionLocation, false, inverseProjectionBuffer);
// Screen size
GL20.glUniform2f(comp_screenSizeLocation, width, height);
GL20.glUniform2f(comp_scaledScreenSizeLocation, scaledWidth, scaledHeight);
FullscreenQuad.draw();
// Очистка
GL13.glActiveTexture(GL13.GL_TEXTURE3);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE2);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE1);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
compositeProgram.unbind();
}
private static void updateProjectionMatrices() {
projection.set(RenderSystem.getProjectionMatrix());
inverseProjection
.set(projection)
.invert();
inverseProjection.set(projection).invert();
projectionBuffer.clear();
projection.get(projectionBuffer);
@@ -318,12 +386,8 @@ public class PostProcessingManager {
}
public static void updateViewMatrix(Matrix4f matrix) {
view.set(matrix);
inverseView
.set(matrix)
.invert();
inverseView.set(matrix).invert();
viewBuffer.clear();
view.get(viewBuffer);
@@ -331,5 +395,4 @@ public class PostProcessingManager {
inverseViewBuffer.clear();
inverseView.get(inverseViewBuffer);
}
}
@@ -0,0 +1,23 @@
#version 150
uniform sampler2D DepthSampler;
uniform vec2 ScreenSize;
in vec2 texCoord;
out vec4 FragColor;
void main()
{
vec2 texelSize = 1.0 / ScreenSize;
// Берём минимальный depth из 2x2 блока (ближайший объект)
float d00 = texture(DepthSampler, texCoord + vec2(-0.5, -0.5) * texelSize).r;
float d10 = texture(DepthSampler, texCoord + vec2( 0.5, -0.5) * texelSize).r;
float d01 = texture(DepthSampler, texCoord + vec2(-0.5, 0.5) * texelSize).r;
float d11 = texture(DepthSampler, texCoord + vec2( 0.5, 0.5) * texelSize).r;
float minDepth = min(min(d00, d10), min(d01, d11));
FragColor = vec4(minDepth, 0.0, 0.0, 1.0);
}
@@ -0,0 +1,106 @@
#version 150
uniform sampler2D DiffuseSampler;
uniform sampler2D FogSampler;
uniform sampler2D DepthSampler;
uniform sampler2D ScaledDepthSampler;
uniform vec2 ScreenSize;
uniform vec2 ScaledScreenSize;
uniform mat4 InverseProjection;
in vec2 texCoord;
out vec4 FragColor;
//------------------------------------------------------------
// Параметры JBU
//------------------------------------------------------------
const int WINDOW_SIZE = 5;
const int WINDOW_RADIUS = WINDOW_SIZE / 2;
const float SIGMA_SPATIAL = 2.0;
const float SIGMA_SPECTRAL = 0.3; // Меньше = резче границы
const float MIN_WEIGHT = 0.0001;
//------------------------------------------------------------
// Предвычисленные spatial weights для окна 5x5
const float spatialWeights[25] = float[25](
0.003765, 0.015019, 0.023792, 0.015019, 0.003765,
0.015019, 0.059912, 0.094907, 0.059912, 0.015019,
0.023792, 0.094907, 0.150342, 0.094907, 0.023792,
0.015019, 0.059912, 0.094907, 0.059912, 0.015019,
0.003765, 0.015019, 0.023792, 0.015019, 0.003765
);
//------------------------------------------------------------
float linearizeDepth(float depth)
{
vec4 clip = vec4(0.0, 0.0, depth * 2.0 - 1.0, 1.0);
vec4 view = InverseProjection * clip;
return -view.z / view.w;
}
//------------------------------------------------------------
void main()
{
vec3 sceneColor = texture(DiffuseSampler, texCoord).rgb;
// Линеаризованный depth текущего full-res пикселя
float depthFull = linearizeDepth(texture(DepthSampler, texCoord).r);
vec2 lowResCoord = texCoord;
vec2 texelSize = 1.0 / ScaledScreenSize;
vec4 sumColor = vec4(0.0);
float sumWeight = 0.0;
int sampleIndex = 0;
for (int y = -WINDOW_RADIUS; y <= WINDOW_RADIUS; y++) {
for (int x = -WINDOW_RADIUS; x <= WINDOW_RADIUS; x++) {
vec2 neighborCoord = lowResCoord + vec2(float(x), float(y)) * texelSize;
if (any(lessThan(neighborCoord, vec2(0.0))) || any(greaterThan(neighborCoord, vec2(1.0)))) {
sampleIndex++;
continue;
}
float wSpatial = spatialWeights[sampleIndex];
// Линеаризованный depth из half-res depth buffer
float depthNeighbor = linearizeDepth(texture(ScaledDepthSampler, neighborCoord).r);
float depthDiff = abs(depthFull - depthNeighbor);
// Range weight с линеаризованным depth в метрах
float wRange = exp(-(depthDiff * depthDiff) / (2.0 * SIGMA_SPECTRAL * SIGMA_SPECTRAL));
float weight = wSpatial * wRange;
vec4 fogSample = texture(FogSampler, neighborCoord);
sumColor += fogSample * weight;
sumWeight += weight;
sampleIndex++;
}
}
vec4 result;
if (sumWeight < MIN_WEIGHT) {
result = texture(FogSampler, lowResCoord);
} else {
result = sumColor / sumWeight;
}
vec3 scattered = result.rgb;
float transmittance = result.a;
vec3 finalColor = sceneColor * transmittance + scattered;
FragColor = vec4(finalColor, 1.0);
}
@@ -1,6 +1,5 @@
#version 150
uniform sampler2D DiffuseSampler;
uniform sampler2D DepthSampler;
uniform sampler3D FogVolume;
uniform sampler3D LightVolume;
@@ -38,22 +37,9 @@ const float GAMMA = 2.0;
const bool USE_JITTERING = false;
// Физические коэффициенты volumetric rendering
// EXTINCTION: насколько сильно туман поглощает/рассеивает свет (0.1-2.0)
// Больше значение = туман сильнее затеняет далёкие объекты
const float EXTINCTION_COEFF = 1.0;
// SCATTERING: насколько сильно туман сам светится от источников
// Обычно равен EXTINCTION (консервативная среда), но можно варьировать
const float SCATTERING_COEFF = 1.0;
// Ambient — базовое освещение среды (воздух, микрочастицы).
// Не зависит от времени суток и от skyLight/blockLight.
// Даёт минимальную видимость тумана даже в полной темноте.
const float AMBIENT_INTENSITY = 0.10;
// Порог transmittance для early termination (оптимизация)
// Если transmittance упал ниже этого значения, дальнейшие шаги ничего не внесут
const float MIN_TRANSMITTANCE = 0.01;
//------------------------------------------------------------
@@ -145,8 +131,6 @@ float mapParameterToDistance(float t)
void main()
{
vec4 sceneColor = texture(DiffuseSampler, texCoord);
vec3 worldPos = reconstructWorldPosition(texCoord);
vec3 rayDir = normalize(worldPos - CameraPosition);
@@ -156,14 +140,10 @@ void main()
vec3 samplePos = CameraPosition;
float currentDistance = 0.0;
// Transmittance: сколько света от сцены дошло до текущего шага
// Начинается с 1.0 (всё доходит) и экспоненциально падает
float transmittance = 1.0;
// Накопленный in-scattered свет (взвешенный по transmittance)
float skyScattered = 0.0;
float blockScattered = 0.0;
float ambientScattered = 0.0;
float jitter = 0.0;
@@ -192,36 +172,25 @@ void main()
float d = density(samplePos);
// Оптическая толщина этого шага
float opticalDepth = d * stepSize * EXTINCTION_COEFF;
if (d > 0.0) {
// Получаем освещение в текущей точке
vec2 lightValues = light(samplePos);
float totalLightAtPoint = lightValues.x + lightValues.y;
// Базовая формула in-scattering для каждого источника
float baseScattering = d * stepSize * SCATTERING_COEFF;
// In-scattering: свет, рассеянный в камерy из этого шага.
// Умножается на transmittance до этого шага (дальний туман затенён ближним).
// Умножается на плотность (больше частиц = больше рассеяния).
float stepScattering = totalLightAtPoint * d * stepSize * SCATTERING_COEFF;
// Вклад ослабляется текущим transmittance
skyScattered += lightValues.x * baseScattering * transmittance;
//Свет от блоков не должен делать значимо ярче те воксели, которые и так подсвечены солнцем. Солнечный свет очень яркий, с ним сложно сравниться
blockScattered += lightValues.y * baseScattering * transmittance*((lightValues.y-lightValues.x)*0.9+0.1);
blockScattered += lightValues.y * baseScattering * transmittance * ((lightValues.y - lightValues.x) * 0.9 + 0.1);
ambientScattered += AMBIENT_INTENSITY * baseScattering * transmittance;
// Обновляем transmittance: Beer-Lambert law
transmittance *= exp(-opticalDepth);
}
currentDistance = nextDistance;
// Early termination: если transmittance упал почти до нуля,
// дальние шаги ничего не внесут в итоговый цвет
if (transmittance < MIN_TRANSMITTANCE) {
transmittance = 0.0;
break;
@@ -232,18 +201,10 @@ void main()
}
}
// Суммарное освещение, рассеянное в камеру
float totalScattered = skyScattered + blockScattered + ambientScattered;
// Финальный цвет:
// - sceneColor × transmittance: сцена, ослабленная туманом (Beer-Lambert)
// - FogColor × totalScattered: свет, который рассеялся в тумане и попал в камеру
//
// Эта формула физически корректна:
// - Когда тумана нет (transmittance=1, scattered=0): result = sceneColor ✓
// - Когда туман очень плотный (transmittance=0): result = FogColor × scattered ✓
// - Фонарь в толще тумана: ближние слои дают scattered, дальние уже не видны
vec3 finalColor = sceneColor.rgb * transmittance + FogColor * totalScattered;
// Выводим: RGB = вложенный свет тумана, A = transmittance
vec3 fogColor = FogColor * totalScattered;
FragColor = vec4(finalColor, 1.0);
FragColor = vec4(fogColor, transmittance);
}