SSAO math bug fixes

This commit is contained in:
Magnus Norddahl 2016-10-06 07:36:49 +02:00
parent 00e72028ef
commit bb79dcb634
8 changed files with 125 additions and 83 deletions

View File

@ -124,7 +124,7 @@ CVAR(Float, gl_ssao_strength, 0.7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Int, gl_ssao_debug, 0, 0) CVAR(Int, gl_ssao_debug, 0, 0)
CVAR(Float, gl_ssao_bias, 0.5f, 0) CVAR(Float, gl_ssao_bias, 0.5f, 0)
CVAR(Float, gl_ssao_radius, 100.0f, 0) CVAR(Float, gl_ssao_radius, 100.0f, 0)
CUSTOM_CVAR(Float, gl_ssao_blur_amount, 4.0f, 0) CUSTOM_CVAR(Float, gl_ssao_blur_amount, 16.0f, 0)
{ {
if (self < 0.1f) self = 0.1f; if (self < 0.1f) self = 0.1f;
} }
@ -178,6 +178,13 @@ void FGLRenderer::AmbientOccludeScene()
float blurSharpness = 1.0f / blurAmount; float blurSharpness = 1.0f / blurAmount;
float sceneScaleX = mSceneViewport.width / (float)mScreenViewport.width;
float sceneScaleY = mSceneViewport.height / (float)mScreenViewport.height;
float sceneOffsetX = mSceneViewport.left / (float)mScreenViewport.width;
float sceneOffsetY = mSceneViewport.top / (float)mScreenViewport.height;
int randomTexture = clamp(gl_ssao - 1, 0, FGLRenderBuffers::NumAmbientRandomTextures - 1);
// Calculate linear depth values // Calculate linear depth values
glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0); glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0);
glViewport(0, 0, mBuffers->AmbientWidth, mBuffers->AmbientHeight); glViewport(0, 0, mBuffers->AmbientWidth, mBuffers->AmbientHeight);
@ -196,8 +203,8 @@ void FGLRenderer::AmbientOccludeScene()
mLinearDepthShader->LinearizeDepthB.Set(MAX(1.0f / GetZNear(), 1.e-8f)); mLinearDepthShader->LinearizeDepthB.Set(MAX(1.0f / GetZNear(), 1.e-8f));
mLinearDepthShader->InverseDepthRangeA.Set(1.0f); mLinearDepthShader->InverseDepthRangeA.Set(1.0f);
mLinearDepthShader->InverseDepthRangeB.Set(0.0f); mLinearDepthShader->InverseDepthRangeB.Set(0.0f);
mLinearDepthShader->Scale.Set(mBuffers->AmbientWidth * 2.0f / (float)mScreenViewport.width, mBuffers->AmbientHeight * 2.0f / (float)mScreenViewport.height); mLinearDepthShader->Scale.Set(sceneScaleX, sceneScaleY);
mLinearDepthShader->Offset.Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height); mLinearDepthShader->Offset.Set(sceneOffsetX, sceneOffsetY);
RenderScreenQuad(); RenderScreenQuad();
// Apply ambient occlusion // Apply ambient occlusion
@ -206,7 +213,7 @@ void FGLRenderer::AmbientOccludeScene()
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientRandomTexture); glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientRandomTexture[randomTexture]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
@ -219,17 +226,21 @@ void FGLRenderer::AmbientOccludeScene()
mSSAOShader->DepthTexture.Set(0); mSSAOShader->DepthTexture.Set(0);
mSSAOShader->RandomTexture.Set(1); mSSAOShader->RandomTexture.Set(1);
mSSAOShader->NormalTexture.Set(2); mSSAOShader->NormalTexture.Set(2);
mSSAOShader->UVToViewA.Set(2.0f * invFocalLenX, -2.0f * invFocalLenY); mSSAOShader->UVToViewA.Set(2.0f * invFocalLenX, 2.0f * invFocalLenY);
mSSAOShader->UVToViewB.Set(-invFocalLenX, invFocalLenY); mSSAOShader->UVToViewB.Set(-invFocalLenX, -invFocalLenY);
mSSAOShader->InvFullResolution.Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight); mSSAOShader->InvFullResolution.Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight);
mSSAOShader->NDotVBias.Set(nDotVBias); mSSAOShader->NDotVBias.Set(nDotVBias);
mSSAOShader->NegInvR2.Set(-1.0f / r2); mSSAOShader->NegInvR2.Set(-1.0f / r2);
mSSAOShader->RadiusToScreen.Set(aoRadius * 0.5 / tanHalfFovy * mBuffers->AmbientHeight); mSSAOShader->RadiusToScreen.Set(aoRadius * 0.5 / tanHalfFovy * mBuffers->AmbientHeight);
mSSAOShader->AOMultiplier.Set(1.0f / (1.0f - nDotVBias)); mSSAOShader->AOMultiplier.Set(1.0f / (1.0f - nDotVBias));
mSSAOShader->AOStrength.Set(aoStrength); mSSAOShader->AOStrength.Set(aoStrength);
mSSAOShader->Scale.Set(sceneScaleX, sceneScaleY);
mSSAOShader->Offset.Set(sceneOffsetX, sceneOffsetY);
RenderScreenQuad(); RenderScreenQuad();
// Blur SSAO texture // Blur SSAO texture
if (gl_ssao_debug < 2)
{
glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0); glBindFramebuffer(GL_FRAMEBUFFER, mBuffers->AmbientFB0);
glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture1); glBindTexture(GL_TEXTURE_2D, mBuffers->AmbientTexture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@ -246,17 +257,15 @@ void FGLRenderer::AmbientOccludeScene()
mDepthBlurShader->InvFullResolution[true].Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight); mDepthBlurShader->InvFullResolution[true].Set(1.0f / mBuffers->AmbientWidth, 1.0f / mBuffers->AmbientHeight);
mDepthBlurShader->PowExponent[true].Set(1.8f); mDepthBlurShader->PowExponent[true].Set(1.8f);
RenderScreenQuad(); RenderScreenQuad();
}
// Add SSAO back to scene texture: // Add SSAO back to scene texture:
mBuffers->BindSceneFB(false); mBuffers->BindSceneFB(false);
glViewport(mSceneViewport.left, mSceneViewport.top, mSceneViewport.width, mSceneViewport.height); glViewport(mSceneViewport.left, mSceneViewport.top, mSceneViewport.width, mSceneViewport.height);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD); glBlendEquation(GL_FUNC_ADD);
if (gl_ssao_debug > 1)
glBlendFunc(GL_ONE, GL_ZERO);
else
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (gl_ssao_debug == 1) if (gl_ssao_debug != 0)
{ {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
@ -270,8 +279,8 @@ void FGLRenderer::AmbientOccludeScene()
mSSAOCombineShader->AODepthTexture.Set(0); mSSAOCombineShader->AODepthTexture.Set(0);
mSSAOCombineShader->SceneFogTexture.Set(1); mSSAOCombineShader->SceneFogTexture.Set(1);
if (gl_multisample > 1) mSSAOCombineShader->SampleCount.Set(gl_multisample); if (gl_multisample > 1) mSSAOCombineShader->SampleCount.Set(gl_multisample);
mSSAOCombineShader->Scale.Set(mBuffers->AmbientWidth * 2.0f / (float)mScreenViewport.width, mBuffers->AmbientHeight * 2.0f / (float)mScreenViewport.height); mSSAOCombineShader->Scale.Set(sceneScaleX, sceneScaleY);
mSSAOCombineShader->Offset.Set(mSceneViewport.left / (float)mScreenViewport.width, mSceneViewport.top / (float)mScreenViewport.height); mSSAOCombineShader->Offset.Set(sceneOffsetX, sceneOffsetY);
RenderScreenQuad(); RenderScreenQuad();
FGLDebug::PopGroup(); FGLDebug::PopGroup();

View File

@ -59,6 +59,11 @@ FGLRenderBuffers::FGLRenderBuffers()
mPipelineFB[i] = 0; mPipelineFB[i] = 0;
} }
for (int i = 0; i < NumAmbientRandomTextures; i++)
{
AmbientRandomTexture[i] = 0;
}
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&mOutputFB); glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&mOutputFB);
glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples); glGetIntegerv(GL_MAX_SAMPLES, &mMaxSamples);
} }
@ -151,7 +156,8 @@ void FGLRenderBuffers::ClearAmbientOcclusion()
DeleteFrameBuffer(AmbientFB1); DeleteFrameBuffer(AmbientFB1);
DeleteTexture(AmbientTexture0); DeleteTexture(AmbientTexture0);
DeleteTexture(AmbientTexture1); DeleteTexture(AmbientTexture1);
DeleteTexture(AmbientRandomTexture); for (int i = 0; i < NumAmbientRandomTextures; i++)
DeleteTexture(AmbientRandomTexture[i]);
} }
void FGLRenderBuffers::DeleteTexture(GLuint &handle) void FGLRenderBuffers::DeleteTexture(GLuint &handle)
@ -368,13 +374,18 @@ void FGLRenderBuffers::CreateAmbientOcclusion(int width, int height)
AmbientFB0 = CreateFrameBuffer("AmbientFB0", AmbientTexture0); AmbientFB0 = CreateFrameBuffer("AmbientFB0", AmbientTexture0);
AmbientFB1 = CreateFrameBuffer("AmbientFB1", AmbientTexture1); AmbientFB1 = CreateFrameBuffer("AmbientFB1", AmbientTexture1);
int16_t randomValues[16 * 4]; // Must match quality enum in FSSAOShader::GetDefines
double numDirections[NumAmbientRandomTextures] = { 2.0, 4.0, 8.0 };
std::mt19937 generator(1337); std::mt19937 generator(1337);
std::uniform_real_distribution<double> distribution(-1.0, 1.0); std::uniform_real_distribution<double> distribution(0.0, 1.0);
for (int quality = 0; quality < NumAmbientRandomTextures; quality++)
{
int16_t randomValues[16 * 4];
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
{ {
double num_directions = 8.0; // Must be same as the define in ssao.fp double angle = 2.0 * M_PI * distribution(generator) / numDirections[quality];
double angle = 2.0 * M_PI * distribution(generator) / num_directions;
double x = cos(angle); double x = cos(angle);
double y = sin(angle); double y = sin(angle);
double z = distribution(generator); double z = distribution(generator);
@ -386,7 +397,8 @@ void FGLRenderBuffers::CreateAmbientOcclusion(int width, int height)
randomValues[i * 4 + 3] = (int16_t)clamp(w * 32767.0, -32768.0, 32767.0); randomValues[i * 4 + 3] = (int16_t)clamp(w * 32767.0, -32768.0, 32767.0);
} }
AmbientRandomTexture = Create2DTexture("AmbientRandomTexture", GL_RGBA16_SNORM, 4, 4, randomValues); AmbientRandomTexture[quality] = Create2DTexture("AmbientRandomTexture", GL_RGBA16_SNORM, 4, 4, randomValues);
}
} }
//========================================================================== //==========================================================================

