实现SSAO

main
wuyize 2024-04-02 17:21:29 +08:00
parent 865f2be548
commit 7bee8eb5dc
14 changed files with 231 additions and 17 deletions

View File

@ -213,6 +213,8 @@
<None Include="res\Shaders\shadow_mapping.comp" />
<None Include="res\Shaders\skybox.frag" />
<None Include="res\Shaders\skybox.vert" />
<None Include="res\Shaders\ssao.comp" />
<None Include="res\Shaders\ssao_blur.comp" />
<None Include="res\Shaders\ssgi.comp" />
<None Include="res\Shaders\tone_mapping.comp" />
</ItemGroup>

View File

@ -466,6 +466,8 @@
</None>
<None Include="res\Shaders\tone_mapping.comp" />
<None Include="res\Shaders\painting_shadow.frag" />
<None Include="res\Shaders\ssao.comp" />
<None Include="res\Shaders\ssao_blur.comp" />
</ItemGroup>
<ItemGroup>
<QtUic Include="EditorWidgetItem.ui">

View File

@ -81,6 +81,16 @@
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="ssaoCheckBox">
<property name="text">
<string>SSAO</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="fxaaCheckBox">
<property name="text">

View File

@ -34,6 +34,8 @@
<file>Shaders/pageId_downsample.comp</file>
<file>Shaders/tone_mapping.comp</file>
<file>Shaders/painting_shadow.frag</file>
<file>Shaders/ssao.comp</file>
<file>Shaders/ssao_blur.comp</file>
</qresource>
<qresource prefix="/qt/etc">
<file>qt.conf</file>

View File

@ -5,7 +5,7 @@ layout (local_size_x = 8, local_size_y = 8) in;
layout(rgba8, binding = 0) uniform image2D gBaseColor;
uniform sampler2D gNormal;
uniform sampler2D gPosition;
uniform sampler2D gMetallicRoughness;
uniform sampler2D gMetallicRoughnessAO;
uniform sampler2DArray gShadowMap;
uniform samplerCube irradianceMap;
@ -187,8 +187,9 @@ void main()
vec4 baseColor = imageLoad(gBaseColor, pixelLocation);
if(baseColor.a == 0) return;
vec3 albedo = pow(baseColor.rgb, vec3(2.2));
float metallic = texelFetch(gMetallicRoughness, pixelLocation, 0).r;
float roughness = texelFetch(gMetallicRoughness, pixelLocation, 0).g;
float metallic = texelFetch(gMetallicRoughnessAO, pixelLocation, 0).r;
float roughness = texelFetch(gMetallicRoughnessAO, pixelLocation, 0).g;
float ambientOcclusion = 1.0 - texelFetch(gMetallicRoughnessAO, pixelLocation, 0).b;
vec3 worldPos = texelFetch(gPosition, pixelLocation,0).xyz;
vec3 normal = (texelFetch(gNormal, pixelLocation,0).xyz - vec3(0.5)) * 2;
@ -224,7 +225,7 @@ void main()
float NdotL = max(dot(normal, L), 0.0);
vec3 Lo = (kD * albedo / PI + specular) * radiance * NdotL;
vec3 ambient = ambientLighting(normal, V, F0, albedo, metallic, roughness, 1);
vec3 ambient = ambientLighting(normal, V, F0, albedo, metallic, roughness, 1) * ambientOcclusion;
int debugLayer;
float shadow = ShadowCalculation(worldPos, normal, debugLayer);

View File

