diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dd136bd0..a2e32bc08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ option(WITH_PICKING_EXAMPLE "Build Picking example" OFF) option(WITH_PRIMITIVES_EXAMPLE "Build Primitives example" OFF) option(WITH_RAYTRACING_EXAMPLE "Build Ray Tracing example" OFF) option(WITH_SHADOWS_EXAMPLE "Build Shadow Mapping example" OFF) +option(WITH_SSAO_EXAMPLE "Build Screen Space Ambient Occlusion Example" OFF) option(WITH_TEXT_EXAMPLE "Build Text example (requires some TTF font plugin)" OFF) cmake_dependent_option(WITH_TEXTUREDTRIANGLE_EXAMPLE "Build TexturedTriangle example (requires some TGA importer plugin)" OFF "NOT MAGNUM_TARGET_GLES" OFF) option(WITH_TRIANGLE_EXAMPLE "Build Triangle example" ON) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ed4b3d632..46c7d6e1b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,10 @@ if(WITH_SHADOWS_EXAMPLE) add_subdirectory(shadows) endif() +if(WITH_SSAO_EXAMPLE) + add_subdirectory(ssao) +endif() + if(WITH_TEXT_EXAMPLE) add_subdirectory(text) endif() diff --git a/src/ssao/.gitignore b/src/ssao/.gitignore new file mode 100644 index 000000000..dd0bb0e5a --- /dev/null +++ b/src/ssao/.gitignore @@ -0,0 +1,2 @@ +/cmake-* +.idea/ diff --git a/src/ssao/Armadillo.ply b/src/ssao/Armadillo.ply new file mode 100644 index 000000000..5db080060 Binary files /dev/null and b/src/ssao/Armadillo.ply differ diff --git a/src/ssao/CMakeLists.txt b/src/ssao/CMakeLists.txt new file mode 100644 index 000000000..49fcfef2c --- /dev/null +++ b/src/ssao/CMakeLists.txt @@ -0,0 +1,88 @@ +# +# This file is part of Magnum. +# +# Original authors — credit is appreciated but not required: +# +# 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — +# Vladimír Vondruš +# 2020 — Janos Meny +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or distribute +# this software, either in source code form or as a compiled binary, for any +# purpose, commercial or non-commercial, and by any means. +# +# In jurisdictions that recognize copyright laws, the author or authors of +# this software dedicate any and all copyright interest in the software to +# the public domain. We make this dedication for the benefit of the public +# at large and to the detriment of our heirs and successors. We intend this +# dedication to be an overt act of relinquishment in perpetuity of all +# present and future rights to this software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +cmake_minimum_required(VERSION 3.4) + +project(MagnumSSAOExample CXX) + +# Add module path in case this is project root +if(PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../../modules/" ${CMAKE_MODULE_PATH}) +endif() + +set(SSAO_EXAMPLE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(SSAO_EXAMPLE_INSTALL_DIR ${MAGNUM_DATA_INSTALL_DIR}/examples/ssao) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + +find_package(Corrade REQUIRED Main) +find_package(Magnum REQUIRED + GL + MeshTools + Shaders + SceneGraph + Trade + Sdl2Application + DebugTools) +find_package(MagnumIntegration REQUIRED ImGui) + +set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) + +corrade_add_resource(SSAO_Rcs Shaders/resources.conf) + +add_executable(magnum-ssao WIN32 + SsaoExample.cpp + ../arcball/ArcBall.cpp + Shaders/DeferredGeometryShader.cpp + Shaders/DeferredGeometryShader.h + Shaders/SsaoShader.cpp + Shaders/SsaoShader.h + Shaders/SsaoApplyShader.cpp + Shaders/SsaoApplyShader.h + ${SSAO_Rcs}) + +target_link_libraries(magnum-ssao PRIVATE + Corrade::Main + Magnum::Application + Magnum::GL + Magnum::Magnum + Magnum::MeshTools + Magnum::Trade + Magnum::Shaders + Magnum::DebugTools + MagnumIntegration::ImGui) + +target_include_directories(magnum-ssao PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + +install(TARGETS magnum-ssao DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}) +install(FILES Armadillo.ply DESTINATION ${MAGNUM_DATA_INSTALL_DIR}/examples/ssao) + +# Make the executable a default target to build & run in Visual Studio +set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT magnum-ssao) diff --git a/src/ssao/Shaders/DeferredGeometry.frag b/src/ssao/Shaders/DeferredGeometry.frag new file mode 100644 index 000000000..e5d032f0a --- /dev/null +++ b/src/ssao/Shaders/DeferredGeometry.frag @@ -0,0 +1,47 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +in highp vec3 viewPosition; +in highp vec3 viewNormal; + +layout(location = 0) +out highp vec4 color; +layout(location = 1) +out highp vec3 position; +layout(location = 2) +out highp vec3 normal; + +uniform vec4 diffuseColor; + +void main() { + color = diffuseColor; + position = viewPosition; + normal = viewNormal; +} \ No newline at end of file diff --git a/src/ssao/Shaders/DeferredGeometry.vert b/src/ssao/Shaders/DeferredGeometry.vert new file mode 100644 index 000000000..efa2aee5b --- /dev/null +++ b/src/ssao/Shaders/DeferredGeometry.vert @@ -0,0 +1,49 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +in highp vec4 position; +layout(location = 5) //@TODO get rid of layout specifier +in highp vec3 normal; + +uniform mat4 transformation; +uniform mat3 normalMatrix; +uniform mat4 projection; + +out highp vec3 viewPosition; +out highp vec3 viewNormal; + +void main(){ + vec4 position4 = transformation*position; + + viewPosition = position4.xyz; + viewNormal = normalMatrix*normal; + + gl_Position = projection*position4; +} \ No newline at end of file diff --git a/src/ssao/Shaders/DeferredGeometryShader.cpp b/src/ssao/Shaders/DeferredGeometryShader.cpp new file mode 100644 index 000000000..1970bedec --- /dev/null +++ b/src/ssao/Shaders/DeferredGeometryShader.cpp @@ -0,0 +1,96 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "DeferredGeometryShader.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Magnum { namespace Examples { + +DeferredGeometryShader::DeferredGeometryShader() { + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL330); + + const Utility::Resource rs{"ssao-data"}; + + GL::Shader vert{GL::Version::GL330, GL::Shader::Type::Vertex}; + GL::Shader frag{GL::Version::GL330, GL::Shader::Type::Fragment}; + + vert.addSource(rs.get("DeferredGeometry.vert")); + + frag.addSource(rs.get("DeferredGeometry.frag")); + + CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); + + attachShaders({vert, frag}); + + CORRADE_INTERNAL_ASSERT_OUTPUT(link()); + + bindAttributeLocation(Position::Location, "position"); + bindAttributeLocation(Normal::Location, "normal"); + + bindFragmentDataLocation(AlbedoOutput, "color"); + bindFragmentDataLocation(PositionsOutput, "position"); + bindFragmentDataLocation(NormalsOutput, "normal"); + + _transformationUniform = uniformLocation("transformation"); + _normalMatrixUniform = uniformLocation("normalMatrix"); + _projectionUniform = uniformLocation("projection"); + _diffuseColorUniform = uniformLocation("diffuseColor"); +} + +DeferredGeometryShader& DeferredGeometryShader::setTransformation(const Matrix4& transformation){ + setUniform(_transformationUniform, transformation); + return *this; +} + +DeferredGeometryShader& DeferredGeometryShader::setNormalMatrix(const Matrix3x3& normalMatrix){ + setUniform(_normalMatrixUniform, normalMatrix); + return *this; +} + +DeferredGeometryShader& DeferredGeometryShader::setProjection(const Matrix4& projection) { + setUniform(_projectionUniform, projection); + return *this; +} + +DeferredGeometryShader& DeferredGeometryShader::setDiffuseColor(const Color4 &color) { + setUniform(_diffuseColorUniform, color); + return *this; +} + +}} \ No newline at end of file diff --git a/src/ssao/Shaders/DeferredGeometryShader.h b/src/ssao/Shaders/DeferredGeometryShader.h new file mode 100644 index 000000000..8b40e2e87 --- /dev/null +++ b/src/ssao/Shaders/DeferredGeometryShader.h @@ -0,0 +1,75 @@ +#ifndef Magnum_Examples_Ssao_DeferredGeometryShader_h +#define Magnum_Examples_Ssao_DeferredGeometryShader_h + +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include + +namespace Magnum { namespace Examples { + +class DeferredGeometryShader : public Magnum::GL::AbstractShaderProgram { +public: + + using Position = Magnum::Shaders::Generic3D::Position; + using Normal = Magnum::Shaders::Generic3D::Normal; + using TextureCoordinates = Magnum::Shaders::Generic3D::TextureCoordinates; + + enum { + AlbedoOutput = 0, + PositionsOutput = 1, + NormalsOutput = 2, + }; + + explicit DeferredGeometryShader(Magnum::NoCreateT) : Magnum::GL::AbstractShaderProgram{Magnum::NoCreate} {}; + + explicit DeferredGeometryShader(); + + DeferredGeometryShader& setTransformation(const Matrix4& transformation); + + DeferredGeometryShader& setNormalMatrix(const Matrix3x3& normalMatrix); + + DeferredGeometryShader& setProjection(const Matrix4& projection); + + DeferredGeometryShader& setDiffuseColor(const Color4& color); + +private: + + Int _transformationUniform, + _normalMatrixUniform, + _projectionUniform, + _diffuseColorUniform; +}; + +}} + +#endif \ No newline at end of file diff --git a/src/ssao/Shaders/FullScreenTriangle.vert b/src/ssao/Shaders/FullScreenTriangle.vert new file mode 100644 index 000000000..bda5ee165 --- /dev/null +++ b/src/ssao/Shaders/FullScreenTriangle.vert @@ -0,0 +1,39 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +in highp vec4 position; + +out highp vec2 textureCoordinate; + +void main() +{ + gl_Position = vec4((gl_VertexID == 2) ? 3.0 : -1.0, (gl_VertexID == 1) ? -3.0 : 1.0, 0.0, 1.0); + textureCoordinate = gl_Position.xy*0.5 + vec2(0.5); +} \ No newline at end of file diff --git a/src/ssao/Shaders/Ssao.comp b/src/ssao/Shaders/Ssao.comp new file mode 100644 index 000000000..c246eb720 --- /dev/null +++ b/src/ssao/Shaders/Ssao.comp @@ -0,0 +1,100 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +layout(local_size_x = 1, local_size_y = 1) in; + +layout(binding = 0) +uniform sampler2D positions; + +layout(binding = 1) +uniform sampler2D normals; + +layout(binding = 2) +uniform sampler2D noise; + +layout(r32f, binding = 3) +uniform writeonly image2D ambientOcclusion; + +layout(location = 0) +uniform mat4 projection; + +layout(location = 1) +uniform float bias = 0.2; + +layout(location = 2) +uniform float radius = 1; + +layout(location = 3) +uniform vec3 samples[SAMPLE_COUNT]; + +vec2 noiseScale = vec2(textureSize(positions,0))/vec2(textureSize(noise,0)); + +void main() +{ + const ivec2 size = imageSize(ambientOcclusion); + const ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + const vec2 coordHom = vec2(coord)/vec2(size); + + vec3 position = texture(positions, coordHom).xyz; + + if(position.z == 0.) { + imageStore(ambientOcclusion, coord, vec4(1, 0, 0, 0)); + return; + } + + vec3 normal = normalize(texture(normals, coordHom).xyz); + vec3 randomVector = normalize(texture(noise, coordHom*noiseScale).xyz); + + /* tangent-space to view-space */ + vec3 tangent = normalize(randomVector - normal * dot(randomVector, 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 < SAMPLE_COUNT; ++i) { + + vec3 randomSample = TBN*samples[i]; + randomSample = position + randomSample*radius; + + vec4 sampleNDC = projection*vec4(randomSample, 1.); + sampleNDC.xyz /= sampleNDC.w; /* perspective division */ + vec2 sampleCoords = sampleNDC.xy*0.5 + 0.5; + float sampleDepth = texture(positions, sampleCoords).z; + + // range check & accumulate + float rangeCheck = smoothstep(0.0, 1.0, radius / abs(position.z - sampleDepth)); + occlusion += (sampleDepth >= randomSample.z + bias ? 1.0 : 0.0)*rangeCheck; + } + + occlusion = 1.0 - (occlusion / float(SAMPLE_COUNT)); + + imageStore(ambientOcclusion, coord, vec4(occlusion, 0, 0, 0)); +} diff --git a/src/ssao/Shaders/Ssao.frag b/src/ssao/Shaders/Ssao.frag new file mode 100644 index 000000000..d9c0ebaf9 --- /dev/null +++ b/src/ssao/Shaders/Ssao.frag @@ -0,0 +1,78 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +in vec2 textureCoordinate; + +uniform sampler2D positions; +uniform sampler2D normals; +uniform sampler2D noise; + +uniform mat4 projection; +uniform float bias = 0.2; +uniform float radius = 1.; +uniform vec3 samples[SAMPLE_COUNT]; + +out float ambientOcclusion; + +vec2 noiseScale = vec2(textureSize(positions,0))/vec2(textureSize(noise,0)); + +void main() +{ + vec3 position = texture(positions, textureCoordinate).xyz; + if(position.z == 0) { + ambientOcclusion = 1; + return; + } + vec3 normal = normalize(texture(normals, textureCoordinate).xyz); + vec3 randomVector = normalize(texture(noise, textureCoordinate*noiseScale).xyz); + + /* tangent-space to view-space */ + vec3 tangent = normalize(randomVector - normal*dot(randomVector, 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 < SAMPLE_COUNT; ++i) { + vec3 randomSample = TBN*samples[i]; + randomSample = position + randomSample * radius; + + vec4 sampleNDC = projection*vec4(randomSample, 1.); + sampleNDC.xyz /= sampleNDC.w; /* perspective division */ + vec2 sampleCoords = sampleNDC.xy*0.5 + 0.5; + float sampleDepth = texture(positions, sampleCoords).z; + + /* range check & accumulate */ + float rangeCheck = smoothstep(0.0, 1.0, radius/abs(position.z - sampleDepth)); + occlusion += (sampleDepth >= randomSample.z + bias ? 1.0 : 0.0)*rangeCheck; + } + + ambientOcclusion = 1.0 - (occlusion / float(SAMPLE_COUNT)); +} diff --git a/src/ssao/Shaders/SsaoApply.frag b/src/ssao/Shaders/SsaoApply.frag new file mode 100644 index 000000000..c830a7522 --- /dev/null +++ b/src/ssao/Shaders/SsaoApply.frag @@ -0,0 +1,91 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +in vec2 textureCoordinate; + +uniform sampler2D positionTexture; +uniform sampler2D normalTexture; +uniform sampler2D albedoTexture; +uniform sampler2D ambientOcclusionTexture; + +uniform vec3 lightPosition; +uniform vec3 lightColor; +uniform float shininess; +uniform vec3 specularColor; + +out vec4 fragmentColor; + +#define BLURR_RADIUS 5 + +void main() +{ + /* retrieve data from gbuffer */ + vec3 position = texture(positionTexture, textureCoordinate).rgb; + +#ifndef DRAW_OCCLUSION_FACTOR + vec3 normal = texture(normalTexture, textureCoordinate).rgb; + vec3 albedo = texture(albedoTexture, textureCoordinate).rgb; +#endif + + /* blur occlusion factor */ + vec2 texelSize = 1.0 / vec2(textureSize(ambientOcclusionTexture, 0)); + float blurredOcclusion = 0.0; + for (int x = -BLURR_RADIUS; x <= BLURR_RADIUS; ++x) { + for (int y = -BLURR_RADIUS; y <= BLURR_RADIUS; ++y) { + vec2 offset = vec2(float(x), float(y)) * texelSize; + blurredOcclusion += texture(ambientOcclusionTexture, textureCoordinate + offset).x; + } + } + blurredOcclusion /= float((BLURR_RADIUS*2 + 1)*(BLURR_RADIUS*2 + 1)); + blurredOcclusion = pow(blurredOcclusion, 4); /* To make the effect more visible */ + +#ifndef DRAW_OCCLUSION_FACTOR + /* No ambient color */ + fragmentColor = vec4(0,0,0,1); + + /* normalize normal */ + mediump vec3 normalizedTransformedNormal = normalize(normal); + + /* Add diffuse color */ + highp vec3 normalizedLightDirection = normalize(lightPosition - position); + lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection)); + fragmentColor.rgb += albedo*lightColor*intensity*blurredOcclusion; + + /* Add specular color, if needed */ + if(intensity > 0.001) { + highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); + mediump float specularity = clamp(pow(max(0.0, dot(normalize(-position), reflection)), shininess), 0.0, 1.0); + fragmentColor.rgb += specularColor*specularity; /* white specular color */ + } +#else + fragmentColor.rgb = vec3(blurredOcclusion); +#endif +} + diff --git a/src/ssao/Shaders/SsaoApplyShader.cpp b/src/ssao/Shaders/SsaoApplyShader.cpp new file mode 100644 index 000000000..99b744767 --- /dev/null +++ b/src/ssao/Shaders/SsaoApplyShader.cpp @@ -0,0 +1,130 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "SsaoApplyShader.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Magnum; + +namespace Magnum { namespace Examples { + +namespace { + +enum { + PositionUnit = 0, + NormalUnit = 1, + AlbedoUnit = 2, + AmbientOcclusionUnit = 3 +}; + +} + +SsaoApplyShader::SsaoApplyShader(Flag flag) { + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL330); + + Utility::Resource rs{"ssao-data"}; + + GL::Shader vert{GL::Version::GL330, GL::Shader::Type::Vertex}; + GL::Shader frag{GL::Version::GL330, GL::Shader::Type::Fragment}; + + vert.addSource(rs.get("FullScreenTriangle.vert")); + frag.addSource(flag == Flag::DrawAmbientOcclusion ? "#define DRAW_OCCLUSION_FACTOR\n" : "") + .addSource(rs.get("SsaoApply.frag")); + + CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); + + attachShaders({vert, frag}); + + CORRADE_INTERNAL_ASSERT_OUTPUT(link()); + + if(flag != Flag::DrawAmbientOcclusion) { + setUniform(uniformLocation("positionTexture"), PositionUnit); + setUniform(uniformLocation("normalTexture"), NormalUnit); + setUniform(uniformLocation("albedoTexture"), AlbedoUnit); + + _shininessUniform = uniformLocation("shininess"); + _lightPositionUniform = uniformLocation("lightPosition"); + _lightColorUniform = uniformLocation("lightColor"); + _specularColorUniform = uniformLocation("specularColor"); + } + + setUniform(uniformLocation("ambientOcclusionTexture"), AmbientOcclusionUnit); +} + +SsaoApplyShader& SsaoApplyShader::bindAlbedoTexture(GL::Texture2D& texture){ + texture.bind(AlbedoUnit); + return *this; +} + +SsaoApplyShader& SsaoApplyShader::bindOcclusionTexture(GL::Texture2D& texture){ + texture.bind(AmbientOcclusionUnit); + return *this; +} + +SsaoApplyShader& SsaoApplyShader::bindNormalTexture(GL::Texture2D& texture){ + texture.bind(NormalUnit); + return *this; +} + +SsaoApplyShader& SsaoApplyShader::bindPositionTexture(GL::Texture2D& texture){ + texture.bind(PositionUnit); + return *this; +} + +SsaoApplyShader& SsaoApplyShader::setLightPosition(const Vector3& position){ + setUniform(_lightPositionUniform, position); + return *this; +} + +SsaoApplyShader& SsaoApplyShader::setLightColor(const Color3& color){ + setUniform(_lightColorUniform, color); + return *this; +} + +SsaoApplyShader& SsaoApplyShader::setShininess(Float shininess){ + setUniform(_shininessUniform, shininess); + return *this; +} + +SsaoApplyShader& SsaoApplyShader::setSpecularColor(const Color3& color){ + setUniform(_specularColorUniform, color); + return *this; +} + +}} diff --git a/src/ssao/Shaders/SsaoApplyShader.h b/src/ssao/Shaders/SsaoApplyShader.h new file mode 100644 index 000000000..51b23d536 --- /dev/null +++ b/src/ssao/Shaders/SsaoApplyShader.h @@ -0,0 +1,76 @@ +#ifndef Magnum_Examples_Ssao_SsaoApplyShader_h +#define Magnum_Examples_Ssao_SsaoApplyShader_h + +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include + +namespace Magnum { namespace Examples { + +class SsaoApplyShader : public Magnum::GL::AbstractShaderProgram { +public: + + enum class Flag : UnsignedInt { + DrawAmbientOcclusion = 1, + }; + + explicit SsaoApplyShader(Flag flag = {}); + + explicit SsaoApplyShader(Magnum::NoCreateT) : Magnum::GL::AbstractShaderProgram{Magnum::NoCreate} {}; + + SsaoApplyShader& bindAlbedoTexture(Magnum::GL::Texture2D&); + + SsaoApplyShader& bindOcclusionTexture(Magnum::GL::Texture2D&); + + SsaoApplyShader& bindPositionTexture(Magnum::GL::Texture2D&); + + SsaoApplyShader& bindNormalTexture(Magnum::GL::Texture2D&); + + SsaoApplyShader& setShininess(Float); + + SsaoApplyShader& setLightPosition(const Vector3&); + + SsaoApplyShader& setLightColor(const Color3&); + + SsaoApplyShader& setSpecularColor(const Color3&); +private: + Int _lightPositionUniform = 0, + _lightColorUniform = 1, + _shininessUniform = 2, + _specularColorUniform = 3; +}; + +}} + +#endif \ No newline at end of file diff --git a/src/ssao/Shaders/SsaoShader.cpp b/src/ssao/Shaders/SsaoShader.cpp new file mode 100644 index 000000000..b07aa56fd --- /dev/null +++ b/src/ssao/Shaders/SsaoShader.cpp @@ -0,0 +1,151 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "SsaoShader.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace Magnum { namespace Examples { + +namespace { +enum { + PositionUnit = 0, + NormalUnit = 1, + NoiseUnit = 2, + OcclusionUnit = 3, +}; +} + +SsaoShader::SsaoShader(Flag flag, UnsignedInt sampleCount) { + + constexpr Float mean = 0.f; + constexpr Float std = 0.4f; + + Containers::Array randomSamples(Containers::NoInit, sampleCount); + std::default_random_engine engine; + std::normal_distribution normalDistr(mean, std); + + for (UnsignedInt i = 0; i < sampleCount; ++i) { + Vector3 p{normalDistr(engine), normalDistr(engine), normalDistr(engine)}; + if(p.z() < 0) + p.z() *= -1.f; + p = Math::fmod(p, Vector3{1}); + randomSamples[i] = p; + } + Utility::Resource rs{"ssao-data"}; + + if(flag == Flag::FragmentShader){ + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL330); + GL::Shader vert{GL::Version::GL330, GL::Shader::Type::Vertex}; + GL::Shader frag{GL::Version::GL330, GL::Shader::Type::Fragment}; + + vert.addSource(rs.get("FullScreenTriangle.vert")); + frag.addSource(Utility::formatString("#define SAMPLE_COUNT {}\n", sampleCount)) + .addSource(rs.get("Ssao.frag")); + + CORRADE_INTERNAL_ASSERT_OUTPUT(GL::Shader::compile({vert, frag})); + attachShaders({vert, frag}); + } else { + MAGNUM_ASSERT_GL_VERSION_SUPPORTED(GL::Version::GL430); + GL::Shader comp{GL::Version::GL430, GL::Shader::Type::Compute}; + comp.addSource(Utility::formatString("#define SAMPLE_COUNT {}\n", sampleCount)) + .addSource(rs.get("Ssao.comp")); + + CORRADE_INTERNAL_ASSERT_OUTPUT(comp.compile()); + attachShaders({comp}); + } + + CORRADE_INTERNAL_ASSERT_OUTPUT(link()); + + bindFragmentDataLocation(AmbientOcclusionOutput, "ambientOcclusion"); + + if(flag == Flag::FragmentShader){ + setUniform(uniformLocation("positions"), PositionUnit); + setUniform(uniformLocation("normals"), NormalUnit); + setUniform(uniformLocation("noise"), NoiseUnit); + + _projectionUniform = uniformLocation("projection"); + _biasUniform = uniformLocation("bias"); + _radiusUniform = uniformLocation("radius"); + _samplesUniform = uniformLocation("samples"); + } + + setUniform(_samplesUniform, randomSamples); +} + +SsaoShader& SsaoShader::bindPositionTexture(GL::Texture2D& texture){ + texture.bind(PositionUnit); + return *this; +} + +SsaoShader& SsaoShader::bindNormalTexture(GL::Texture2D& texture){ + texture.bind(NormalUnit); + return *this; +} + +SsaoShader& SsaoShader::bindOcclusionTexture(GL::Texture2D& texture){ + texture.bindImage(OcclusionUnit, 0, GL::ImageAccess::WriteOnly, GL::ImageFormat::R32F); + return *this; +} + +SsaoShader& SsaoShader::bindNoiseTexture(Magnum::GL::Texture2D& texture) { + texture.bind(NoiseUnit); + return *this; +} + +SsaoShader& SsaoShader::setProjectionMatrix(const Matrix4& projection) { + setUniform(_projectionUniform, projection); + return *this; +} + +SsaoShader& SsaoShader::setSampleRadius(Float radius) { + setUniform(_radiusUniform, radius); + return *this; +} + +SsaoShader& SsaoShader::setBias(Float bias) { + setUniform(_biasUniform, bias); + return *this; +} + +}} diff --git a/src/ssao/Shaders/SsaoShader.h b/src/ssao/Shaders/SsaoShader.h new file mode 100644 index 000000000..0f8364f90 --- /dev/null +++ b/src/ssao/Shaders/SsaoShader.h @@ -0,0 +1,81 @@ +#ifndef Magnum_Examples_Ssao_SsaoShader_h +#define Magnum_Examples_Ssao_SsaoShader_h + +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include + +namespace Magnum { namespace Examples { + +class SsaoShader : public GL::AbstractShaderProgram { +public: + enum { + AmbientOcclusionOutput = 0, + }; + + enum class Flag : UnsignedInt { + ComputeShader, + FragmentShader + }; + + explicit SsaoShader(Flag flag = Flag::FragmentShader, UnsignedInt sampleCount = 64); + + explicit SsaoShader(Magnum::NoCreateT) : GL::AbstractShaderProgram{Magnum::NoCreate} {}; + + SsaoShader& bindPositionTexture(GL::Texture2D&); + + SsaoShader& bindNormalTexture(GL::Texture2D&); + + SsaoShader& bindNoiseTexture(GL::Texture2D&); + + SsaoShader& bindOcclusionTexture(GL::Texture2D&); + + SsaoShader& setProjectionMatrix(Matrix4 const&); + + SsaoShader& setSampleRadius(Float); + + SsaoShader& setBias(Float); + +private: + + UnsignedInt _projectionUniform = 0, + _biasUniform = 1, + _radiusUniform = 2, + _samplesUniform = 3; +}; + +}} + +#endif \ No newline at end of file diff --git a/src/ssao/Shaders/resources.conf b/src/ssao/Shaders/resources.conf new file mode 100644 index 000000000..bcb93b3da --- /dev/null +++ b/src/ssao/Shaders/resources.conf @@ -0,0 +1,20 @@ +group = ssao-data + +[file] +filename=DeferredGeometry.vert + +[file] +filename=DeferredGeometry.frag + +[file] +filename=SsaoApply.frag + +[file] +filename=FullScreenTriangle.vert + +[file] +filename=Ssao.frag + +[file] +filename=Ssao.comp + diff --git a/src/ssao/SsaoExample.cpp b/src/ssao/SsaoExample.cpp new file mode 100644 index 000000000..68afd2e92 --- /dev/null +++ b/src/ssao/SsaoExample.cpp @@ -0,0 +1,505 @@ +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "../arcball/ArcBall.h" + +#include "configure.h" +#include "Shaders/DeferredGeometryShader.h" +#include "Shaders/SsaoShader.h" +#include "Shaders/SsaoApplyShader.h" + +#include +#include +#include /* until Corrade/Directory can handle Containers::String */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace Magnum { namespace Examples { + +void setupTexture(GL::Texture2D& texture, Vector2i const& size, GL::TextureFormat format){ + texture = GL::Texture2D{}; + texture.setMagnificationFilter(GL::SamplerFilter::Linear) + .setMinificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::ClampToEdge) + .setStorage(1, format, size); +} + +using namespace Math::Literals; + +class SsaoExample: public Platform::Application { + public: + explicit SsaoExample(const Arguments& arguments); + + private: + void drawEvent() override; + + void viewportEvent(ViewportEvent& event) override; + + void keyPressEvent(KeyEvent& event) override; + void keyReleaseEvent(KeyEvent& event) override; + + void mousePressEvent(MouseEvent& event) override; + void mouseReleaseEvent(MouseEvent& event) override; + void mouseMoveEvent(MouseMoveEvent& event) override; + void mouseScrollEvent(MouseScrollEvent& event) override; + void textInputEvent(TextInputEvent& event) override; + + void setupFramebuffer(const Vector2i& size); + void drawSettings(); + + DeferredGeometryShader _geometryShader{NoCreate}; + SsaoApplyShader _ssaoApplyShader{NoCreate}; + SsaoShader _ssaoShader{NoCreate}; + + Magnum::Shaders::Phong _phong{NoCreate}; + + Containers::Optional _arcball; + Matrix4 _projection; + + GL::Mesh _mesh{NoCreate}, _screenAlignedTriangle{NoCreate}; + + GL::Framebuffer _framebuffer{NoCreate}; + + GL::Texture2D _albedo{NoCreate}; + GL::Texture2D _positions{NoCreate}; + GL::Texture2D _normals{NoCreate}; + GL::Texture2D _occlusion{NoCreate}; + GL::Texture2D _noise{NoCreate}; + + GL::Texture2D _depth{NoCreate}; + + /* Profiling */ + DebugTools::GLFrameProfiler _profiler; + + Color4 _diffuseColor{0.9}; + Color4 _specularColor{0.3}; + bool _applySsao = true; + Float _radius = 5.f; + Float _bias = 0.5f; + + SsaoShader::Flag _ssaoFlag = SsaoShader::Flag::FragmentShader; + SsaoApplyShader::Flag _ssaoApplyFlag = {}; + + ImGuiIntegration::Context _imgui{NoCreate}; +}; + +SsaoExample::SsaoExample(const Arguments& arguments) : + Platform::Application{arguments, NoCreate} +{ + /* Try to locate the mesh, first in the source directory, then in the + installation directory and as a fallback next to the executable */ + Containers::String meshPath; + { + if(Utility::Directory::exists(SSAO_EXAMPLE_DIR)) + meshPath = SSAO_EXAMPLE_DIR; + else if(Utility::Directory::exists(SSAO_EXAMPLE_INSTALL_DIR)) + meshPath = SSAO_EXAMPLE_INSTALL_DIR; + else + meshPath = Utility::Directory::path(Utility::Directory::executableLocation()); + meshPath = Utility::Directory::join(meshPath, "Armadillo.ply"); + + /* Finally, provide a way for the user to override the model directory */ + Utility::Arguments args; + args.addFinalOptionalArgument("mesh", meshPath) + .setHelp("mesh", "Path to the mesh you want to import") + .addSkippedPrefix("magnum", "engine-specific options") + .setGlobalHelp("Press P to toggle between phong shading and phong shading + ssao\n" + "Press H in debug mode to hot reload the shaders\n" + "Press C in debug mode to use a compute shader in the ssao pass\n" + "Press R to reset the camera\n" + "Press L to toggle lagging for the camera controls\n") + .parse(arguments.argc, arguments.argv); + /* relative paths are brittle, so prepend CWD to them if needed */ + meshPath = Utility::Directory::join(Utility::Directory::current(), args.value("mesh")); + } + + /* Setup window */ + { + const Vector2 dpiScaling = this->dpiScaling({}); + Configuration conf; + conf.setTitle("Magnum SSAO Example") + .setSize(conf.size(), dpiScaling) + .setWindowFlags(Configuration::WindowFlag::Resizable); + GLConfiguration glConf; + glConf.setSampleCount(0); + if(!tryCreate(conf, glConf)) { + create(conf, glConf.setSampleCount(0)); + } + } + + GL::Renderer::enable(GL::Renderer::Feature::DepthTest); + GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); + + /* setup imgui context and proper blending */ + { + _imgui = ImGuiIntegration::Context(Vector2{windowSize()}/dpiScaling(), + windowSize(), framebufferSize()); + GL::Renderer::setBlendEquation(GL::Renderer::BlendEquation::Add, + GL::Renderer::BlendEquation::Add); + GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::SourceAlpha, + GL::Renderer::BlendFunction::OneMinusSourceAlpha); + } + + /* Load Mesh, setup textures and framebuffer*/ + { + PluginManager::Manager manager; + Containers::Pointer importer = manager.loadAndInstantiate("AnySceneImporter"); + if(!importer) + std::exit(1); + if(!importer->openFile(meshPath)) + std::exit(4); + + const auto meshData = importer->mesh(0); + _mesh = MeshTools::compile(*meshData); + _screenAlignedTriangle = GL::Mesh{}; + _screenAlignedTriangle.setCount(3); + + std::random_device device; + std::default_random_engine engine(device()); + std::uniform_real_distribution distr(-1,1); + Containers::Array noise(Containers::NoInit, 16); + for (Vector4& n : noise) + n = Vector4{distr(engine),distr(engine),0,0}; + + _noise = GL::Texture2D{}; + ImageView2D view{PixelFormat::RGBA32F, {4, 4}, noise}; + _noise.setMagnificationFilter(GL::SamplerFilter::Linear) + .setMinificationFilter(GL::SamplerFilter::Linear) + .setWrapping(GL::SamplerWrapping::Repeat) + .setStorage(1, GL::TextureFormat::RGBA32F, {4, 4}) + .setSubImage(0, {}, view); + + const Range2Di viewport = GL::defaultFramebuffer.viewport(); + const Vector2i vpSize = viewport.size(); + + _framebuffer = GL::Framebuffer{viewport}; + setupFramebuffer(vpSize); + GL::Renderer::setClearColor({}); + + _geometryShader = DeferredGeometryShader{}; + _ssaoShader = SsaoShader{_ssaoFlag}; + _ssaoApplyShader = SsaoApplyShader{}; + _phong = Magnum::Shaders::Phong{}; + } + + /* Set up the arcball and projection */ + { + const Vector3 eye = Vector3::zAxis(-500.0f); + const Vector3 center{}; + const Vector3 up = Vector3::yAxis(); + _arcball.emplace(eye, center, up, 45.0_degf, windowSize()); + _arcball->setLagging(0.85f); + + _projection = Matrix4::perspectiveProjection(45.0_degf, Vector2{framebufferSize()}.aspectRatio(), 0.01, 1000); + } + + _profiler = DebugTools::GLFrameProfiler{ + DebugTools::GLFrameProfiler::Value::FrameTime| + DebugTools::GLFrameProfiler::Value::GpuDuration| + DebugTools::GLFrameProfiler::Value::CpuDuration, 180}; + + /* Loop at 60 Hz max */ + setSwapInterval(1); + setMinimalLoopPeriod(16); +} + +void SsaoExample::drawEvent() { + GL::defaultFramebuffer.clear(GL::FramebufferClear::Color|GL::FramebufferClear::Depth); + _profiler.beginFrame(); + + const bool camChanged = _arcball->updateTransformation(); + const Matrix4 tf = _arcball->viewMatrix(); + + /* render the scene into g-buffer */ + if(!_applySsao){ + GL::defaultFramebuffer.bind(); + + _phong.setDiffuseColor(_diffuseColor) + .setTransformationMatrix(tf) + .setNormalMatrix(tf.normalMatrix()) + .setProjectionMatrix(_projection) + .setLightPosition({5.0f, 5.0f, 7.0f}) + .setLightColor(Color4{1.}) + .setShininess(80) + .setSpecularColor(_specularColor) /* no ambient color */ + .draw(_mesh); + } else { + _framebuffer.mapForDraw({ + {DeferredGeometryShader::AlbedoOutput, GL::Framebuffer::ColorAttachment{0}}, + {DeferredGeometryShader::PositionsOutput, GL::Framebuffer::ColorAttachment{1}}, + {DeferredGeometryShader::NormalsOutput, GL::Framebuffer::ColorAttachment{2}} + }) + .clear(GL::FramebufferClear::Depth|GL::FramebufferClear::Color) + .bind(); + + _geometryShader.setTransformation(tf) + .setNormalMatrix(tf.normalMatrix()) + .setProjection(_projection) + .setDiffuseColor(_diffuseColor) + .draw(_mesh); + + _ssaoShader.bindNormalTexture(_normals) + .bindNoiseTexture(_noise) + .bindPositionTexture(_positions) + .setProjectionMatrix(_projection) + .setSampleRadius(_radius) + .setBias(_bias); + + if(_ssaoFlag == SsaoShader::Flag::ComputeShader) { + const Vector2i size = _occlusion.imageSize(0); + const Vector3ui wgCount(size.x(), size.y(), 1); + _ssaoShader.bindOcclusionTexture(_occlusion) + .dispatchCompute(wgCount); + GL::Renderer::setMemoryBarrier(GL::Renderer::MemoryBarrier::TextureFetch); + } else if(_ssaoFlag == SsaoShader::Flag::FragmentShader) { + _framebuffer.mapForDraw({ + {SsaoShader::AmbientOcclusionOutput, GL::Framebuffer::ColorAttachment{3}} + }).clear(GL::FramebufferClear::Color); + _ssaoShader.draw(_screenAlignedTriangle); + } + + GL::defaultFramebuffer.bind(); + _ssaoApplyShader.bindAlbedoTexture(_albedo) + .bindOcclusionTexture(_occlusion) + .bindNormalTexture(_normals) + .bindPositionTexture(_positions) + .setLightPosition({5.0f, 5.0f, 7.0f}) + .setLightColor(Color3{1.f}) + .setShininess(80) + .setSpecularColor(_specularColor.rgb()) + .draw(_screenAlignedTriangle); + } + + _profiler.endFrame(); + + _imgui.newFrame(); + if(ImGui::GetIO().WantTextInput && !isTextInputActive()) + startTextInput(); + else if(!ImGui::GetIO().WantTextInput && isTextInputActive()) + stopTextInput(); + + drawSettings(); + _imgui.updateApplicationCursor(*this); + + GL::Renderer::enable(GL::Renderer::Feature::Blending); + GL::Renderer::enable(GL::Renderer::Feature::ScissorTest); + GL::Renderer::disable(GL::Renderer::Feature::FaceCulling); + GL::Renderer::disable(GL::Renderer::Feature::DepthTest); + + _imgui.drawFrame(); + + GL::Renderer::enable(GL::Renderer::Feature::DepthTest); + GL::Renderer::enable(GL::Renderer::Feature::FaceCulling); + GL::Renderer::disable(GL::Renderer::Feature::ScissorTest); + GL::Renderer::disable(GL::Renderer::Feature::Blending); + + swapBuffers(); + + redraw(); +} + +void SsaoExample::viewportEvent(ViewportEvent& event) { + const Vector2i fbSize = event.framebufferSize(); + const Vector2i wSize = event.windowSize(); + + GL::defaultFramebuffer.setViewport({{}, fbSize}); + _framebuffer.setViewport({{}, fbSize}); + + _arcball->reshape(wSize); + _projection = Matrix4::perspectiveProjection(45.0_degf, Vector2{fbSize}.aspectRatio(), 0.1, 1000); + + setupFramebuffer(fbSize); + + _imgui.relayout(Vector2{wSize}/event.dpiScaling(), event.windowSize(), fbSize); +} + +void SsaoExample::setupFramebuffer(const Vector2i& size) { + setupTexture(_albedo, size, GL::TextureFormat::RGBA32F); + setupTexture(_positions, size, GL::TextureFormat::RGBA32F); + setupTexture(_normals, size, GL::TextureFormat::RGBA32F); + setupTexture(_occlusion, size, GL::TextureFormat::R32F); + setupTexture(_depth, size, GL::TextureFormat::DepthComponent32F); + + _framebuffer.attachTexture(GL::Framebuffer::BufferAttachment::Depth, _depth, 0) + .attachTexture(GL::Framebuffer::ColorAttachment{0}, _albedo, 0) + .attachTexture(GL::Framebuffer::ColorAttachment{1}, _positions, 0) + .attachTexture(GL::Framebuffer::ColorAttachment{2}, _normals, 0) + .attachTexture(GL::Framebuffer::ColorAttachment{3}, _occlusion, 0); + + CORRADE_INTERNAL_ASSERT(_framebuffer.checkStatus(GL::FramebufferTarget::Draw) == GL::Framebuffer::Status::Complete); +} + +void SsaoExample::drawSettings() { + ImGui::Begin("SSAO Options"); + const float spacing = 10; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); + + using T = std::pair; + const static auto shaderTypes = Containers::array({ + {"Compute Shader", SsaoShader::Flag::ComputeShader}, + {"Fragment Shader", SsaoShader::Flag::FragmentShader} + }); + static std::size_t current = 1; + if(ImGui::BeginCombo("##Shader Type", shaderTypes[current].first)) { + for(std::size_t i = 0; i < shaderTypes.size(); ++i) { + bool isSelected = i == current; + if(ImGui::Selectable(shaderTypes[i].first, isSelected)){ + current = i; + _ssaoFlag = shaderTypes[i].second; + _ssaoShader = SsaoShader{_ssaoFlag}; + } + if(isSelected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + ImGui::Checkbox("Apply SSAO", &_applySsao); + + ImGui::SameLine(); + + static bool drawOcclusion = false; + if(ImGui::Checkbox("Show Occlusion Factor", &drawOcclusion)) { + if(drawOcclusion) + _ssaoApplyFlag = SsaoApplyShader::Flag::DrawAmbientOcclusion; + else + _ssaoApplyFlag = {}; + _ssaoApplyShader = SsaoApplyShader{_ssaoApplyFlag}; + } + + if(ImGui::Button("Hot Reload Shader")) { + Utility::Resource::overrideGroup("ssao-data", "../Shaders/resources.conf"); + _geometryShader = DeferredGeometryShader{}; + _ssaoShader = SsaoShader{_ssaoFlag}; + _ssaoApplyShader = SsaoApplyShader{_ssaoApplyFlag}; + } + + if(ImGui::Button("Reset Camera")) + _arcball->reset(); + + ImGui::SameLine(); + static bool lagging = true; + if(ImGui::Checkbox("Camera Lagging", &lagging)) { + _arcball->setLagging(float(lagging)*0.75); + } + + Containers::String stats = _profiler.statistics(); + ImGui::TextUnformatted(stats.begin(), stats.end()); + + ImGui::PopStyleVar(); + ImGui::End(); +} + +void SsaoExample::keyPressEvent(KeyEvent& event) { + if(_imgui.handleKeyPressEvent(event)) return; +} + +void SsaoExample::keyReleaseEvent(KeyEvent& event) { + if(_imgui.handleKeyReleaseEvent(event)) return; +} + +void SsaoExample::textInputEvent(TextInputEvent& event) { + if(_imgui.handleTextInputEvent(event)) return; +} + +void SsaoExample::mousePressEvent(MouseEvent& event) { + if(_imgui.handleMousePressEvent(event)) return; + /* Enable mouse capture so the mouse can drag outside of the window */ + /** @todo replace once https://github.com/mosra/magnum/pull/419 is in */ + SDL_CaptureMouse(SDL_TRUE); + _arcball->initTransformation(event.position()); + event.setAccepted(); + redraw(); +} + +void SsaoExample::mouseReleaseEvent(MouseEvent& event) { + if(_imgui.handleMouseReleaseEvent(event)) return; + /* Disable mouse capture again */ + /** @todo replace once https://github.com/mosra/magnum/pull/419 is in */ + SDL_CaptureMouse(SDL_FALSE); +} + +void SsaoExample::mouseMoveEvent(MouseMoveEvent& event) { + if(_imgui.handleMouseMoveEvent(event)) return; + if(!event.buttons()) return; + + if(event.modifiers() & MouseMoveEvent::Modifier::Shift) + _arcball->translate(event.position()); + else _arcball->rotate(event.position()); + + event.setAccepted(); + redraw(); +} + +void SsaoExample::mouseScrollEvent(MouseScrollEvent& event) { + if(_imgui.handleMouseScrollEvent(event)) { + /* Prevent scrolling the page */ + event.setAccepted(); + return; + } + + const Float delta = event.offset().y(); + if(Math::abs(delta) < 1.0e-2f) return; + + _arcball->zoom(delta*50); /* the Aramdillo is in mm */ + + event.setAccepted(); + redraw(); +} + +}} + +MAGNUM_APPLICATION_MAIN(Magnum::Examples::SsaoExample) diff --git a/src/ssao/configure.h.cmake b/src/ssao/configure.h.cmake new file mode 100644 index 000000000..cdb63bb1b --- /dev/null +++ b/src/ssao/configure.h.cmake @@ -0,0 +1,37 @@ +#ifndef Magnum_Examples_Ssao_configure_h +#define Magnum_Examples_Ssao_configure_h + +/* + This file is part of Magnum. + + Original authors — credit is appreciated but not required: + + 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 — + Vladimír Vondruš + 2020 — Janos Meny + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or distribute + this software, either in source code form or as a compiled binary, for any + purpose, commercial or non-commercial, and by any means. + + In jurisdictions that recognize copyright laws, the author or authors of + this software dedicate any and all copyright interest in the software to + the public domain. We make this dedication for the benefit of the public + at large and to the detriment of our heirs and successors. We intend this + dedication to be an overt act of relinquishment in perpetuity of all + present and future rights to this software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#define SSAO_EXAMPLE_DIR "${SSAO_EXAMPLE_DIR}" +#define SSAO_EXAMPLE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${SSAO_EXAMPLE_INSTALL_DIR}" + +#endif \ No newline at end of file