faster ssao
This commit is contained in:
+14
-92
@@ -22,7 +22,7 @@ public class SSAORenderer
|
||||
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||
|
||||
private static final int MAX_KERNEL_SIZE = 128;
|
||||
private static final int MAX_KERNEL_SIZE = 32;
|
||||
private static final float[] box_vertices = {
|
||||
-1, -1,
|
||||
1, -1,
|
||||
@@ -33,21 +33,14 @@ public class SSAORenderer
|
||||
};
|
||||
|
||||
|
||||
|
||||
private ShaderProgram ssaoShader;
|
||||
private ShaderProgram applyShader;
|
||||
|
||||
private GLVertexBuffer boxBuffer;
|
||||
private VertexAttribute va;
|
||||
private boolean init = false;
|
||||
|
||||
private float[] kernel = new float[MAX_KERNEL_SIZE * 3];
|
||||
|
||||
private int width = -1;
|
||||
private int height = -1;
|
||||
private int ssaoFramebuffer = -1;
|
||||
|
||||
private int ssaoTexture = -1;
|
||||
|
||||
// ssao uniforms
|
||||
private final SsaoShaderUniforms ssaoShaderUniforms = new SsaoShaderUniforms();
|
||||
private static class SsaoShaderUniforms
|
||||
@@ -61,15 +54,6 @@ public class SSAORenderer
|
||||
public int gDepthMapUniform;
|
||||
}
|
||||
|
||||
// apply uniforms
|
||||
private final ApplyShaderUniforms applyShaderUniforms = new ApplyShaderUniforms();
|
||||
private static class ApplyShaderUniforms
|
||||
{
|
||||
public int gSSAOMapUniform;
|
||||
public int gDepthMapUniform;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
@@ -79,26 +63,19 @@ public class SSAORenderer
|
||||
|
||||
public void init()
|
||||
{
|
||||
if (this.init)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (this.init) return;
|
||||
this.init = true;
|
||||
|
||||
|
||||
this.va = VertexAttribute.create();
|
||||
this.va.bind();
|
||||
|
||||
// Pos
|
||||
this.va.setVertexAttribute(0, 0, VertexAttribute.VertexPointer.addVec2Pointer(false));
|
||||
this.va.completeAndCheck(Float.BYTES * 2);
|
||||
this.ssaoShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/ao.frag",
|
||||
"fragColor", new String[]{"vPosition"});
|
||||
|
||||
this.applyShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/apply-frag.frag",
|
||||
"fragColor", new String[]{"vPosition"});
|
||||
|
||||
|
||||
|
||||
// SSAO uniform setup
|
||||
this.ssaoShaderUniforms.gProjUniform = this.ssaoShader.getUniformLocation("gProj");
|
||||
this.ssaoShaderUniforms.gInvProjUniform = this.ssaoShader.getUniformLocation("gInvProj");
|
||||
@@ -108,43 +85,12 @@ public class SSAORenderer
|
||||
this.ssaoShaderUniforms.gKernelUniform = this.ssaoShader.getUniformLocation("gKernel");
|
||||
this.ssaoShaderUniforms.gDepthMapUniform = this.ssaoShader.getUniformLocation("gDepthMap");
|
||||
|
||||
// Apply uniform setup
|
||||
this.applyShaderUniforms.gSSAOMapUniform = this.applyShader.getUniformLocation("gSSAOMap");
|
||||
this.applyShaderUniforms.gDepthMapUniform = this.applyShader.getUniformLocation("gDepthMap");
|
||||
|
||||
|
||||
|
||||
// Generate kernel
|
||||
this.kernel = genKernel();
|
||||
// Framebuffer
|
||||
this.createBuffer();
|
||||
}
|
||||
|
||||
private void createFramebuffer(int width, int height)
|
||||
{
|
||||
if (this.ssaoFramebuffer != -1)
|
||||
{
|
||||
GL32.glDeleteFramebuffers(this.ssaoFramebuffer);
|
||||
this.ssaoFramebuffer = -1;
|
||||
}
|
||||
|
||||
if (this.ssaoTexture != -1)
|
||||
{
|
||||
GL32.glDeleteTextures(this.ssaoTexture);
|
||||
this.ssaoTexture = -1;
|
||||
}
|
||||
|
||||
this.ssaoFramebuffer = GL32.glGenFramebuffers();
|
||||
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer);
|
||||
|
||||
this.ssaoTexture = GL32.glGenTextures();
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.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, this.ssaoTexture, 0);
|
||||
}
|
||||
|
||||
private void createBuffer()
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(box_vertices.length * Float.BYTES);
|
||||
@@ -194,24 +140,20 @@ public class SSAORenderer
|
||||
public void render(float partialTicks)
|
||||
{
|
||||
GLState state = new GLState();
|
||||
|
||||
this.init();
|
||||
|
||||
int width = MC_RENDER.getTargetFrameBufferViewportWidth();
|
||||
int height = MC_RENDER.getTargetFrameBufferViewportHeight();
|
||||
|
||||
if (this.width != width || this.height != height)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.createFramebuffer(width, height);
|
||||
}
|
||||
|
||||
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer);
|
||||
GL32.glViewport(0, 0, width, height);
|
||||
GL32.glDisable(GL32.GL_DEPTH_TEST);
|
||||
GL32.glDisable(GL32.GL_BLEND);
|
||||
GL32.glEnable(GL11.GL_BLEND);
|
||||
GL32.glBlendEquation(GL32.GL_FUNC_ADD);
|
||||
GL32.glBlendFuncSeparate(GL32.GL_ZERO, GL32.GL_SRC_ALPHA, GL32.GL_ZERO, GL32.GL_ONE);
|
||||
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer());
|
||||
GL32.glDisable(GL32.GL_SCISSOR_TEST);
|
||||
|
||||
|
||||
Mat4f perspective = Mat4f.perspective(
|
||||
(float) MC_RENDER.getFov(partialTicks),
|
||||
MC_RENDER.getTargetFrameBufferViewportWidth() / (float) MC_RENDER.getTargetFrameBufferViewportHeight(),
|
||||
@@ -221,13 +163,12 @@ public class SSAORenderer
|
||||
Mat4f invertedPerspective = new Mat4f(perspective);
|
||||
invertedPerspective.invert();
|
||||
|
||||
|
||||
this.ssaoShader.bind();
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gProjUniform, perspective);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gInvProjUniform, invertedPerspective);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gSampleRadUniform, 3.0f);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gFactorUniform, 0.8f);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gPowerUniform, 1.0f);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gFactorUniform, 0.7f);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gPowerUniform, 1.5f);
|
||||
|
||||
this.va.bind();
|
||||
this.va.bindBufferToAllBindingPoint(this.boxBuffer.getId());
|
||||
@@ -240,30 +181,11 @@ public class SSAORenderer
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6);
|
||||
|
||||
|
||||
|
||||
this.applyShader.bind();
|
||||
|
||||
GL32.glEnable(GL11.GL_BLEND);
|
||||
GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer());
|
||||
|
||||
GL32.glActiveTexture(GL32.GL_TEXTURE0);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.ssaoTexture);
|
||||
GL32.glUniform1i(this.applyShaderUniforms.gSSAOMapUniform, 0);
|
||||
GL32.glActiveTexture(GL32.GL_TEXTURE1);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, MC_RENDER.getDepthTextureId());
|
||||
GL32.glUniform1i(this.applyShaderUniforms.gDepthMapUniform, 1);
|
||||
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6);
|
||||
|
||||
|
||||
state.restore();
|
||||
}
|
||||
|
||||
public void free()
|
||||
{
|
||||
this.ssaoShader.free();
|
||||
this.applyShader.free();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#version 150 core
|
||||
#extension GL_ARB_derivative_control : enable
|
||||
|
||||
in vec2 TexCoord;
|
||||
|
||||
@@ -11,55 +12,73 @@ uniform float gPower;
|
||||
uniform mat4 gProj;
|
||||
uniform mat4 gInvProj;
|
||||
|
||||
const int MAX_KERNEL_SIZE = 128;
|
||||
const float SAMPLE_BIAS = 0.1;
|
||||
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;
|
||||
const vec3 MAGIC = vec3(0.06711056, 0.00583715, 52.9829189);
|
||||
const float PI = 3.1415926538;
|
||||
|
||||
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 = gInvProj * ndc;
|
||||
vs_pos.xyz = vs_pos.xyz / vs_pos.w;
|
||||
return vs_pos.xyz;
|
||||
float InterleavedGradientNoise(const in vec2 pixel) {
|
||||
float x = dot(pixel, MAGIC.xy);
|
||||
return fract(MAGIC.z * fract(x));
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 viewPos = calcViewPosition(TexCoord);
|
||||
vec3 viewNormal = normalize(cross(dFdy(viewPos.xyz), dFdx(viewPos.xyz)) * -1.0);
|
||||
vec3 calcViewPosition(const in vec3 clipPos) {
|
||||
vec4 viewPos = gInvProj * vec4(clipPos * 2.0 - 1.0, 1.0);
|
||||
return viewPos.xyz / viewPos.w;
|
||||
}
|
||||
|
||||
vec3 randomVec = vec3(0.0, -1.0, 0.0);
|
||||
void main() {
|
||||
float fragmentDepth = textureLod(gDepthMap, TexCoord, 0).r;
|
||||
float occlusion = 1.0;
|
||||
|
||||
if (fragmentDepth < 1.0) {
|
||||
float dither = InterleavedGradientNoise(gl_FragCoord.xy);
|
||||
vec3 viewPos = calcViewPosition(vec3(TexCoord, fragmentDepth));
|
||||
|
||||
#ifdef GL_ARB_derivative_control
|
||||
vec3 viewNormal = normalize(cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz)));
|
||||
#else
|
||||
vec3 viewNormal = normalize(cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz)));
|
||||
#endif
|
||||
|
||||
vec3 tangent = normalize(randomVec - viewNormal * dot(randomVec, viewNormal));
|
||||
vec3 bitangent = cross(viewNormal, tangent);
|
||||
mat3 TBN = mat3(tangent, bitangent, viewNormal);
|
||||
const vec3 upVec = vec3(0.0, -1.0, 0.0);
|
||||
|
||||
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 = gProj * vec4(samplePos + viewPos, 1.0);
|
||||
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));
|
||||
// the number added to the samplePos.z can be used to reduce noise in the SSAO application at the cost of reducing the overall affect
|
||||
occlusion_factor += float(geometryDepth >= samplePos.z + 0.1) * rangeCheck;
|
||||
float angle = dither * (PI * 2.0);
|
||||
vec3 rotation = vec3(sin(angle), cos(angle), 0.0);
|
||||
vec3 tangent = normalize(cross(viewNormal, rotation));
|
||||
|
||||
vec3 bitangent = normalize(cross(viewNormal, tangent));
|
||||
mat3 TBN = mat3(tangent, bitangent, viewNormal);
|
||||
|
||||
float maxWeight = 0.0;
|
||||
float occlusion_factor = 0.0;
|
||||
for (int i = 0; i < MAX_KERNEL_SIZE; i++) {
|
||||
vec3 samplePos = TBN * gKernel[i];
|
||||
samplePos = viewPos + samplePos * gSampleRad;
|
||||
|
||||
vec4 sampleNdcPos = gProj * vec4(samplePos + viewPos, 1.0);
|
||||
sampleNdcPos = sampleNdcPos / sampleNdcPos.w;
|
||||
if (any(greaterThanEqual(abs(sampleNdcPos.xy), vec2(1.0)))) continue;
|
||||
|
||||
maxWeight += 1.0;
|
||||
|
||||
vec2 sampleTexPos = sampleNdcPos.xy * 0.5 + 0.5;
|
||||
float sampleDepth = textureLod(gDepthMap, sampleTexPos, 0).r;
|
||||
float geometryDepth = calcViewPosition(vec3(sampleTexPos, sampleDepth)).z;
|
||||
|
||||
float rangeCheck = smoothstep(0.0, 1.0, gSampleRad / abs(viewPos.z - geometryDepth));
|
||||
// the number added to the samplePos.z can be used to reduce noise in the SSAO application at the cost of reducing the overall affect
|
||||
occlusion_factor += step(samplePos.z + SAMPLE_BIAS, geometryDepth) * rangeCheck;
|
||||
}
|
||||
|
||||
float visibility_factor = 1.0 - (occlusion_factor / maxWeight);
|
||||
occlusion = (1.0 - pow(visibility_factor, gFactor)) * gPower;
|
||||
occlusion = clamp(1.0 - occlusion, 0.25, 1.0);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
fragColor = vec4(vec3(1.0), occlusion);
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#version 150 core
|
||||
|
||||
in vec2 TexCoord;
|
||||
in vec2 ViewRay;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D gSSAOMap;
|
||||
uniform sampler2D gDepthMap;
|
||||
|
||||
void main()
|
||||
{
|
||||
float fragmentDepth = texture(gDepthMap, TexCoord).r;
|
||||
// a fragment depth of "1" means the fragment wasn't drawn to,
|
||||
// we only want to apply SSAO to LODs, not to the sky outside the LODs
|
||||
if (fragmentDepth < 1)
|
||||
{
|
||||
fragColor = vec4(0.0, 0.0, 0.0, 1-texture(gSSAOMap, TexCoord).r);
|
||||
}
|
||||
else
|
||||
{
|
||||
// every pixel needs to be set to something, otherwise the pixel may be undefined by some drivers (specifically Intel)
|
||||
fragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user