@ -0,0 +1,63 @@
#version 450 core
layout (local_size_x = 8, local_size_y = 8) in;
layout(rgba8, binding = 1) uniform image2D gMetallicRoughnessAO;
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D texNoise;
uniform vec3 samples[64];
// parameters (you'd probably want to use them as uniforms to more easily tweak the effect)
int kernelSize = 32;
float radius = 0.5;
float bias = 0.025;
// tile noise texture over screen based on screen dimensions divided by noise size
const vec2 noiseScale = vec2(1./4.0, 1./4.0);
uniform mat4 projection;
uniform mat4 view;
void main()
{
ivec2 pixelLocation = ivec2(gl_GlobalInvocationID.xy);
// get input for SSAO algorithm
vec3 worldPos = texelFetch(gPosition, pixelLocation, 0).xyz;
vec3 fragPos = (view * vec4(worldPos, 1.0)).xyz;
vec3 normal = normalize((texelFetch(gNormal, pixelLocation, 0).xyz - vec3(0.5)) * 2);
vec3 randomVec = normalize(texture(texNoise, vec2(pixelLocation) * noiseScale).xyz);
// create TBN change-of-basis matrix: from tangent-space to view-space
vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 TBN = mat3(tangent, bitangent, normal);
// iterate over the sample kernel and calculate occlusion factor
float occlusion = 0.0;
for(int i = 0; i < kernelSize; ++i)
{
// get sample position
vec3 samplePos = TBN * samples[i]; // from tangent to view-space
samplePos = fragPos + samplePos * radius;
// project sample position (to sample texture) (to get position on screen/texture)
vec4 offset = vec4(samplePos, 1.0);
offset = projection * offset; // from view to clip-space
offset.xyz /= offset.w; // perspective divide
offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0
// get sample depth
float sampleDepth = (view * vec4(texture(gPosition, offset.xy).xyz, 1.0)).z; // get depth value of kernel sample
// range check & accumulate
float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth));
occlusion += (sampleDepth >= samplePos.z + bias ? 1.0 : 0.0) * rangeCheck;
}
occlusion = occlusion / kernelSize;
vec4 color = imageLoad(gMetallicRoughnessAO, pixelLocation);
color.b = occlusion;
imageStore(gMetallicRoughnessAO, pixelLocation, color);
}

View File

@ -0,0 +1,23 @@
#version 450 core
layout (local_size_x = 8, local_size_y = 8) in;
layout(rgba8, binding = 1) uniform image2D gMetallicRoughnessAO;
void main()
{
ivec2 pixelLocation = ivec2(gl_GlobalInvocationID.xy);
float result = 0.0;
for (int x = -1; x < 2; ++x)
{
for (int y = -1; y < 2; ++y)
{
ivec2 samplePos = pixelLocation + ivec2(x, y);
samplePos = clamp(samplePos, ivec2(0), imageSize(gMetallicRoughnessAO) - ivec2(1));
result += imageLoad(gMetallicRoughnessAO, samplePos).b;
}
}
vec4 color = imageLoad(gMetallicRoughnessAO, pixelLocation);
color.b = result / (3.0 * 3.0);
imageStore(gMetallicRoughnessAO, pixelLocation, color);
}

View File

@ -25,7 +25,7 @@ namespace Renderer
// Default camera values
static constexpr float YAW = -90.0f;
static constexpr float PITCH = 0.0f;
static constexpr float SPEED = 200.f;
static constexpr float SPEED = 2.f;
static constexpr float SENSITIVITY = 0.1f;
static constexpr float ZOOM = 45.0f;
// camera Attributes

View File

@ -87,7 +87,6 @@ void Renderer::Model::loadModel(QString path)
aiMatrix4x4 transform;
aiMatrix4x4::Scaling(aiVector3D(1 / 0.008), transform);
processNode(scene->mRootNode, scene, transform * scene->mRootNode->mTransformation);
AABB.clear();
AABB.emplace_back(minPos.x, minPos.y, minPos.z);

View File

