diff --git a/include/Engine/Rendering/SSAOPass.h b/include/Engine/Rendering/SSAOPass.h index f15e20d3..57b3be6d 100644 --- a/include/Engine/Rendering/SSAOPass.h +++ b/include/Engine/Rendering/SSAOPass.h @@ -29,8 +29,6 @@ private: void InitializeShaderProgram(); void InitializeBuffer(); - void GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const; - void ComputeAO(GLuint depthBuffer, Camera* camera); //void blurHorizontal(GLuint depthBuffer); //void blurVertical(GLuint depthBuffer); @@ -38,7 +36,7 @@ private: Model* m_ScreenQuad; const IRenderer* m_Renderer; - + static const GLint MAX_MIP_LEVEL = 5; float m_Radius; float m_Bias; float m_Contrast; diff --git a/resources/Shaders/SSAO.frag.glsl b/resources/Shaders/SSAO.frag.glsl index 68c830f8..2709d099 100644 --- a/resources/Shaders/SSAO.frag.glsl +++ b/resources/Shaders/SSAO.frag.glsl @@ -1,12 +1,15 @@ #version 430 +#define MIP_MAPS_LEVELS (3) + +#define MAX_PIXEL_BEFOR_LOWER_MIP_LEVEL (3) //Number of samples per pixel uniform int uNumOfSamples; -//#define NUM_SAMPLES (11) +//#define uNumOfSamples (11) //Number of turns around the cirle uniform int uNumOfTurns; -//#define NUM_TURNS (7) +//#define uNumOfTurns (7) layout (binding = 0) uniform sampler2D ViewSpaceZ; @@ -42,42 +45,41 @@ vec3 getVSFaceNormal(vec3 ViewSpacePosition) { vec3 getSampleViewSpacePos(ivec2 ScreenSpaceCoord, int SampleIndex, float RotationAngle, float ScreenSpaceSampleRadius){ - // Pure Magic... - float alpha = float(SampleIndex) * (1.0 / uNumOfSamples); + // SampleIndex + 0.5f so that we alpha != 0, since if alpha = 0 then screenSpaceSampleTexel will be the same texel as origin. + float alpha = float(SampleIndex + 0.5f) * (1.0f / uNumOfSamples); // Angle to where to sample - float angle = alpha * (uNumOfTurns * 6.28) + RotationAngle; + float angle = alpha * (uNumOfTurns * 6.28f) + RotationAngle; //Lenght to were to sample ScreenSpaceSampleRadius = ScreenSpaceSampleRadius * alpha; vec2 screenSpaceSampleOffsetVecor = vec2(cos(angle), sin(angle)); + //int mipmapLevel = clamp(int(floor(log2(-ScreenSpaceSampleRadius))) - MAX_PIXEL_BEFOR_LOWER_MIP_LEVEL, 0, MIP_MAPS_LEVELS); + // Get texel coordinate on where to sample by going screenSpaceSampleOffsetVecor direction in ScreenSpaceSampleRadius units from ScreenSpaceCoord (the point being shaded); - ivec2 screenSpaceSampleTexel = ivec2(ScreenSpaceSampleRadius * screenSpaceSampleOffsetVecor) + ScreenSpaceCoord; + ivec2 screenSpaceSampleTexel = (ivec2(ScreenSpaceSampleRadius * screenSpaceSampleOffsetVecor) + ScreenSpaceCoord);// >> mipmapLevel; return getVSPosition(screenSpaceSampleTexel); } -float sampleAO(ivec2 ScreenSpaceCoord, vec3 Origin, vec3 OriginNormal, float ScreenSpaceSampleRadius, int SampleIndex, float RotationAngle, float Radius) { - float radius2 = Radius * Radius; +float sampleAO(ivec2 ScreenSpaceCoord, vec3 Origin, vec3 OriginNormal, float ScreenSpaceSampleRadius, int SampleIndex, float RotationAngle, float Radius, float Radius2) { vec3 sampleViewSpacePosition = getSampleViewSpacePos(ScreenSpaceCoord, SampleIndex, RotationAngle, ScreenSpaceSampleRadius); - vec3 sampleVector = Origin - sampleViewSpacePosition; - // vv = sampleVectorLenght ^ 2 float vv = dot(sampleVector, sampleVector); // vn = angle between sampleVector and Normal float vn = dot(sampleVector, OriginNormal); - const float epsilon = 0.0001f; + const float epsilon = 0.01f; // vv < radius2 if the vector is shorter then the radius; // vn - bias, offset the angle to reduse self occlusion. // epsilon is here to make divison by 0 impossible. - return float(vv < radius2) * max((vn - uBias) / (epsilon + vv), 0.0); + return float(vv < Radius2) * max((vn - uBias) / (epsilon + vv), 0.0); //float f = max(radius2 - vv, 0.0); //return f * f * f * max((vn - uBias) / (epsilon + vv), 0.0); } @@ -86,29 +88,33 @@ float sampleAO(ivec2 ScreenSpaceCoord, vec3 Origin, vec3 OriginNormal, float Scr void main() { ivec2 originScreenCoord = ivec2(gl_FragCoord.xy); + // We always get the Orginin from the level 0 of the mipmaps vec3 origin = getVSPosition(originScreenCoord); float radius; + if(origin.z < uRadius){ radius = origin.z; } else { radius = uRadius; } + float radius2 = radius * radius; vec3 originNormal = getVSFaceNormal(origin); - float screenSpaceSampleRadius = -uProjScale * radius / origin.z; + //screenSpaceSampleRadius is in pixels + float screenSpaceSampleRadius = uProjScale * radius / origin.z; float rotationAngleOffset = 30 * originScreenCoord.x ^ originScreenCoord.y + 10 * originScreenCoord.x * originScreenCoord.y; - float sum = 0.0; - for (int i = 0; i < uNumOfSamples; i++) { - sum += sampleAO(originScreenCoord, origin, originNormal, screenSpaceSampleRadius, i, rotationAngleOffset, radius); + float sum = 0.0f; + for(int i = 0; i < uNumOfSamples; i++) { + sum += sampleAO(originScreenCoord, origin, originNormal, screenSpaceSampleRadius, i, rotationAngleOffset, radius, radius2); } //float A = max(0.0, 1.0 - sum * (2.0f / uNumOfSamples)); - float A = 1.0 - sum * (2.0f * uIntensityScale / float(uNumOfSamples)); + float A = 1.0f - sum * (2.0f * uIntensityScale / float(uNumOfSamples)); AO = clamp(pow(A, uContrast), 0.0f, 1.0f); //AO = vec4(originNormal, 1.0f); } diff --git a/resources/Shaders/SSAOMipMappedZ.frag.glsl b/resources/Shaders/SSAOMipMappedZ.frag.glsl new file mode 100644 index 00000000..10023cd3 --- /dev/null +++ b/resources/Shaders/SSAOMipMappedZ.frag.glsl @@ -0,0 +1,120 @@ +#version 430 + +#define MIP_MAPS_LEVELS (3) + +#define MAX_PIXEL_BEFOR_LOWER_MIP_LEVEL (3) +//Number of samples per pixel +uniform int uNumOfSamples; +//#define uNumOfSamples (11) + +//Number of turns around the cirle +uniform int uNumOfTurns; +//#define uNumOfTurns (7) + +layout (binding = 0) uniform sampler2D ViewSpaceZ; + +uniform vec4 uProjInfo; + +uniform float uProjScale; +//#define ProjScale 500 + +uniform float uRadius; +//#define Radius 1.0f + +uniform float uBias; +//#define Bias 0.012f + +uniform float uContrast; +//#define IntensityDivR6 1 + +uniform float uIntensityScale; + +out float AO; + +vec3 getVSPosition(ivec2 ScreenSpaceCoord, int mipmaplevel) { + float z = texelFetch(ViewSpaceZ, ScreenSpaceCoord, 0).r; + //Get the xy view space coordinates and add the z value from ViewSpaceZ buffer. + return vec3((uProjInfo[0] + (ScreenSpaceCoord.x * uProjInfo[1])) * z, (uProjInfo[2] + (ScreenSpaceCoord.y * uProjInfo[3])) * z, z); +} + +vec3 getVSFaceNormal(vec3 ViewSpacePosition) { + // Get tangets vector for the plane and ViewSpacePositin... don't ask how this functions works. It's pure magic. + // They do this and it just works... I would guess that they approximate the function of a plane from pixels close to the pixel were on now. + return normalize(cross(dFdx(ViewSpacePosition), dFdy(ViewSpacePosition))); +} + + +vec3 getSampleViewSpacePos(ivec2 ScreenSpaceCoord, int SampleIndex, float RotationAngle, float ScreenSpaceSampleRadius){ + // SampleIndex + 0.5f so that we alpha != 0, since if alpha = 0 then screenSpaceSampleTexel will be the same texel as origin. + float alpha = float(SampleIndex + 0.5f) * (1.0f / uNumOfSamples); + + // Angle to where to sample + float angle = alpha * (uNumOfTurns * 6.28f) + RotationAngle; + + //Lenght to were to sample + ScreenSpaceSampleRadius = ScreenSpaceSampleRadius * alpha; + + vec2 screenSpaceSampleOffsetVecor = vec2(cos(angle), sin(angle)); + + int mipmapLevel = clamp(int(floor(log2(-ScreenSpaceSampleRadius))) - MAX_PIXEL_BEFOR_LOWER_MIP_LEVEL, 0, MIP_MAPS_LEVELS); + + // Get texel coordinate on where to sample by going screenSpaceSampleOffsetVecor direction in ScreenSpaceSampleRadius units from ScreenSpaceCoord (the point being shaded); + ivec2 screenSpaceSampleTexel = (ivec2(ScreenSpaceSampleRadius * screenSpaceSampleOffsetVecor) + ScreenSpaceCoord) >> mipmapLevel; + + return getVSPosition(screenSpaceSampleTexel, mipmapLevel); +} + + + +float sampleAO(ivec2 ScreenSpaceCoord, vec3 Origin, vec3 OriginNormal, float ScreenSpaceSampleRadius, int SampleIndex, float RotationAngle, float Radius, float Radius2) { + vec3 sampleViewSpacePosition = getSampleViewSpacePos(ScreenSpaceCoord, SampleIndex, RotationAngle, ScreenSpaceSampleRadius); + vec3 sampleVector = Origin - sampleViewSpacePosition; + // vv = sampleVectorLenght ^ 2 + float vv = dot(sampleVector, sampleVector); + // vn = angle between sampleVector and Normal + float vn = dot(sampleVector, OriginNormal); + + const float epsilon = 0.01f; + + // vv < radius2 if the vector is shorter then the radius; + // vn - bias, offset the angle to reduse self occlusion. + // epsilon is here to make divison by 0 impossible. + return float(vv < Radius2) * max((vn - uBias) / (epsilon + vv), 0.0); + //float f = max(radius2 - vv, 0.0); + //return f * f * f * max((vn - uBias) / (epsilon + vv), 0.0); +} + + +void main() { + ivec2 originScreenCoord = ivec2(gl_FragCoord.xy); + + // We always get the Orginin from the level 0 of the mipmaps + vec3 origin = getVSPosition(originScreenCoord, 0); + + float radius; + + if(origin.z < uRadius){ + radius = origin.z; + } else { + radius = uRadius; + } + + float radius2 = radius * radius; + + vec3 originNormal = getVSFaceNormal(origin); + + //screenSpaceSampleRadius is in pixels + float screenSpaceSampleRadius = uProjScale * radius / origin.z; + + float rotationAngleOffset = 30 * originScreenCoord.x ^ originScreenCoord.y + 10 * originScreenCoord.x * originScreenCoord.y; + + float sum = 0.0f; + for(int i = 0; i < uNumOfSamples; i++) { + sum += sampleAO(originScreenCoord, origin, originNormal, screenSpaceSampleRadius, i, rotationAngleOffset, radius, radius2); + } + + //float A = max(0.0, 1.0 - sum * (2.0f / uNumOfSamples)); + float A = 1.0f - sum * (2.0f * uIntensityScale / float(uNumOfSamples)); + AO = clamp(pow(A, uContrast), 0.0f, 1.0f); + //AO = vec4(originNormal, 1.0f); +} diff --git a/src/Engine/Rendering/SSAOPass.cpp b/src/Engine/Rendering/SSAOPass.cpp index 331e040f..74e81124 100644 --- a/src/Engine/Rendering/SSAOPass.cpp +++ b/src/Engine/Rendering/SSAOPass.cpp @@ -31,12 +31,28 @@ void SSAOPass::InitializeShaderProgram() void SSAOPass::InitializeBuffer() { - GenerateTexture(&m_SSAOTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_R8, GL_RED, GL_FLOAT); + glGenTextures(1, &m_SSAOTexture); + glBindTexture(GL_TEXTURE_2D, m_SSAOTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height, 0, GL_RED, GL_FLOAT, nullptr); m_SSAOFramBuffer.AddResource(std::shared_ptr(new Texture2D(&m_SSAOTexture, GL_COLOR_ATTACHMENT0))); m_SSAOFramBuffer.Generate(); - GenerateTexture(&m_SSAOViewSpaceZTexture, GL_CLAMP_TO_EDGE, GL_LINEAR, glm::vec2(m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height), GL_R32F, GL_RED, GL_FLOAT); + glGenTextures(1, &m_SSAOViewSpaceZTexture); + glBindTexture(GL_TEXTURE_2D, m_SSAOViewSpaceZTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height, 0, GL_RED, GL_FLOAT, nullptr); + //glTexStorage2D(GL_TEXTURE_2D, MAX_MIP_LEVEL, GL_R32F, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height);; + //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height, GL_RED, GL_FLOAT, nullptr); + //glGenerateMipmap(GL_TEXTURE_2D); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + //glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); m_SSAOViewSpaceZFramBuffer.AddResource(std::shared_ptr(new Texture2D(&m_SSAOViewSpaceZTexture, GL_COLOR_ATTACHMENT0))); m_SSAOViewSpaceZFramBuffer.Generate(); @@ -45,12 +61,12 @@ void SSAOPass::InitializeBuffer() void SSAOPass::ClearBuffer() { m_SSAOFramBuffer.Bind(); - glClearColor(0.f, 0.f, 0.f, 0.f); + glClearColor(1.f, 1.f, 1.f, 1.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_SSAOFramBuffer.Unbind(); m_SSAOViewSpaceZFramBuffer.Bind(); - glClearColor(0.f, 0.f, 0.f, 0.f); + glClearColor(1.f, 1.f, 1.f, 1.f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_SSAOViewSpaceZFramBuffer.Unbind(); } @@ -64,17 +80,6 @@ void SSAOPass::Setting(float radius, float bias, float contrast, float intensity m_NumOfTurns = NumOfTurns; } -void SSAOPass::GenerateTexture(GLuint* texture, GLenum wrapping, GLenum filtering, glm::vec2 dimensions, GLint internalFormat, GLint format, GLenum type) const -{ - glGenTextures(1, texture); - glBindTexture(GL_TEXTURE_2D, *texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapping); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapping); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, dimensions.x, dimensions.y, 0, format, type, nullptr); - GLERROR("Texture initialization failed"); -} void SSAOPass::Draw(GLuint depthBuffer, Camera* camera) { @@ -111,6 +116,11 @@ void SSAOPass::Draw(GLuint depthBuffer, Camera* camera) (-2.0 / (m_Renderer->GetViewportSize().Height * camera->ProjectionMatrix()[1][1])) ); + m_SSAOViewSpaceZFramBuffer.Unbind(); + + //glBindTexture(GL_TEXTURE_2D, m_SSAOViewSpaceZTexture); + //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_Renderer->GetViewportSize().Width, m_Renderer->GetViewportSize().Height, GL_RED, GL_FLOAT, &m_SSAOViewSpaceZTexture); + //glGenerateMipmap(GL_TEXTURE_2D); m_SSAOFramBuffer.Bind(); m_SSAOProgram->Bind(); @@ -119,7 +129,7 @@ void SSAOPass::Draw(GLuint depthBuffer, Camera* camera) glBindTexture(GL_TEXTURE_2D, m_SSAOViewSpaceZTexture); // How many pixel there are in a 1m long object 1m away from the camera - glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uProjScale"), m_Renderer->GetViewportSize().Height / (-2.0f * glm::tan(camera->FOV() * 0.5f))); + glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uProjScale"), m_Renderer->GetViewportSize().Height / (2.0f * glm::tan(glm::radians(camera->FOV() * 0.5f)))); glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uRadius"), m_Radius); glUniform1f(glGetUniformLocation(SSAOShaderHandle, "uBias"), m_Bias);