Merge branch 'ssao' into 'main'
SSAO See merge request jeseibel/distant-horizons-core!8
This commit is contained in:
@@ -182,6 +182,20 @@ public class Config
|
||||
.addListener(RenderCacheConfigEventHandler.INSTANCE)
|
||||
.build();
|
||||
|
||||
// TODO merge with horizontal quality
|
||||
public static ConfigEntry<Integer> horizontalScale = new ConfigEntry.Builder<Integer>()
|
||||
.setMinDefaultMax(2, 12, 64)
|
||||
.comment(""
|
||||
+ "This indicates how quickly fake chunks decrease in quality the further away they are. \n"
|
||||
+ "Higher settings will render higher quality fake chunks farther away, \n"
|
||||
+ " but will increase memory and GPU usage.")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Boolean> ssao = new ConfigEntry.Builder<Boolean>()
|
||||
.set(true)
|
||||
.comment("Enable Screen Space Ambient Occlusion")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EHorizontalQuality> horizontalQuality = new ConfigEntry.Builder<EHorizontalQuality>()
|
||||
.set(EHorizontalQuality.MEDIUM)
|
||||
.comment(""
|
||||
|
||||
@@ -241,6 +241,10 @@ public class LodRenderer
|
||||
//TODO: Directional culling
|
||||
bufferHandler.renderOpaque(this);
|
||||
|
||||
if (Config.Client.Advanced.Graphics.Quality.ssao.get()) {
|
||||
SSAORenderer.INSTANCE.render(partialTicks);
|
||||
}
|
||||
|
||||
//======================//
|
||||
// render transparency //
|
||||
//======================//
|
||||
@@ -253,7 +257,7 @@ public class LodRenderer
|
||||
}
|
||||
//if (drawCall==0)
|
||||
// tickLogger.info("DrawCall Count: {}", drawCount);
|
||||
|
||||
|
||||
//================//
|
||||
// render cleanup //
|
||||
//================//
|
||||
@@ -262,7 +266,7 @@ public class LodRenderer
|
||||
LagSpikeCatcher drawCleanup = new LagSpikeCatcher();
|
||||
lightmap.unbind();
|
||||
if (ENABLE_IBO) quadIBO.unbind();
|
||||
|
||||
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
shaderProgram.unbind();
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
package com.seibel.lod.core.render.renderer;
|
||||
|
||||
import com.seibel.lod.api.enums.config.EGpuUploadMethod;
|
||||
import com.seibel.lod.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.lod.core.render.glObject.GLState;
|
||||
import com.seibel.lod.core.render.glObject.buffer.GLVertexBuffer;
|
||||
import com.seibel.lod.core.render.glObject.shader.ShaderProgram;
|
||||
import com.seibel.lod.core.render.glObject.vertexAttribute.VertexAttribute;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.RenderUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.lod.coreapi.util.math.Mat4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class SSAORenderer {
|
||||
public static SSAORenderer INSTANCE = new SSAORenderer();
|
||||
|
||||
public SSAORenderer() {
|
||||
}
|
||||
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||
|
||||
private static final float[] box_vertices = {
|
||||
-1, -1,
|
||||
1, -1,
|
||||
1, 1,
|
||||
-1, -1,
|
||||
1, 1,
|
||||
-1, 1,
|
||||
};
|
||||
|
||||
ShaderProgram ssaoShader;
|
||||
ShaderProgram applyShader;
|
||||
GLVertexBuffer boxBuffer;
|
||||
VertexAttribute va;
|
||||
boolean init = false;
|
||||
|
||||
private static final int MAX_KERNEL_SIZE = 32;
|
||||
private float[] kernel = new float[MAX_KERNEL_SIZE * 3];
|
||||
|
||||
public void init() {
|
||||
if (init) return;
|
||||
|
||||
init = true;
|
||||
va = VertexAttribute.create();
|
||||
va.bind();
|
||||
// Pos
|
||||
va.setVertexAttribute(0, 0, VertexAttribute.VertexPointer.addVec2Pointer(false));
|
||||
va.completeAndCheck(Float.BYTES * 2);
|
||||
ssaoShader = new ShaderProgram("shaders/ssao/ao-vert.vert", "shaders/ssao/ao-frag.frag",
|
||||
"fragColor", new String[]{"vPosition"});
|
||||
|
||||
applyShader = new ShaderProgram("shaders/ssao/ao-vert.vert", "shaders/ssao/apply-frag.frag",
|
||||
"fragColor", new String[]{"vPosition"});
|
||||
|
||||
|
||||
// Generate kernel
|
||||
kernel = genKernel();
|
||||
// Framebuffer
|
||||
createBuffer();
|
||||
}
|
||||
|
||||
private int width = -1;
|
||||
private int height = -1;
|
||||
private int ssaoFramebuffer = -1;
|
||||
|
||||
private int ssaoTexture = -1;
|
||||
|
||||
private void createFramebuffer(int width, int height) {
|
||||
if (ssaoFramebuffer != -1) {
|
||||
GL32.glDeleteFramebuffers(ssaoFramebuffer);
|
||||
ssaoFramebuffer = -1;
|
||||
}
|
||||
|
||||
if (ssaoTexture != -1) {
|
||||
GL32.glDeleteTextures(ssaoTexture);
|
||||
ssaoTexture = -1;
|
||||
}
|
||||
|
||||
ssaoFramebuffer = GL32.glGenFramebuffers();
|
||||
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, ssaoFramebuffer);
|
||||
|
||||
ssaoTexture = GL32.glGenTextures();
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, ssaoTexture);
|
||||
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RED, width, height, 0, GL32.GL_RED, GL32.GL_FLOAT, (ByteBuffer) null);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_NEAREST);
|
||||
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, ssaoTexture, 0);
|
||||
}
|
||||
|
||||
private void createBuffer() {
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(box_vertices.length * Float.BYTES);
|
||||
buffer.order(ByteOrder.nativeOrder());
|
||||
buffer.asFloatBuffer().put(box_vertices);
|
||||
buffer.rewind();
|
||||
boxBuffer = new GLVertexBuffer(false);
|
||||
boxBuffer.bind();
|
||||
boxBuffer.uploadBuffer(buffer, box_vertices.length, EGpuUploadMethod.DATA, box_vertices.length * Float.BYTES);
|
||||
}
|
||||
|
||||
private static float[] genKernel() {
|
||||
float[] kernel = new float[MAX_KERNEL_SIZE * 3];
|
||||
for (int i = 0; i < MAX_KERNEL_SIZE; i++) {
|
||||
float sampleX = (float) (Math.random() * 2.0 - 1.0);
|
||||
float sampleY = (float) (Math.random() * 2.0 - 1.0);
|
||||
float sampleZ = (float) Math.random();
|
||||
|
||||
|
||||
// Normalize
|
||||
float magnitude = (float) Math.sqrt(Math.pow(sampleX, 2) + Math.pow(sampleY, 2) + Math.pow(sampleZ, 2));
|
||||
sampleX /= magnitude;
|
||||
sampleY /= magnitude;
|
||||
sampleZ /= magnitude;
|
||||
|
||||
float scale = i / (float) MAX_KERNEL_SIZE;
|
||||
float interpolatedScale = (float) (0.1 + (scale * scale) * (0.9));
|
||||
|
||||
sampleX *= interpolatedScale;
|
||||
sampleY *= interpolatedScale;
|
||||
sampleZ *= interpolatedScale;
|
||||
kernel[i * 3] = sampleX;
|
||||
kernel[i * 3 + 1] = sampleY;
|
||||
kernel[i * 3 + 2] = sampleZ;
|
||||
}
|
||||
return kernel;
|
||||
}
|
||||
|
||||
public void render(float partialTicks) {
|
||||
GLState state = new GLState();
|
||||
init();
|
||||
//GL32.glDepthMask(false);
|
||||
int width = MC_RENDER.getTargetFrameBufferViewportWidth();
|
||||
int height = MC_RENDER.getTargetFrameBufferViewportHeight();
|
||||
|
||||
if (this.width != width || this.height != height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
createFramebuffer(width, height);
|
||||
}
|
||||
|
||||
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, ssaoFramebuffer);
|
||||
GL32.glViewport(0, 0, width, height);
|
||||
GL32.glDisable(GL32.GL_DEPTH_TEST);
|
||||
GL32.glDisable(GL32.GL_BLEND);
|
||||
GL32.glDisable(GL32.GL_SCISSOR_TEST);
|
||||
|
||||
|
||||
Mat4f perspective = Mat4f.perspective(
|
||||
(float) MC_RENDER.getFov(partialTicks),
|
||||
MC_RENDER.getTargetFrameBufferViewportWidth() / (float) MC_RENDER.getTargetFrameBufferViewportHeight(),
|
||||
RenderUtil.getNearClipPlaneDistanceInBlocks(partialTicks),
|
||||
(float) ((RenderUtil.getFarClipPlaneDistanceInBlocks() + LodUtil.REGION_WIDTH) * Math.sqrt(2)));
|
||||
|
||||
ssaoShader.bind();
|
||||
ssaoShader.setUniform(ssaoShader.getUniformLocation("gProj"), perspective);
|
||||
ssaoShader.setUniform(ssaoShader.getUniformLocation("gSampleRad"), 3.0f);
|
||||
ssaoShader.setUniform(ssaoShader.getUniformLocation("gFactor"), 0.8f);
|
||||
ssaoShader.setUniform(ssaoShader.getUniformLocation("gPower"), 1.0f);
|
||||
va.bind();
|
||||
va.bindBufferToAllBindingPoint(boxBuffer.getId());
|
||||
|
||||
GL32.glActiveTexture(GL32.GL_TEXTURE0);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, MC_RENDER.getDepthTextureId());
|
||||
|
||||
GL32.glUniform3fv(ssaoShader.getUniformLocation("gKernel"), kernel);
|
||||
GL32.glUniform1i(ssaoShader.getUniformLocation("gDepthMap"), 0);
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6);
|
||||
|
||||
applyShader.bind();
|
||||
GL32.glEnable(GL11.GL_BLEND);
|
||||
GL32.glBlendFunc(GL32.GL_ZERO, GL32.GL_SRC_ALPHA);
|
||||
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer());
|
||||
GL32.glActiveTexture(GL32.GL_TEXTURE0);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, ssaoTexture);
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6);
|
||||
|
||||
//GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, ssaoFramebuffer);
|
||||
//GL32.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer());
|
||||
//GL32.glBlitFramebuffer(
|
||||
// 0, 0, width, height,
|
||||
// 0, 0, width, height,
|
||||
// GL11.GL_COLOR_BUFFER_BIT,
|
||||
// GL11.GL_NEAREST
|
||||
//);
|
||||
|
||||
state.restore();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+1
@@ -74,6 +74,7 @@ public interface IMinecraftRenderWrapper extends IBindable
|
||||
int getScreenHeight();
|
||||
|
||||
int getTargetFrameBuffer();
|
||||
int getDepthTextureId();
|
||||
int getTargetFrameBufferViewportWidth();
|
||||
int getTargetFrameBufferViewportHeight();
|
||||
|
||||
|
||||
@@ -90,6 +90,14 @@
|
||||
"Vertical Quality",
|
||||
"lod.config.client.advanced.graphics.quality.verticalQuality.@tooltip":
|
||||
"How well LODs represent overhangs, caves, cliffsides, etc.\n\nHigher options will increase memory and GPU usage.",
|
||||
"lod.config.client.advanced.graphics.quality.horizontalScale":
|
||||
"Horizontal Scale",
|
||||
"lod.config.client.advanced.graphics.quality.horizontalScale.@tooltip":
|
||||
"How quickly LODs drop off in quality.\n\nLarger numbers will improve how distant terrain looks\nbut will increase memory and GPU usage.",
|
||||
"lod.config.client.advanced.graphics.quality.ssao":
|
||||
"SSAO",
|
||||
"lod.config.client.advanced.graphics.quality.ssao.@tooltip":
|
||||
"Screen Space Ambient Occlusion adds depth to the lighting of blocks.",
|
||||
"lod.config.client.advanced.graphics.quality.horizontalQuality":
|
||||
"Horizontal Quality",
|
||||
"lod.config.client.advanced.graphics.quality.horizontalQuality.@tooltip":
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#version 150 core
|
||||
|
||||
in vec2 TexCoord;
|
||||
in vec2 ViewRay;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D gDepthMap;
|
||||
uniform float gSampleRad;
|
||||
uniform float gFactor;
|
||||
uniform float gPower;
|
||||
uniform mat4 gProj;
|
||||
|
||||
const int MAX_KERNEL_SIZE = 32;
|
||||
const float INV_MAX_KERNEL_SIZE_F = 1.0 / float(MAX_KERNEL_SIZE);
|
||||
const vec2 HALF_2 = vec2(0.5);
|
||||
uniform vec3 gKernel[MAX_KERNEL_SIZE];
|
||||
|
||||
vec3 calcViewPosition(vec2 coords) {
|
||||
float fragmentDepth = texture(gDepthMap, coords).r;
|
||||
|
||||
vec4 ndc = vec4(
|
||||
coords.x * 2.0 - 1.0,
|
||||
coords.y * 2.0 - 1.0,
|
||||
fragmentDepth * 2.0 - 1.0,
|
||||
1.0
|
||||
);
|
||||
|
||||
vec4 vs_pos = inverse(gProj) * ndc;
|
||||
vs_pos.xyz = vs_pos.xyz / vs_pos.w;
|
||||
return vs_pos.xyz;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 viewPos = calcViewPosition(TexCoord);
|
||||
vec3 viewNormal = normalize(cross(dFdy(viewPos.xyz), dFdx(viewPos.xyz)) * -1.0);
|
||||
|
||||
vec3 randomVec = vec3(
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0
|
||||
);
|
||||
|
||||
vec3 tangent = normalize(randomVec - viewNormal * dot(randomVec, viewNormal));
|
||||
vec3 bitangent = cross(viewNormal, tangent);
|
||||
mat3 TBN = mat3(tangent, bitangent, viewNormal);
|
||||
float occlusion_factor = 0.0;
|
||||
for (int i = 0; i < MAX_KERNEL_SIZE; i++) {
|
||||
vec3 samplePos = vec3(0.0) + (TBN * gKernel[i]);
|
||||
samplePos = viewPos + samplePos * gSampleRad;
|
||||
|
||||
vec4 offset = vec4(samplePos, 1.0);
|
||||
offset = gProj * offset;
|
||||
offset.xy /= offset.w;
|
||||
offset.xy = offset.xy * HALF_2 + HALF_2;
|
||||
|
||||
float geometryDepth = calcViewPosition(offset.xy).z;
|
||||
|
||||
float rangeCheck = smoothstep(0.0, 1.0, gSampleRad / abs(viewPos.z - geometryDepth));
|
||||
occlusion_factor += float(geometryDepth >= samplePos.z + 0.0) * rangeCheck;
|
||||
|
||||
}
|
||||
|
||||
float visibility_factor = 1.0 - (occlusion_factor / MAX_KERNEL_SIZE);
|
||||
fragColor = vec4(clamp(1.0 - ((1.0 - pow(visibility_factor, gFactor)) * gPower), 0.1, 1.0));
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#version 150 core
|
||||
|
||||
in vec2 vPosition;
|
||||
out vec2 TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(vPosition, 1.0, 1.0);
|
||||
TexCoord = vPosition.xy * 0.5 + 0.5;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#version 150 core
|
||||
|
||||
in vec2 TexCoord;
|
||||
in vec2 ViewRay;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D gSSAOMap;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = vec4(0.0, 0.0, 0.0, texture(gSSAOMap, TexCoord).r);
|
||||
}
|
||||
Reference in New Issue
Block a user