@ -1,6 +1,7 @@
#include <glad/gl.h>
#include "RenderPass.h"
#include "IblUtils.h"
#include <random>
namespace Renderer
{
@ -132,6 +133,75 @@ namespace Renderer
}
}
SsaoPass::SsaoPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, GLuint& gNormal, GLuint& gPosition, GLuint& gMetallicRoughness, QMatrix4x4& projection, QMatrix4x4& view)
: RenderPass(gl)
, ssaoShader(":/Shaders/ssao.comp")
, blurShader(":/Shaders/ssao_blur.comp")
, frameWidth(frameWidth)
, frameHeight(frameHeight)
, gNormal(gNormal)
, gPosition(gPosition)
, gMetallicRoughness(gMetallicRoughness)
, projection(projection)
, view(view)
{
// generate sample kernel
// ----------------------
std::uniform_real_distribution<GLfloat> randomFloats(0.0, 1.0); // generates random floats between 0.0 and 1.0
std::default_random_engine generator;
for (unsigned int i = 0; i < 64; ++i)
{
QVector3D sample(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, randomFloats(generator));
sample.normalize();
sample *= randomFloats(generator);
float scale = float(i) / 64.0;
// scale samples s.t. they're more aligned to center of kernel
scale = std::lerp(0.1f, 1.0f, scale * scale);
sample *= scale;
ssaoKernel.push_back(sample);
}
// generate noise texture
// ----------------------
std::vector<glm::vec3> ssaoNoise;
for (unsigned int i = 0; i < 16; i++)
// rotate around z-axis (in tangent space)
ssaoNoise.emplace_back(randomFloats(generator) * 2.0 - 1.0, randomFloats(generator) * 2.0 - 1.0, 0.0f);
gl->GenTextures(1, &noiseTexture);
gl->BindTexture(GL_TEXTURE_2D, noiseTexture);
gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, 4, 4, 0, GL_RGB, GL_FLOAT, &ssaoNoise[0]);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
ssaoShader.bind();
ssaoShader.setUniformValue("gNormal", gNormalBinding);
ssaoShader.setUniformValue("gPosition", gPositionBinding);
ssaoShader.setUniformValue("texNoise", texNoiseBinding);
ssaoShader.release();
}
void SsaoPass::dispatch()
{
ssaoShader.bind();
ssaoShader.setUniformValueArray("samples", ssaoKernel.data(), ssaoKernel.size());
ssaoShader.setUniformValue("projection", projection);
ssaoShader.setUniformValue("view", view);
gl->BindTextureUnit(gNormalBinding, gNormal);
gl->BindTextureUnit(gPositionBinding, gPosition);
gl->BindTextureUnit(texNoiseBinding, noiseTexture);
gl->BindImageTexture(1, gMetallicRoughness, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
gl->DispatchCompute(ceil(frameWidth / 8.), ceil(frameHeight / 8.), 1);
ssaoShader.release();
blurShader.bind();
gl->DispatchCompute(ceil(frameWidth / 8.), ceil(frameHeight / 8.), 1);
blurShader.release();
}
LightingPass::LightingPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, QMatrix4x4& view, Camera& camera, Light& light, GLuint& gBaseColor, GLuint& gNormal, GLuint& gPosition, GLuint& gMetallicRoughness, GLuint& shadowGbuffer, GLuint& irradianceMap, GLuint& prefilterMap, GLuint& brdfLUTTexture)
: RenderPass(gl)
, pbrShader(":/Shaders/shadow_mapping.comp")
@ -149,7 +219,7 @@ namespace Renderer
pbrShader.bind();
pbrShader.setUniformValue("gNormal", gNormalBinding);
pbrShader.setUniformValue("gPosition", gPositionBinding);
pbrShader.setUniformValue("gMetallicRoughness", gMetallicRoughnessBinding);
pbrShader.setUniformValue("gMetallicRoughnessAO", gMetallicRoughnessBinding);
pbrShader.setUniformValue("gShadowMap", gShadowMapBinding);
pbrShader.setUniformValue("irradianceMap", irradianceMapBinding);
pbrShader.setUniformValue("prefilterMap", prefilterMapBinding);
@ -284,4 +354,5 @@ namespace Renderer
{
enableFxaa = enable;
}
}

View File