View File

@ -65,7 +65,8 @@ public:
GLuint AmbientFB1 = 0; GLuint AmbientFB1 = 0;
int AmbientWidth = 0; int AmbientWidth = 0;
int AmbientHeight = 0; int AmbientHeight = 0;
GLuint AmbientRandomTexture = 0; enum { NumAmbientRandomTextures = 3 };
GLuint AmbientRandomTexture[NumAmbientRandomTextures];
static bool IsEnabled(); static bool IsEnabled();

View File

@ -84,6 +84,8 @@ void FSSAOShader::Bind()
RadiusToScreen.Init(*mShader, "RadiusToScreen"); RadiusToScreen.Init(*mShader, "RadiusToScreen");
AOMultiplier.Init(*mShader, "AOMultiplier"); AOMultiplier.Init(*mShader, "AOMultiplier");
AOStrength.Init(*mShader, "AOStrength"); AOStrength.Init(*mShader, "AOStrength");
Scale.Init(*mShader, "Scale");
Offset.Init(*mShader, "Offset");
mMultisample = multisample; mMultisample = multisample;
} }
mShader->Bind(); mShader->Bind();
@ -91,6 +93,7 @@ void FSSAOShader::Bind()
FString FSSAOShader::GetDefines(int mode, bool multisample) FString FSSAOShader::GetDefines(int mode, bool multisample)
{ {
// Must match quality values in FGLRenderBuffers::CreateAmbientOcclusion
int numDirections, numSteps; int numDirections, numSteps;
switch (gl_ssao) switch (gl_ssao)
{ {

View File

@ -39,6 +39,8 @@ public:
FBufferedUniform1f RadiusToScreen; FBufferedUniform1f RadiusToScreen;
FBufferedUniform1f AOMultiplier; FBufferedUniform1f AOMultiplier;
FBufferedUniform1f AOStrength; FBufferedUniform1f AOStrength;
FBufferedUniform2f Scale;
FBufferedUniform2f Offset;
private: private:
enum Quality enum Quality

View File

@ -7,7 +7,7 @@ uniform float BlurSharpness;
uniform vec2 InvFullResolution; uniform vec2 InvFullResolution;
uniform float PowExponent; uniform float PowExponent;
#define KERNEL_RADIUS 7.0 #define KERNEL_RADIUS 3.0
float CrossBilateralWeight(float r, float sampleDepth, float centerDepth) float CrossBilateralWeight(float r, float sampleDepth, float centerDepth)
{ {

View File

@ -18,6 +18,12 @@ uniform float InverseDepthRangeB;
uniform vec2 Scale; uniform vec2 Scale;
uniform vec2 Offset; uniform vec2 Offset;
float normalizeDepth(float depth)
{
float normalizedDepth = clamp(InverseDepthRangeA * depth + InverseDepthRangeB, 0.0, 1.0);
return 1.0 / (normalizedDepth * LinearizeDepthA + LinearizeDepthB);
}
void main() void main()
{ {
vec2 uv = Offset + TexCoord * Scale; vec2 uv = Offset + TexCoord * Scale;
@ -28,19 +34,24 @@ void main()
ivec2 texSize = textureSize(DepthTexture, 0); ivec2 texSize = textureSize(DepthTexture, 0);
#endif #endif
// Use floor here because as we downscale the sampling error has to remain uniform to prevent ivec2 ipos = ivec2(max(uv * vec2(texSize), vec2(0.0)));
// noise in the depth values.
ivec2 ipos = ivec2(max(floor(uv * vec2(texSize) - 0.75), vec2(0.0)));
#if defined(MULTISAMPLE) #if defined(MULTISAMPLE)
float depth = 0.0; float depth = normalizeDepth(texelFetch(ColorTexture, ipos, 0).a != 0.0 ? texelFetch(DepthTexture, ipos, 0).x : 1.0);
for (int i = 0; i < SampleCount; i++) float sampleIndex = 0.0;
depth += texelFetch(ColorTexture, ipos, i).a != 0.0 ? texelFetch(DepthTexture, ipos, i).x : 1.0; for (int i = 1; i < SampleCount; i++)
depth /= float(SampleCount); {
float hardwareDepth = texelFetch(ColorTexture, ipos, i).a != 0.0 ? texelFetch(DepthTexture, ipos, i).x : 1.0;
float sampleDepth = normalizeDepth(hardwareDepth);
if (sampleDepth < depth)
{
depth = sampleDepth;
sampleIndex = float(i);
}
}
FragColor = vec4(depth, sampleIndex, 0.0, 1.0);
#else #else
float depth = texelFetch(ColorTexture, ipos, 0).a != 0.0 ? texelFetch(DepthTexture, ipos, 0).x : 1.0; float depth = normalizeDepth(texelFetch(ColorTexture, ipos, 0).a != 0.0 ? texelFetch(DepthTexture, ipos, 0).x : 1.0);
FragColor = vec4(depth, 0.0, 0.0, 1.0);
#endif #endif
float normalizedDepth = clamp(InverseDepthRangeA * depth + InverseDepthRangeB, 0.0, 1.0);
FragColor = vec4(1.0 / (normalizedDepth * LinearizeDepthA + LinearizeDepthB), 0.0, 0.0, 1.0);
} }

View File

@ -13,6 +13,9 @@ uniform float AOMultiplier;
uniform float AOStrength; uniform float AOStrength;
uniform vec2 Scale;
uniform vec2 Offset;
uniform sampler2D DepthTexture; uniform sampler2D DepthTexture;
#if defined(MULTISAMPLE) #if defined(MULTISAMPLE)
@ -27,42 +30,42 @@ uniform sampler2D RandomTexture;
#define PI 3.14159265358979323846 #define PI 3.14159265358979323846
// Calculate eye space position for the specified texture coordinate // Calculate eye space position for the specified texture coordinate and depth
vec3 FetchViewPos(vec2 uv) vec3 FetchViewPos(vec2 uv, float z)
{ {
float z = texture(DepthTexture, uv).x;
return vec3((UVToViewA * uv + UVToViewB) * z, z); return vec3((UVToViewA * uv + UVToViewB) * z, z);
} }
#if defined(MULTISAMPLE) #if defined(MULTISAMPLE)
vec3 FetchNormal(vec2 uv) vec3 SampleNormal(vec2 uv, float samplerIndex)
{ {
ivec2 texSize = textureSize(NormalTexture); ivec2 texSize = textureSize(NormalTexture);
ivec2 ipos = ivec2(uv * vec2(texSize)); ivec2 ipos = ivec2(uv * vec2(texSize));
return normalize(texelFetch(NormalTexture, ipos, 0).xyz * 2.0 - 1.0); return texelFetch(NormalTexture, ipos, int(samplerIndex)).xyz * 2.0 - 1.0;
} }
#else #else
vec3 FetchNormal(vec2 uv) vec3 SampleNormal(vec2 uv, float samplerIndex)
{ {
return normalize(texture(NormalTexture, uv).xyz * 2.0 - 1.0); ivec2 texSize = textureSize(NormalTexture, 0);
ivec2 ipos = ivec2(uv * vec2(texSize));
return texelFetch(NormalTexture, ipos, 0).xyz * 2.0 - 1.0;
} }
#endif #endif
vec3 MinDiff(vec3 p, vec3 pr, vec3 pl) // Look up the eye space normal for the specified texture coordinate
vec3 FetchNormal(vec2 uv, float samplerIndex)
{ {
vec3 v1 = pr - p; vec3 normal = SampleNormal(Offset + uv * Scale, samplerIndex);
vec3 v2 = p - pl; if (length(normal) > 0.1)
return (dot(v1, v1) < dot(v2, v2)) ? v1 : v2; {
} normal = normalize(normal);
normal.z = -normal.z;
// Reconstruct eye space normal from nearest neighbors return normal;
vec3 ReconstructNormal(vec3 p) }
{ else
vec3 pr = FetchViewPos(TexCoord + vec2(InvFullResolution.x, 0)); {
vec3 pl = FetchViewPos(TexCoord + vec2(-InvFullResolution.x, 0)); return vec3(0.0);
vec3 pt = FetchViewPos(TexCoord + vec2(0, InvFullResolution.y)); }
vec3 pb = FetchViewPos(TexCoord + vec2(0, -InvFullResolution.y));
return normalize(cross(MinDiff(p, pr, pl), MinDiff(p, pt, pb)));
} }
// Compute normalized 2D direction // Compute normalized 2D direction
@ -113,7 +116,7 @@ float ComputeAO(vec3 viewPosition, vec3 viewNormal)
for (float StepIndex = 0.0; StepIndex < NUM_STEPS; ++StepIndex) for (float StepIndex = 0.0; StepIndex < NUM_STEPS; ++StepIndex)
{ {
vec2 sampleUV = round(rayPixels * direction) * InvFullResolution + TexCoord; vec2 sampleUV = round(rayPixels * direction) * InvFullResolution + TexCoord;
vec3 samplePos = FetchViewPos(sampleUV); vec3 samplePos = FetchViewPos(sampleUV, texture(DepthTexture, sampleUV).x);
ao += ComputeSampleAO(viewPosition, viewNormal, samplePos); ao += ComputeSampleAO(viewPosition, viewNormal, samplePos);
rayPixels += stepSizePixels; rayPixels += stepSizePixels;
} }
@ -125,9 +128,10 @@ float ComputeAO(vec3 viewPosition, vec3 viewNormal)
void main() void main()
{ {
vec3 viewPosition = FetchViewPos(TexCoord); vec2 depthData = texture(DepthTexture, TexCoord).xy;
//vec3 viewNormal = ReconstructNormal(viewPosition); vec3 viewPosition = FetchViewPos(TexCoord, depthData.x);
vec3 viewNormal = FetchNormal(TexCoord); vec3 viewNormal = FetchNormal(TexCoord, depthData.y);
float occlusion = ComputeAO(viewPosition, viewNormal) * AOStrength + (1.0 - AOStrength); float occlusion = viewNormal != vec3(0.0) ? ComputeAO(viewPosition, viewNormal) * AOStrength + (1.0 - AOStrength) : 1.0;
FragColor = vec4(occlusion, viewPosition.z, 0.0, 1.0); FragColor = vec4(occlusion, viewPosition.z, 0.0, 1.0);
} }