Merge branch 'distant-horizons-core-ssao.improvements'
This commit is contained in:
+24
@@ -84,6 +84,30 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
|
||||
public IDhApiConfigValue<Boolean> ambientOcclusion()
|
||||
{ return new DhApiConfigValue<Boolean, Boolean>(Config.Client.Advanced.Graphics.Quality.ssao); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Integer> ambientOcclusion_SampleCount()
|
||||
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.Quality.ssaoSampleCount); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Double> ambientOcclusion_Radius()
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Quality.ssaoRadius); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Double> ambientOcclusion_Strength()
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Quality.ssaoStrength); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Double> ambientOcclusion_Bias()
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Quality.ssaoBias); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Double> ambientOcclusion_MinLight()
|
||||
{ return new DhApiConfigValue<Double, Double>(Config.Client.Advanced.Graphics.Quality.ssaoMinLight); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<Integer> ambientOcclusion_BlurRadius()
|
||||
{ return new DhApiConfigValue<Integer, Integer>(Config.Client.Advanced.Graphics.Quality.ssaoBlurRadius); }
|
||||
|
||||
@Override
|
||||
public IDhApiConfigValue<ETransparency> transparency()
|
||||
{ return new DhApiConfigValue<ETransparency, ETransparency>(Config.Client.Advanced.Graphics.Quality.transparency); }
|
||||
|
||||
@@ -179,6 +179,36 @@ public class Config
|
||||
.comment("Enable Screen Space Ambient Occlusion")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Integer> ssaoSampleCount = new ConfigEntry.Builder<Integer>()
|
||||
.set(6)
|
||||
.comment("Number of samples to use for Screen Space Ambient Occlusion")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> ssaoRadius = new ConfigEntry.Builder<Double>()
|
||||
.set(4.0)
|
||||
.comment("Radius of Screen Space Ambient Occlusion effect in blocks")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> ssaoStrength = new ConfigEntry.Builder<Double>()
|
||||
.set(0.2)
|
||||
.comment("Strength of Screen Space Ambient Occlusion effect")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> ssaoBias = new ConfigEntry.Builder<Double>()
|
||||
.set(0.02)
|
||||
.comment("Bias of Screen Space Ambient Occlusion effect")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Double> ssaoMinLight = new ConfigEntry.Builder<Double>()
|
||||
.set(0.25)
|
||||
.comment("Minimum brightness of Screen Space Ambient Occlusion effect")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Integer> ssaoBlurRadius = new ConfigEntry.Builder<Integer>()
|
||||
.set(2)
|
||||
.comment("Radius in pixels of Screen Space Ambient Occlusion blurring")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EHorizontalQuality> horizontalQuality = new ConfigEntry.Builder<EHorizontalQuality>()
|
||||
.set(EHorizontalQuality.MEDIUM)
|
||||
.comment(""
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ public class RenderQualityPresetConfigEventHandler extends AbstractPresetConfigE
|
||||
this.put(EQualityPreset.HIGH, true);
|
||||
this.put(EQualityPreset.EXTREME, true);
|
||||
}});
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
|
||||
+72
-75
@@ -20,6 +20,7 @@
|
||||
package com.seibel.distanthorizons.core.render.renderer.shaders;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EGpuUploadMethod;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLState;
|
||||
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
|
||||
@@ -41,7 +42,6 @@ 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 float[] box_vertices = {
|
||||
-1, -1,
|
||||
1, -1,
|
||||
@@ -52,15 +52,13 @@ 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;
|
||||
@@ -73,10 +71,11 @@ public class SSAORenderer
|
||||
{
|
||||
public int gProjUniform;
|
||||
public int gInvProjUniform;
|
||||
public int gSampleRadUniform;
|
||||
public int gFactorUniform;
|
||||
public int gPowerUniform;
|
||||
public int gKernelUniform;
|
||||
public int gSampleCountUniform;
|
||||
public int gRadiusUniform;
|
||||
public int gStrengthUniform;
|
||||
public int gMinLightUniform;
|
||||
public int gBiasUniform;
|
||||
public int gDepthMapUniform;
|
||||
}
|
||||
|
||||
@@ -86,10 +85,13 @@ public class SSAORenderer
|
||||
{
|
||||
public int gSSAOMapUniform;
|
||||
public int gDepthMapUniform;
|
||||
public int gViewSizeUniform;
|
||||
public int gBlurRadiusUniform;
|
||||
public int gNearUniform;
|
||||
public int gFarUniform;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
@@ -98,43 +100,39 @@ 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",
|
||||
this.applyShader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/apply.frag",
|
||||
"fragColor", new String[]{"vPosition"});
|
||||
|
||||
|
||||
|
||||
// SSAO uniform setup
|
||||
this.ssaoShaderUniforms.gProjUniform = this.ssaoShader.getUniformLocation("gProj");
|
||||
this.ssaoShaderUniforms.gInvProjUniform = this.ssaoShader.getUniformLocation("gInvProj");
|
||||
this.ssaoShaderUniforms.gSampleRadUniform = this.ssaoShader.getUniformLocation("gSampleRad");
|
||||
this.ssaoShaderUniforms.gFactorUniform = this.ssaoShader.getUniformLocation("gFactor");
|
||||
this.ssaoShaderUniforms.gPowerUniform = this.ssaoShader.getUniformLocation("gPower");
|
||||
this.ssaoShaderUniforms.gKernelUniform = this.ssaoShader.getUniformLocation("gKernel");
|
||||
this.ssaoShaderUniforms.gSampleCountUniform = this.ssaoShader.getUniformLocation("gSampleCount");
|
||||
this.ssaoShaderUniforms.gRadiusUniform = this.ssaoShader.getUniformLocation("gRadius");
|
||||
this.ssaoShaderUniforms.gStrengthUniform = this.ssaoShader.getUniformLocation("gStrength");
|
||||
this.ssaoShaderUniforms.gMinLightUniform = this.ssaoShader.getUniformLocation("gMinLight");
|
||||
this.ssaoShaderUniforms.gBiasUniform = this.ssaoShader.getUniformLocation("gBias");
|
||||
this.ssaoShaderUniforms.gDepthMapUniform = this.ssaoShader.getUniformLocation("gDepthMap");
|
||||
|
||||
// Apply uniform setup
|
||||
this.applyShaderUniforms.gSSAOMapUniform = this.applyShader.getUniformLocation("gSSAOMap");
|
||||
this.applyShaderUniforms.gDepthMapUniform = this.applyShader.getUniformLocation("gDepthMap");
|
||||
this.applyShaderUniforms.gViewSizeUniform = tryGetUniformLocation(this.applyShader, "gViewSize");
|
||||
this.applyShaderUniforms.gBlurRadiusUniform = tryGetUniformLocation(this.applyShader, "gBlurRadius");
|
||||
this.applyShaderUniforms.gNearUniform = tryGetUniformLocation(this.applyShader, "gNear");
|
||||
this.applyShaderUniforms.gFarUniform = tryGetUniformLocation(this.applyShader, "gFar");
|
||||
|
||||
|
||||
|
||||
// Generate kernel
|
||||
this.kernel = genKernel();
|
||||
// Framebuffer
|
||||
this.createBuffer();
|
||||
}
|
||||
@@ -158,53 +156,24 @@ public class SSAORenderer
|
||||
|
||||
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.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_R16F, width, height, 0, GL32.GL_RED, GL32.GL_HALF_FLOAT, (ByteBuffer) null);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
|
||||
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
|
||||
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);
|
||||
buffer.order(ByteOrder.nativeOrder());
|
||||
buffer.asFloatBuffer().put(box_vertices);
|
||||
buffer.rewind();
|
||||
|
||||
this.boxBuffer = new GLVertexBuffer(false);
|
||||
this.boxBuffer.bind();
|
||||
this.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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// render //
|
||||
@@ -213,7 +182,9 @@ public class SSAORenderer
|
||||
public void render(float partialTicks)
|
||||
{
|
||||
GLState state = new GLState();
|
||||
|
||||
this.init();
|
||||
|
||||
int width = MC_RENDER.getTargetFrameBufferViewportWidth();
|
||||
int height = MC_RENDER.getTargetFrameBufferViewportHeight();
|
||||
|
||||
@@ -226,27 +197,36 @@ public class SSAORenderer
|
||||
|
||||
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.glDisable(GL32.GL_SCISSOR_TEST);
|
||||
GL32.glDisable(GL32.GL_DEPTH_TEST);
|
||||
GL32.glDisable(GL11.GL_BLEND);
|
||||
|
||||
float near = RenderUtil.getNearClipPlaneDistanceInBlocks(partialTicks);
|
||||
float far = (float) ((RenderUtil.getFarClipPlaneDistanceInBlocks() + LodUtil.REGION_WIDTH) * Math.sqrt(2));
|
||||
|
||||
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)));
|
||||
width / (float) height,
|
||||
near, far);
|
||||
|
||||
Mat4f invertedPerspective = new Mat4f(perspective);
|
||||
invertedPerspective.invert();
|
||||
|
||||
int sampleCount = Config.Client.Advanced.Graphics.Quality.ssaoSampleCount.get();
|
||||
int blurRadius = Config.Client.Advanced.Graphics.Quality.ssaoBlurRadius.get();
|
||||
float radius = Config.Client.Advanced.Graphics.Quality.ssaoRadius.get().floatValue();
|
||||
float strength = Config.Client.Advanced.Graphics.Quality.ssaoStrength.get().floatValue();
|
||||
float minLight = Config.Client.Advanced.Graphics.Quality.ssaoMinLight.get().floatValue();
|
||||
float bias = Config.Client.Advanced.Graphics.Quality.ssaoBias.get().floatValue();
|
||||
|
||||
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.gSampleCountUniform, sampleCount);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gRadiusUniform, radius);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gStrengthUniform, strength);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gMinLightUniform, minLight);
|
||||
this.ssaoShader.setUniform(this.ssaoShaderUniforms.gBiasUniform, bias);
|
||||
|
||||
this.va.bind();
|
||||
this.va.bindBufferToAllBindingPoint(this.boxBuffer.getId());
|
||||
@@ -254,35 +234,52 @@ public class SSAORenderer
|
||||
GL32.glActiveTexture(GL32.GL_TEXTURE0);
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, MC_RENDER.getDepthTextureId());
|
||||
|
||||
GL32.glUniform3fv(this.ssaoShaderUniforms.gKernelUniform, this.kernel);
|
||||
GL32.glUniform1i(this.ssaoShaderUniforms.gDepthMapUniform, 0);
|
||||
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.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.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.glUniform1i(this.applyShaderUniforms.gBlurRadiusUniform, blurRadius);
|
||||
|
||||
if (this.applyShaderUniforms.gViewSizeUniform >= 0)
|
||||
GL32.glUniform2f(this.applyShaderUniforms.gViewSizeUniform, width, height);
|
||||
|
||||
if (this.applyShaderUniforms.gNearUniform >= 0)
|
||||
GL32.glUniform1f(this.applyShaderUniforms.gNearUniform, near);
|
||||
|
||||
if (this.applyShaderUniforms.gFarUniform >= 0)
|
||||
GL32.glUniform1f(this.applyShaderUniforms.gFarUniform, far);
|
||||
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, 6);
|
||||
|
||||
|
||||
state.restore();
|
||||
}
|
||||
|
||||
private int tryGetUniformLocation(ShaderProgram shader, String uniformName)
|
||||
{
|
||||
try {
|
||||
return shader.getUniformLocation(uniformName);
|
||||
}
|
||||
catch (RuntimeException error) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void free()
|
||||
{
|
||||
this.ssaoShader.free();
|
||||
this.applyShader.free();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -98,6 +98,18 @@
|
||||
"SSAO",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.ssao.@tooltip":
|
||||
"Screen Space Ambient Occlusion adds depth to the lighting of blocks.",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.ssaoSampleCount":
|
||||
"SSAO Sample Count",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.ssaoRadius":
|
||||
"SSAO Radius",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.ssaoStrength":
|
||||
"SSAO Strength",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.ssaoMinLight":
|
||||
"SSAO Min Light",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.ssaoBias":
|
||||
"SSAO Bias",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.ssaoBlurRadius":
|
||||
"SSAO Blur Radius",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.horizontalQuality":
|
||||
"Horizontal Quality",
|
||||
"distanthorizons.config.client.advanced.graphics.quality.horizontalQuality.@tooltip":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
in vec4 vertexColor;
|
||||
in vec3 vertexWorldPos;
|
||||
in float vertexYPos;
|
||||
//in float vertexYPos;
|
||||
in vec4 vPos;
|
||||
|
||||
out vec4 fragColor;
|
||||
@@ -15,50 +15,19 @@ uniform float noiseIntensity;
|
||||
uniform int noiseDropoff;
|
||||
|
||||
|
||||
// method definitions
|
||||
|
||||
float fade(float value, float start, float end) {
|
||||
return (clamp(value, start, end) - start) / (end - start);
|
||||
}
|
||||
|
||||
// The random functions for diffrent dimentions
|
||||
float rand(float co) { return fract(sin(co*(91.3458)) * 47453.5453); }
|
||||
float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }
|
||||
float rand(vec3 co){ return rand(co.xy+rand(co.z)); }
|
||||
float rand(vec2 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }
|
||||
float rand(vec3 co) { return rand(co.xy + rand(co.z)); }
|
||||
|
||||
// Puts steps in a float
|
||||
// EG. setting stepSize to 4 then this would be the result of this function
|
||||
// In: 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, ..., 1.1, 1.2, 1.3
|
||||
// Out: 0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, ..., 1.0, 1.0, 1.25
|
||||
float quantize(float val, int stepSize) {
|
||||
return floor(val*stepSize)/stepSize;
|
||||
vec3 quantize(vec3 val, int stepSize) {
|
||||
return floor(val * stepSize) / stepSize;
|
||||
}
|
||||
|
||||
// The modulus function dosnt exist in GLSL so I made my own
|
||||
// To speed up the mod function, this only accepts full numbers for y
|
||||
float mod(float x, int y) {
|
||||
return x - y * floor(x/y);
|
||||
}
|
||||
|
||||
|
||||
// Some HSV functions I stole somewhere online
|
||||
vec3 RGB2HSV(vec3 c) {
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
vec3 HSV2RGB(vec3 c) {
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fragment Shader
|
||||
@@ -71,66 +40,31 @@ void main()
|
||||
{
|
||||
fragColor = vertexColor;
|
||||
|
||||
|
||||
// TODO: Move into its own function instead of in an if statement
|
||||
if (noiseEnabled) {
|
||||
vec3 vertexNormal = normalize(cross(dFdx(vPos.xyz), dFdy(vPos.xyz)));
|
||||
vec3 vertexNormal = normalize(cross(dFdy(vPos.xyz), dFdx(vPos.xyz)));
|
||||
// This bit of code is required to fix the vertex position problem cus of floats in the verted world position varuable
|
||||
vec3 fixedVPos = vPos.xyz - vertexNormal * 0.001;
|
||||
vec3 fixedVPos = vPos.xyz + vertexNormal * 0.001;
|
||||
|
||||
float noiseAmplification = noiseIntensity * 0.01;
|
||||
noiseAmplification = (-1.0 * pow(2.0*((fragColor.x + fragColor.y + fragColor.z) / 3.0) - 1.0, 2.0) + 1.0) * noiseAmplification; // Lessen the effect on depending on how dark the object is, equasion for this is -(2x-1)^{2}+1
|
||||
noiseAmplification *= fragColor.w; // The effect would lessen on transparent objects
|
||||
float lum = (fragColor.r + fragColor.g + fragColor.b) / 3.0;
|
||||
noiseAmplification = (1.0 - pow(lum * 2.0 - 1.0, 2.0)) * noiseAmplification; // Lessen the effect on depending on how dark the object is, equasion for this is -(2x-1)^{2}+1
|
||||
noiseAmplification *= fragColor.a; // The effect would lessen on transparent objects
|
||||
|
||||
// Random value for each position
|
||||
float randomValue = rand(vec3(
|
||||
quantize(fixedVPos.x, noiseSteps),
|
||||
quantize(fixedVPos.y, noiseSteps),
|
||||
quantize(fixedVPos.z, noiseSteps)
|
||||
))
|
||||
* 2.0 * noiseAmplification - noiseAmplification;
|
||||
|
||||
|
||||
float randomValue = rand(quantize(fixedVPos, noiseSteps))
|
||||
* 2.0 * noiseAmplification - noiseAmplification;
|
||||
|
||||
// Modifies the color
|
||||
// A value of 0 on the randomValue will result in the original color, while a value of 1 will result in a fully bright color
|
||||
vec3 newCol = fragColor.rgb + (1.0 - fragColor.rgb) * randomValue;
|
||||
newCol = clamp(newCol, 0.0, 1.0);
|
||||
|
||||
if (noiseDropoff != 0) {
|
||||
float distF = min(length(vertexWorldPos) / noiseDropoff, 1.0);
|
||||
newCol = mix(newCol, fragColor.rgb, distF); // The further away it gets, the less noise gets applied
|
||||
}
|
||||
|
||||
// Clamps it and turns it back into a vec4
|
||||
if (noiseDropoff == 0)
|
||||
fragColor = vec4(clamp(newCol.rgb, 0.0, 1.0), fragColor.w);
|
||||
else
|
||||
fragColor = mix(
|
||||
vec4(clamp(newCol.rgb, 0.0, 1.0), fragColor.w), fragColor,
|
||||
min(length(vertexWorldPos) / noiseDropoff, 1.0) // The further away it gets, the less noise gets applied
|
||||
);
|
||||
|
||||
// For testing
|
||||
// if (fragColor.r != 69420.) {
|
||||
// fragColor = vec4(
|
||||
// mod(fixedVPos.x, 1),
|
||||
// mod(fixedVPos.y, 1),
|
||||
// mod(fixedVPos.z, 1),
|
||||
// fragColor.w);
|
||||
// }
|
||||
fragColor.rgb = newCol;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Are these still needed?
|
||||
float linearFog(float x, float fogStart, float fogLength, float fogMin, float fogRange) {
|
||||
x = clamp((x-fogStart)/fogLength, 0.0, 1.0);
|
||||
return fogMin + fogRange * x;
|
||||
}
|
||||
|
||||
float exponentialFog(float x, float fogStart, float fogLength,
|
||||
float fogMin, float fogRange, float fogDensity) {
|
||||
x = max((x-fogStart)/fogLength, 0.0) * fogDensity;
|
||||
return fogMin + fogRange - fogRange/exp(x);
|
||||
}
|
||||
|
||||
float exponentialSquaredFog(float x, float fogStart, float fogLength,
|
||||
float fogMin, float fogRange, float fogDensity) {
|
||||
x = max((x-fogStart)/fogLength, 0.0) * fogDensity;
|
||||
return fogMin + fogRange - fogRange/exp(x*x);
|
||||
}
|
||||
@@ -42,23 +42,14 @@ float mixFogThickness(float near, float far, float height);
|
||||
// =========================================================
|
||||
|
||||
|
||||
// Puts steps in a float
|
||||
// EG. setting stepSize to 4 then this would be the result of this function
|
||||
// In: 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, ..., 1.1, 1.2, 1.3
|
||||
// Out: 0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, ..., 1.0, 1.0, 1.25
|
||||
float quantize(float val, int stepSize) {
|
||||
return floor(val*stepSize)/stepSize;
|
||||
const vec3 MAGIC = vec3(0.06711056, 0.00583715, 52.9829189);
|
||||
|
||||
float InterleavedGradientNoise(const in vec2 pixel) {
|
||||
float x = dot(pixel, MAGIC.xy);
|
||||
return fract(MAGIC.z * fract(x));
|
||||
}
|
||||
|
||||
// The modulus function dosnt exist in GLSL so I made my own
|
||||
// To speed up the mod function, this only accepts full numbers for y
|
||||
float mod(float x, int y) {
|
||||
return x - y * floor(x/y);
|
||||
}
|
||||
|
||||
|
||||
vec3 calcViewPosition(float fragmentDepth)
|
||||
{
|
||||
vec3 calcViewPosition(float fragmentDepth) {
|
||||
vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0);
|
||||
ndc.xyz = ndc.xyz * 2.0 - 1.0;
|
||||
|
||||
@@ -76,13 +67,12 @@ void main()
|
||||
{
|
||||
float vertexYPos = 100.0f;
|
||||
float fragmentDepth = texture(gDepthMap, TexCoord).r;
|
||||
fragColor = vec4(fogColor.rgb, 0.0);
|
||||
|
||||
// a fragment depth of "1" means the fragment wasn't drawn to,
|
||||
// we only want to apply Fog to LODs, not to the sky outside the LODs
|
||||
if (fragmentDepth < 1)
|
||||
{
|
||||
if (fullFogMode == 0)
|
||||
{
|
||||
if (fragmentDepth < 1.0) {
|
||||
if (fullFogMode == 0) {
|
||||
// render fog based on distance from the camera
|
||||
vec3 vertexWorldPos = calcViewPosition(fragmentDepth);
|
||||
|
||||
@@ -94,17 +84,16 @@ void main()
|
||||
float farFogThickness = getFarFogThickness(farDist);
|
||||
float heightFogThickness = getHeightFogThickness(heightDist);
|
||||
float mixedFogThickness = mixFogThickness(nearFogThickness, farFogThickness, heightFogThickness);
|
||||
mixedFogThickness = clamp(mixedFogThickness, 0.0, 1.0);
|
||||
fragColor.a = clamp(mixedFogThickness, 0.0, 1.0);
|
||||
|
||||
fragColor = vec4(fogColor.rgb, mixedFogThickness);
|
||||
float dither = InterleavedGradientNoise(gl_FragCoord.xy) - 0.5;
|
||||
fragColor.a += dither / 255.0;
|
||||
}
|
||||
else if (fullFogMode == 1)
|
||||
{
|
||||
else if (fullFogMode == 1) {
|
||||
// render everything with the fog color
|
||||
fragColor = vec4(fogColor.rgb, 1.0);
|
||||
fragColor.a = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// test code.
|
||||
|
||||
// this can be fired by manually changing the fullFogMode to a (normally)
|
||||
@@ -112,19 +101,13 @@ void main()
|
||||
// a uniform we don't have to worry about GLSL optimizing away different
|
||||
// options when testing, causing a bunch of headaches if we just want to render the screen red.
|
||||
|
||||
float depthValue = texture(gDepthMap, TexCoord).r;
|
||||
fragColor = vec4(vec3(depthValue), 1.0); // Convert depth value to grayscale color
|
||||
float depthValue = textureLod(gDepthMap, TexCoord, 0).r;
|
||||
fragColor.rgb = vec3(depthValue); // Convert depth value to grayscale color
|
||||
fragColor.a = 1.0;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Are these still needed?
|
||||
float linearFog(float x, float fogStart, float fogLength, float fogMin, float fogRange) {
|
||||
x = clamp((x-fogStart)/fogLength, 0.0, 1.0);
|
||||
@@ -132,13 +115,15 @@ float linearFog(float x, float fogStart, float fogLength, float fogMin, float fo
|
||||
}
|
||||
|
||||
float exponentialFog(float x, float fogStart, float fogLength,
|
||||
float fogMin, float fogRange, float fogDensity) {
|
||||
float fogMin, float fogRange, float fogDensity)
|
||||
{
|
||||
x = max((x-fogStart)/fogLength, 0.0) * fogDensity;
|
||||
return fogMin + fogRange - fogRange/exp(x);
|
||||
}
|
||||
|
||||
float exponentialSquaredFog(float x, float fogStart, float fogLength,
|
||||
float fogMin, float fogRange, float fogDensity) {
|
||||
float fogMin, float fogRange, float fogDensity)
|
||||
{
|
||||
x = max((x-fogStart)/fogLength, 0.0) * fogDensity;
|
||||
return fogMin + fogRange - fogRange/exp(x*x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,14 +30,6 @@ vec3 quantize(vec3 val, int stepSize) {
|
||||
return floor(val*stepSize)/stepSize;
|
||||
}
|
||||
|
||||
// The modulus function dosnt exist in GLSL so I made my own
|
||||
// To speed up the mod function, this only accepts full numbers for y
|
||||
float mod(float x, int y) {
|
||||
return x - y * floor(x/y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fragment shader for adding noise to lods.
|
||||
|
||||
@@ -1,65 +1,110 @@
|
||||
#version 150 core
|
||||
#extension GL_ARB_derivative_control : enable
|
||||
|
||||
#define SAMPLE_MAX 64
|
||||
|
||||
#define saturate(x) (clamp((x), 0.0, 1.0))
|
||||
|
||||
in vec2 TexCoord;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D gDepthMap;
|
||||
uniform float gSampleRad;
|
||||
uniform float gFactor;
|
||||
uniform float gPower;
|
||||
uniform mat4 gProj;
|
||||
uniform int gSampleCount;
|
||||
uniform float gRadius;
|
||||
uniform float gStrength;
|
||||
uniform float gMinLight;
|
||||
uniform float gBias;
|
||||
uniform mat4 gInvProj;
|
||||
uniform mat4 gProj;
|
||||
|
||||
const int MAX_KERNEL_SIZE = 128;
|
||||
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];
|
||||
const float EPSILON = 1.e-6;
|
||||
const float GOLDEN_ANGLE = 2.39996323;
|
||||
const vec3 MAGIC = vec3(0.06711056, 0.00583715, 52.9829189);
|
||||
const float PI = 3.1415926538;
|
||||
const float TAU = PI * 2.0;
|
||||
|
||||
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 = gInvProj * ndc;
|
||||
vs_pos.xyz = vs_pos.xyz / vs_pos.w;
|
||||
return vs_pos.xyz;
|
||||
vec3 unproject(vec4 pos) {
|
||||
return pos.xyz / pos.w;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 viewPos = calcViewPosition(TexCoord);
|
||||
vec3 viewNormal = normalize(cross(dFdy(viewPos.xyz), dFdx(viewPos.xyz)) * -1.0);
|
||||
float InterleavedGradientNoise(const in vec2 pixel) {
|
||||
float x = dot(pixel, MAGIC.xy);
|
||||
return fract(MAGIC.z * fract(x));
|
||||
}
|
||||
|
||||
vec3 randomVec = vec3(0.0, -1.0, 0.0);
|
||||
vec3 calcViewPosition(const in vec3 clipPos) {
|
||||
vec4 viewPos = gInvProj * vec4(clipPos * 2.0 - 1.0, 1.0);
|
||||
return viewPos.xyz / viewPos.w;
|
||||
}
|
||||
|
||||
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 = 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 GetSpiralOcclusion(const in vec2 uv, const in vec3 viewPos, const in vec3 viewNormal) {
|
||||
float dither = InterleavedGradientNoise(gl_FragCoord.xy);
|
||||
float rotatePhase = dither * TAU;
|
||||
float rStep = gRadius / gSampleCount;
|
||||
|
||||
vec2 offset;
|
||||
|
||||
float ao = 0.0;
|
||||
int sampleCount = 0;
|
||||
float radius = rStep;
|
||||
for (int i = 0; i < clamp(gSampleCount, 1, SAMPLE_MAX); i++) {
|
||||
vec2 offset = vec2(
|
||||
sin(rotatePhase),
|
||||
cos(rotatePhase)
|
||||
) * radius;
|
||||
|
||||
radius += rStep;
|
||||
rotatePhase += GOLDEN_ANGLE;
|
||||
|
||||
vec3 sampleViewPos = viewPos + vec3(offset, -0.1);
|
||||
vec3 sampleClipPos = unproject(gProj * vec4(sampleViewPos, 1.0)) * 0.5 + 0.5;
|
||||
sampleClipPos = saturate(sampleClipPos);
|
||||
|
||||
float sampleClipDepth = textureLod(gDepthMap, sampleClipPos.xy, 0.0).r;
|
||||
if (sampleClipDepth >= 1.0 - EPSILON) continue;
|
||||
|
||||
sampleClipPos.z = sampleClipDepth;
|
||||
sampleViewPos = unproject(gInvProj * vec4(sampleClipPos * 2.0 - 1.0, 1.0));
|
||||
|
||||
vec3 diff = sampleViewPos - viewPos;
|
||||
float sampleDist = length(diff);
|
||||
vec3 sampleNormal = diff / sampleDist;
|
||||
|
||||
float sampleNoLm = max(dot(viewNormal, sampleNormal) - gBias, 0.0);
|
||||
float aoF = 1.0 - saturate(sampleDist / gRadius);
|
||||
ao += sampleNoLm * aoF;
|
||||
sampleCount++;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
ao /= max(sampleCount, 1);
|
||||
ao = smoothstep(0.0, gStrength, ao);
|
||||
|
||||
return ao * (1.0 - gMinLight);
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
float fragmentDepth = textureLod(gDepthMap, TexCoord, 0).r;
|
||||
float occlusion = 0.0;
|
||||
|
||||
// Do not apply to sky
|
||||
if (fragmentDepth < 1.0) {
|
||||
vec3 viewPos = calcViewPosition(vec3(TexCoord, fragmentDepth));
|
||||
|
||||
#ifdef GL_ARB_derivative_control
|
||||
// Get higher precision derivatives when available
|
||||
vec3 viewNormal = cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz));
|
||||
#else
|
||||
vec3 viewNormal = cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz));
|
||||
#endif
|
||||
|
||||
viewNormal = normalize(viewNormal);
|
||||
|
||||
occlusion = GetSpiralOcclusion(TexCoord, viewPos, viewNormal);
|
||||
}
|
||||
|
||||
fragColor = vec4(vec3(1.0 - occlusion), 1.0);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#version 150 core
|
||||
|
||||
in vec2 TexCoord;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform sampler2D gSSAOMap;
|
||||
uniform sampler2D gDepthMap;
|
||||
uniform vec2 gViewSize;
|
||||
uniform int gBlurRadius;
|
||||
uniform float gNear;
|
||||
uniform float gFar;
|
||||
|
||||
|
||||
float linearizeDepth(const in float depth) {
|
||||
return (gNear * gFar) / (depth * (gNear - gFar) + gFar);
|
||||
}
|
||||
|
||||
float Gaussian(const in float sigma, const in float x) {
|
||||
return exp(-(x*x) / (2.0 * (sigma*sigma)));
|
||||
}
|
||||
|
||||
float BilateralGaussianBlur(const in vec2 texcoord, const in float linearDepth, const in float g_sigmaV) {
|
||||
float g_sigmaX = 1.6;
|
||||
float g_sigmaY = 1.6;
|
||||
|
||||
int radius = clamp(gBlurRadius, 1, 3);
|
||||
|
||||
vec2 pixelSize = 1.0 / gViewSize;
|
||||
|
||||
float accum = 0.0;
|
||||
float total = 0.0;
|
||||
for (int iy = -radius; iy <= radius; iy++) {
|
||||
float fy = Gaussian(g_sigmaY, iy);
|
||||
|
||||
for (int ix = -radius; ix <= radius; ix++) {
|
||||
float fx = Gaussian(g_sigmaX, ix);
|
||||
|
||||
vec2 sampleTex = texcoord + ivec2(ix, iy) * pixelSize;
|
||||
float sampleValue = textureLod(gSSAOMap, sampleTex, 0).r;
|
||||
float sampleDepth = textureLod(gDepthMap, sampleTex, 0).r;
|
||||
float sampleLinearDepth = linearizeDepth(sampleDepth);
|
||||
|
||||
float depthDiff = abs(sampleLinearDepth - linearDepth);
|
||||
float fv = Gaussian(g_sigmaV, depthDiff);
|
||||
|
||||
float weight = fx*fy*fv;
|
||||
accum += weight * sampleValue;
|
||||
total += weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (total <= 1.e-4) return 1.0;
|
||||
return accum / total;
|
||||
}
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = vec4(1.0);
|
||||
|
||||
float fragmentDepth = textureLod(gDepthMap, TexCoord, 0).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) {
|
||||
if (gBlurRadius > 0) {
|
||||
float fragmentDepthLinear = linearizeDepth(fragmentDepth);
|
||||
fragColor.a = BilateralGaussianBlur(TexCoord, fragmentDepthLinear, 1.6);
|
||||
}
|
||||
else {
|
||||
fragColor.a = texelFetch(gSSAOMap, ivec2(gl_FragCoord.xy), 0).r;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user