@ -23,7 +23,7 @@ namespace Renderer
{
public:
ShadowMapPass(GladGLContext* gl, GLuint& shadowFboHandle, int& shadowMapResolution, Light& light, Model*& model);
void dispatch();
void dispatch() override;
private:
Shader modelShadowShader, paintingShadowShader;
GLuint& shadowFboHandle;
@ -38,7 +38,7 @@ namespace Renderer
public:
GeometryPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight,
QOpenGLFramebufferObject*& fbo, Model*& model, QMatrix4x4& projection, QMatrix4x4& view);
void dispatch();
void dispatch() override;
private:
Shader modelShader, paintingShader, plainShader;
unsigned int& frameWidth;
@ -54,7 +54,7 @@ namespace Renderer
public:
PageIDFeedbackPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, QOpenGLFramebufferObject*& fbo,
GLuint* gBuffers, GLuint& gPageID, VirtualTextureManager& vtManager);
void dispatch();
void dispatch() override;
private:
Shader pageIdDownsampleShader;
unsigned int& frameWidth;
@ -65,6 +65,28 @@ namespace Renderer
VirtualTextureManager& vtManager;
};
class SsaoPass : public RenderPass
{
public:
SsaoPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, GLuint& gNormal, GLuint& gPosition, GLuint& gMetallicRoughness, QMatrix4x4& projection, QMatrix4x4& view);
void dispatch() override;
private:
Shader ssaoShader, blurShader;
std::vector<QVector3D> ssaoKernel;
GLuint noiseTexture;
unsigned int& frameWidth;
unsigned int& frameHeight;
GLuint& gNormal;
GLuint& gPosition;
GLuint& gMetallicRoughness;
QMatrix4x4& projection;
QMatrix4x4& view;
constexpr static GLuint gNormalBinding = 1,
gPositionBinding = 2,
texNoiseBinding = 3;
};
class LightingPass : public RenderPass
{
public:
@ -72,7 +94,7 @@ namespace Renderer
QMatrix4x4& view, Camera& camera, Light& light,
GLuint& gBaseColor, GLuint& gNormal, GLuint& gPosition, GLuint& gMetallicRoughness,
GLuint& shadowGbuffer, GLuint& irradianceMap, GLuint& prefilterMap, GLuint& brdfLUTTexture);
void dispatch();
void dispatch() override;
private:
Shader pbrShader;
unsigned int& frameWidth;
@ -102,7 +124,7 @@ namespace Renderer
{
public:
SkyboxPass(GladGLContext* gl, QOpenGLFramebufferObject*& fbo, QMatrix4x4& projection, QMatrix4x4& view, GLuint& skyCubemap);
void dispatch();
void dispatch() override;
private:
Shader skyBoxShader;
QOpenGLFramebufferObject*& fbo;
@ -117,7 +139,7 @@ namespace Renderer
{
public:
ToneMappingPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, GLuint& gBaseColor, float& exposure);
void dispatch();
void dispatch() override;
private:
Shader toneMappingShader;
@ -131,7 +153,7 @@ namespace Renderer
{
public:
FinalPass(GladGLContext* gl, unsigned int& frameWidth, unsigned int& frameHeight, GLuint& gBaseColor);
void dispatch();
void dispatch() override;
void setEnableFxaa(bool enable);
private:
Shader finalShader;

View File

@ -19,7 +19,7 @@ static int sunSpeed = 10;
RendererGLWidget::RendererGLWidget(QWidget* parent)
: QOpenGLWidget(parent)
, camera(QVector3D(0.0f, 100.0f, 0.0f))
, camera(QVector3D(0.0f, 1.0f, 0.0f))
, light(&camera)
{
//startTimer();
@ -97,6 +97,11 @@ void Renderer::RendererGLWidget::setEnableFxaa(bool enable)
finalPass->setEnableFxaa(enable);
}
void Renderer::RendererGLWidget::setEnableSsao(bool enable)
{
enableSsao = enable;
}
QOpenGLTexture randomMap(QOpenGLTexture::Target2D);
void GLAPIENTRY messageCallback(GLenum source,
@ -139,6 +144,7 @@ void RendererGLWidget::initializeGL()
shadowMapPass = std::make_unique<ShadowMapPass>(gl.get(), shadowFboHandle, shadowMapResolution, light, model);
geometryPass = std::make_unique<GeometryPass>(gl.get(), frameWidth, frameHeight, fboPtr, model, projection, view);
pageIDFeedbackPass = std::make_unique<PageIDFeedbackPass>(gl.get(), frameWidth, frameHeight, fboPtr, gbuffers, gPageID, *vtManager);
ssaoPass = std::make_unique<SsaoPass>(gl.get(), frameWidth, frameHeight, gNormal, gPosition, gMetallicRoughness, projection, view);
lightingPass = std::make_unique<LightingPass>(gl.get(), frameWidth, frameHeight, view, camera, light,
gBaseColor, gNormal, gPosition, gMetallicRoughness, shadowGbuffer, irradianceMap, prefilterMap, brdfLUTTexture);
skyboxPass = std::make_unique<SkyboxPass>(gl.get(), fboPtr, projection, view, skyCubemap);
@ -213,6 +219,8 @@ void RendererGLWidget::paintGL()
pageIDFeedbackPass->dispatch();
//gl->Finish();
//vtManager->commitMutex.unlock();
if (enableSsao)
ssaoPass->dispatch();
lightingPass->dispatch();
skyboxPass->dispatch();
toneMappingPass->dispatch();
@ -293,12 +301,16 @@ void RendererGLWidget::resizeGL(int width, int height)
gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, frameWidth, frameHeight, 0, GL_RGB, GL_FLOAT, NULL);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gbuffers[2], 0);
//MetallicRoughness
//R: Metallic, G: Roughness, B: AO
gl->BindTexture(GL_TEXTURE_2D, gbuffers[3]);
gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RG8, frameWidth, frameHeight, 0, GL_RG, GL_UNSIGNED_BYTE, NULL);
gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, frameWidth, frameHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, gbuffers[3], 0);
//gPageID
gl->BindTexture(GL_TEXTURE_2D, gbuffers[4]);

View File

@ -35,6 +35,7 @@ namespace Renderer
void setMainLightYaw(float yaw);
void setExposure(float exposure);
void setEnableFxaa(bool enable);
void setEnableSsao(bool enable);
protected:
void initializeGL() override;
void paintGL() override;
@ -56,6 +57,8 @@ namespace Renderer
int shadowMapResolution;
float exposure = 0.8;
bool enableSsao = true;
QMatrix4x4 projection;
QMatrix4x4 view;
@ -64,6 +67,7 @@ namespace Renderer
std::unique_ptr<ShadowMapPass> shadowMapPass;
std::unique_ptr<GeometryPass> geometryPass;
std::unique_ptr<PageIDFeedbackPass> pageIDFeedbackPass;
std::unique_ptr<SsaoPass> ssaoPass;
std::unique_ptr<LightingPass> lightingPass;
std::unique_ptr<SkyboxPass> skyboxPass;
std::unique_ptr<ToneMappingPass> toneMappingPass;

View File

@ -68,6 +68,9 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent)
QObject::connect(ui.fxaaCheckBox, &QCheckBox::toggled, this, [&](bool checked) {
ui.openGLWidget->setEnableFxaa(checked);
});
QObject::connect(ui.ssaoCheckBox, &QCheckBox::toggled, this, [&](bool checked) {
ui.openGLWidget->setEnableSsao(checked);
});
QObject::connect(ui.lightRadianceSlider, &QSlider::valueChanged, [&](int value) {
ui.openGLWidget->setMainLightRadiance(value / 10.f * QVector3D(0.7529, 0.7450, 0.6784).normalized());