diff --git a/README.md b/README.md index 96d15eafc..22de73638 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ MxEngine is an educational modern-C++ general-purpose 3D game engine. -Right now MxEngine is developed only by me, [#Momo](https://github.com/MomoDeve), but any contributions are welcome and will be reviewed. +Right now MxEngine is developed only by me, [#Momo](https://github.com/MomoDeve) and [#Lucas](https://github.com/fall2019) contributing some features from time to time. Any contributions are welcome and will be reviewed. Fow now MxEngine supports OpenGL as graphic API and targets x64 only. I develop the project in my free time, so updates may be not so frequent! ***Note:** MxEngine is currently being ported to new Vulkan rendering backend. Development progress of the rendering library can be found here: [VulkanAbstractionLayer](https://github.com/asc-community/VulkanAbstractionLayer)* @@ -292,9 +292,8 @@ If you are interesed in libraries MxEngine depend on, consider reading [dependen shadow casting from dynamic lights, screen-space reflections

-## Special thanks -#### God Ray Effect by [#Fall2019](https://github.com/fall2019) +god ray effect

## Projects based on MxEngine diff --git a/src/Core/Components/Camera/CameraController.cpp b/src/Core/Components/Camera/CameraController.cpp index ab0229f9a..5c766c64d 100644 --- a/src/Core/Components/Camera/CameraController.cpp +++ b/src/Core/Components/Camera/CameraController.cpp @@ -27,6 +27,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "CameraController.h" +#include "Utilities/Format/Format.h" #include "Core/Events/WindowResizeEvent.h" #include "Core/Config/GlobalConfig.h" #include "Core/Application/Event.h" @@ -425,11 +426,26 @@ namespace MxEngine return this->renderBuffers->Material; } + TextureHandle CameraController::GetSSRMask()const + { + return this->renderBuffers->SSRMask; + } + TextureHandle CameraController::GetDepthTexture() const { return this->renderBuffers->Depth; } + const MxVector& CameraController::GetHiZ() const + { + return this->renderBuffers->HiZ; + } + + const TextureHandle& CameraController::GetPackedDepth() const + { + return this->renderBuffers->PackedDepthMap; + } + TextureHandle CameraController::GetAverageWhiteTexture() const { return this->renderBuffers->AverageWhite; @@ -456,23 +472,28 @@ namespace MxEngine this->Albedo = Factory::Create(); this->Normal = Factory::Create(); this->Material = Factory::Create(); + this->SSRMask = Factory::Create(); this->Depth = Factory::Create(); this->AverageWhite = Factory::Create(); this->HDR = Factory::Create(); this->SwapHDR1 = Factory::Create(); this->SwapHDR2 = Factory::Create(); + for (int i = 0; i < 4; i++) this->HiZ.emplace_back(Factory::Create()); + this->PackedDepthMap = Factory::Create(); this->Resize(width, height); this->GBuffer->AttachTexture(this->Albedo, Attachment::COLOR_ATTACHMENT0); this->GBuffer->AttachTextureExtra(this->Normal, Attachment::COLOR_ATTACHMENT1); this->GBuffer->AttachTextureExtra(this->Material, Attachment::COLOR_ATTACHMENT2); + this->GBuffer->AttachTextureExtra(this->SSRMask, Attachment::COLOR_ATTACHMENT3); this->GBuffer->AttachTextureExtra(this->Depth, Attachment::DEPTH_ATTACHMENT); std::array attachments = { Attachment::COLOR_ATTACHMENT0, Attachment::COLOR_ATTACHMENT1, Attachment::COLOR_ATTACHMENT2, + Attachment::COLOR_ATTACHMENT3 }; this->GBuffer->UseDrawBuffers(attachments); this->GBuffer->Validate(); @@ -493,10 +514,29 @@ namespace MxEngine this->Material->SetInternalEngineTag(MXENGINE_MAKE_INTERNAL_TAG("camera material")); this->Material->SetWrapType(TextureWrap::CLAMP_TO_EDGE); + this->SSRMask->Load(nullptr, width, height, 1, false, TextureFormat::R32F); + this->SSRMask->SetInternalEngineTag(MXENGINE_MAKE_INTERNAL_TAG("ssr mask")); + this->SSRMask->SetWrapType(TextureWrap::CLAMP_TO_EDGE); + this->Depth->LoadDepth(width, height, TextureFormat::DEPTH32F); this->Depth->SetInternalEngineTag(MXENGINE_MAKE_INTERNAL_TAG("camera depth")); this->Depth->SetWrapType(TextureWrap::CLAMP_TO_EDGE); + for (int i = 0; i < this->HiZ.size(); i++) + { + int scale = 1 << (i + 1); + MxString tag = MxFormat("!camera depth lv{}", i + 1); + int w = width / scale; + int h = height / scale; + this->HiZ[i]->Load(nullptr, w, h, 1, false, TextureFormat::R32F); + this->HiZ[i]->SetInternalEngineTag(tag); + this->HiZ[i]->SetWrapType(TextureWrap::CLAMP_TO_EDGE); + } + + this->PackedDepthMap->Load(nullptr, width, height * 1.5, 1, false, TextureFormat::R32F); + this->PackedDepthMap->SetInternalEngineTag(MXENGINE_MAKE_INTERNAL_TAG("packed depth map")); + this->PackedDepthMap->SetWrapType(TextureWrap::CLAMP_TO_EDGE); + this->AverageWhite->Load(nullptr, 1, 1, 3, false, TextureFormat::RGBA16F); this->AverageWhite->SetInternalEngineTag(MXENGINE_MAKE_INTERNAL_TAG("camera white")); this->AverageWhite->SetWrapType(TextureWrap::CLAMP_TO_EDGE); @@ -520,10 +560,14 @@ namespace MxEngine Factory::Destroy(this->Albedo); Factory::Destroy(this->Normal); Factory::Destroy(this->Material); + Factory::Destroy(this->SSRMask); Factory::Destroy(this->Depth); Factory::Destroy(this->HDR); Factory::Destroy(this->SwapHDR1); Factory::Destroy(this->SwapHDR2); + for (auto& it : this->HiZ) + Factory::Destroy(it); + Factory::Destroy(this->PackedDepthMap); } MXENGINE_REFLECT_TYPE diff --git a/src/Core/Components/Camera/CameraController.h b/src/Core/Components/Camera/CameraController.h index 73b885e33..8e198fd11 100644 --- a/src/Core/Components/Camera/CameraController.h +++ b/src/Core/Components/Camera/CameraController.h @@ -49,6 +49,9 @@ namespace MxEngine TextureHandle Normal; TextureHandle Material; TextureHandle Depth; + TextureHandle SSRMask; + MxVector HiZ; + TextureHandle PackedDepthMap; TextureHandle AverageWhite; TextureHandle HDR; TextureHandle SwapHDR1; @@ -130,7 +133,10 @@ namespace MxEngine TextureHandle GetAlbedoTexture() const; TextureHandle GetNormalTexture() const; TextureHandle GetMaterialTexture() const; + TextureHandle GetSSRMask()const; TextureHandle GetDepthTexture() const; + const MxVector& GetHiZ() const; + const TextureHandle& GetPackedDepth() const; TextureHandle GetAverageWhiteTexture() const; TextureHandle GetHDRTexture() const; TextureHandle GetSwapHDRTexture1() const; diff --git a/src/Core/Components/Camera/CameraSSR.cpp b/src/Core/Components/Camera/CameraSSR.cpp index 99ccf69bf..31c2e698b 100644 --- a/src/Core/Components/Camera/CameraSSR.cpp +++ b/src/Core/Components/Camera/CameraSSR.cpp @@ -37,31 +37,31 @@ namespace MxEngine return this->thickness; } - size_t CameraSSR::GetSteps() const + void CameraSSR::SetThickness(float thickness) { - return this->steps; + this->thickness = Max(thickness, 1.0f); } - float CameraSSR::GetStartDistance() const + int CameraSSR::GetMaxLevel() const { - return this->startDistance; + return this->maxLevel; } - void CameraSSR::SetThickness(float thickness) + void CameraSSR::SetMaxLevel(int level) { - this->thickness = Max(thickness, 0.0f); + this->maxLevel = Clamp(level,1,4); } - void CameraSSR::SetSteps(size_t steps) + int CameraSSR::GetMaxStep()const { - this->steps = steps; + return this->maxStep; } - void CameraSSR::SetStartDistance(float distance) + void CameraSSR::SetMaxStep(int step) { - this->startDistance = Max(distance, 0.0f); + this->maxStep = Clamp(step,150,255); } - + MXENGINE_REFLECT_TYPE { rttr::registration::class_("CameraSSR") @@ -72,19 +72,19 @@ namespace MxEngine .property("thickness", &CameraSSR::GetThickness, &CameraSSR::SetThickness) ( rttr::metadata(MetaInfo::FLAGS, MetaInfo::SERIALIZABLE | MetaInfo::EDITABLE), - rttr::metadata(EditorInfo::EDIT_RANGE, Range { 0.0f, 10000.0f }), + rttr::metadata(EditorInfo::EDIT_RANGE, Range { 0.5f, 20.0f }), rttr::metadata(EditorInfo::EDIT_PRECISION, 0.01f) ) - .property("steps", &CameraSSR::GetSteps, &CameraSSR::SetSteps) + .property("max level", &CameraSSR::GetMaxLevel, &CameraSSR::SetMaxLevel) ( rttr::metadata(MetaInfo::FLAGS, MetaInfo::SERIALIZABLE | MetaInfo::EDITABLE), - rttr::metadata(EditorInfo::EDIT_RANGE, Range { 0.0f, 128.0f }), - rttr::metadata(EditorInfo::EDIT_PRECISION, 0.1f) + rttr::metadata(EditorInfo::EDIT_RANGE, Range { 1, 4 }), + rttr::metadata(EditorInfo::EDIT_PRECISION, 1) ) - .property("start distance", &CameraSSR::GetStartDistance, &CameraSSR::SetStartDistance) + .property("maxStep", &CameraSSR::GetMaxStep, &CameraSSR::SetMaxStep) ( rttr::metadata(MetaInfo::FLAGS, MetaInfo::SERIALIZABLE | MetaInfo::EDITABLE), - rttr::metadata(EditorInfo::EDIT_RANGE, Range { 0.0f, 10000000.0f }), + rttr::metadata(EditorInfo::EDIT_RANGE, Range { 150, 255 }), rttr::metadata(EditorInfo::EDIT_PRECISION, 0.01f) ); } diff --git a/src/Core/Components/Camera/CameraSSR.h b/src/Core/Components/Camera/CameraSSR.h index ff5d75a7b..09d5ea80f 100644 --- a/src/Core/Components/Camera/CameraSSR.h +++ b/src/Core/Components/Camera/CameraSSR.h @@ -36,18 +36,20 @@ namespace MxEngine { MAKE_COMPONENT(CameraSSR); - float thickness = 0.5f; - size_t steps = 10; - float startDistance = 2.0f; + float thickness = 1.5f; + int maxLevel = 2; + int maxStep = 150; + public: CameraSSR() = default; float GetThickness() const; - size_t GetSteps() const; - float GetStartDistance() const; + int GetMaxLevel()const; + int GetMaxStep()const; void SetThickness(float thickness); - void SetSteps(size_t steps); - void SetStartDistance(float distance); + void SetMaxLevel(int level); + void SetMaxStep(int step); + }; } \ No newline at end of file diff --git a/src/Core/Rendering/RenderAdaptor.cpp b/src/Core/Rendering/RenderAdaptor.cpp index 78ef8b50f..29ba68bcb 100644 --- a/src/Core/Rendering/RenderAdaptor.cpp +++ b/src/Core/Rendering/RenderAdaptor.cpp @@ -143,6 +143,11 @@ namespace MxEngine shaderFolder / "gbuffer_fragment.glsl" ); + environment.Shaders["HIZ"_id] = AssetManager::LoadShader( + shaderFolder / "rect_vertex.glsl", + shaderFolder / "hierarchical_depth_buffer_generator.glsl" + ); + environment.Shaders["GBufferMask"_id] = AssetManager::LoadShader( shaderFolder / "gbuffer_vertex.glsl", shaderFolder / "gbuffer_mask_fragment.glsl" diff --git a/src/Core/Rendering/RenderController.cpp b/src/Core/Rendering/RenderController.cpp index 79a0fb3fb..dcb3d7ce9 100644 --- a/src/Core/Rendering/RenderController.cpp +++ b/src/Core/Rendering/RenderController.cpp @@ -79,11 +79,11 @@ namespace MxEngine ); generatorMasked.GenerateFor( *this->Pipeline.Environment.Shaders["DirLightMaskDepthMap"_id], - this->Pipeline.Lighting.DirectionalLights, + this->Pipeline.Lighting.DirectionalLights, ShadowMapGenerator::LoadStoreOptions::LOAD ); } - + if (hasSpotLights) { MAKE_RENDER_PASS_SCOPE("RenderController::PrepareSpotLightMaps()"); @@ -91,13 +91,13 @@ namespace MxEngine *this->Pipeline.Environment.Shaders["SpotLightDepthMap"_id], this->Pipeline.Lighting.SpotLights, ShadowMapGenerator::LoadStoreOptions::CLEAR - ); + ); generatorMasked.GenerateFor( *this->Pipeline.Environment.Shaders["SpotLightMaskDepthMap"_id], this->Pipeline.Lighting.SpotLights, ShadowMapGenerator::LoadStoreOptions::LOAD - ); - } + ); + } if (hasPointLights) { @@ -223,6 +223,9 @@ namespace MxEngine shader.IgnoreNonExistingUniform("camera.position"); shader.IgnoreNonExistingUniform("camera.invViewProjMatrix"); shader.IgnoreNonExistingUniform("material.transparency"); + shader.IgnoreNonExistingUniform("camera.viewMatrix"); + shader.IgnoreNonExistingUniform("camera.projectionMatrix"); + shader.IgnoreNonExistingUniform("camera.invProjectionMatrix"); this->BindCameraInformation(camera, shader); shader.SetUniform("gamma", camera.Gamma); @@ -277,6 +280,8 @@ namespace MxEngine shader.SetUniform("parentModel", unit.ModelMatrix); //-V807 shader.SetUniform("parentNormal", unit.NormalMatrix); shader.SetUniform("parentColor", material.BaseColor); + + shader.SetUniform("receiveSSR", static_cast(material.ReceiveSSR)); this->DrawIndices(RenderPrimitive::TRIANGLES, unit.IndexCount, unit.IndexOffset, unit.VertexOffset, instanceCount, baseInstance); } @@ -322,6 +327,9 @@ namespace MxEngine ssaoShader->IgnoreNonExistingUniform("materialTex"); ssaoShader->IgnoreNonExistingUniform("albedoTex"); ssaoShader->IgnoreNonExistingUniform("camera.position"); + ssaoShader->IgnoreNonExistingUniform("camera.viewMatrix"); + ssaoShader->IgnoreNonExistingUniform("camera.projectionMatrix"); + ssaoShader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); Texture::TextureBindId textureId = 0; this->BindGBuffer(camera, *ssaoShader, textureId); @@ -352,11 +360,41 @@ namespace MxEngine std::swap(input, output); } - void RenderController::GenerateDepthPyramid(TextureHandle& depth) + void RenderController::GenerateDepthPyramid(CameraUnit& camera) { MAKE_RENDER_PASS_SCOPE("RenderController::GenerateDepthPyramid()"); - // generate depth texture mipmaps for post-processing algorithms. Replace later with hierarhical depth map - depth->GenerateMipmaps(); + auto& shader = this->Pipeline.Environment.Shaders["HIZ"_id]; + auto& zBuffer = camera.DepthTexture; + auto& HiZ = camera.HiZ; + + auto fGetTex = [&zBuffer, &HiZ](int layer)->TextureHandle& + { + if (layer == 0) + return zBuffer; + else + return HiZ[layer - 1]; + }; + + int w = zBuffer->GetWidth(); + int h = zBuffer->GetHeight(); + Texture::Copy(zBuffer->GetNativeHandle(), camera.PackedDepth->GetNativeHandle(), 0, 0, w, h); + w = h = 0; + camera.PackedDepthOrigins[0] = { 0.0,0.0 }; + for (int i = 0; i < HiZ.size(); i++) + { + shader->Bind(); + auto& inputTex = fGetTex(i); + inputTex->Bind(0); + auto& outputTex = fGetTex(i + 1); + shader->SetUniform("uPreviousLevelRes", VectorInt2(outputTex->GetWidth(), outputTex->GetHeight())); + this->RenderToTexture(outputTex, shader); + if (i & 1) + w += inputTex->GetWidth(); + else + h += inputTex->GetHeight(); + camera.PackedDepthOrigins[i + 1] = { w,h }; + Texture::Copy(outputTex->GetNativeHandle(), camera.PackedDepth->GetNativeHandle(), w, h, outputTex->GetWidth(), outputTex->GetHeight()); + } } TextureHandle RenderController::ComputeAverageWhite(CameraUnit& camera) @@ -433,6 +471,9 @@ namespace MxEngine shader->IgnoreNonExistingUniform("camera.viewProjMatrix"); shader->IgnoreNonExistingUniform("camera.position"); + shader->IgnoreNonExistingUniform("camera.viewMatrix"); + shader->IgnoreNonExistingUniform("camera.projectionMatrix"); + shader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); shader->IgnoreNonExistingUniform("albedoTex"); shader->IgnoreNonExistingUniform("materialTex"); @@ -518,6 +559,9 @@ namespace MxEngine auto shader = this->Pipeline.Environment.Shaders["IBL"_id]; shader->Bind(); shader->IgnoreNonExistingUniform("camera.viewProjMatrix"); + shader->IgnoreNonExistingUniform("camera.viewMatrix"); + shader->IgnoreNonExistingUniform("camera.projectionMatrix"); + shader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); Texture::TextureBindId textureId = 0; this->BindGBuffer(camera, *shader, textureId); @@ -570,6 +614,9 @@ namespace MxEngine auto godRayShader = this->Pipeline.Environment.Shaders["GodRay"_id]; godRayShader->Bind(); godRayShader->IgnoreNonExistingUniform("camera.viewProjMatrix"); + godRayShader->IgnoreNonExistingUniform("camera.viewMatrix"); + godRayShader->IgnoreNonExistingUniform("camera.projectionMatrix"); + godRayShader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); godRayShader->IgnoreNonExistingUniform("normalTex"); godRayShader->IgnoreNonExistingUniform("albedoTex"); godRayShader->IgnoreNonExistingUniform("materialTex"); @@ -607,6 +654,9 @@ namespace MxEngine auto fogShader = this->Pipeline.Environment.Shaders["Fog"_id]; fogShader->Bind(); fogShader->IgnoreNonExistingUniform("camera.viewProjMatrix"); + fogShader->IgnoreNonExistingUniform("camera.viewMatrix"); + fogShader->IgnoreNonExistingUniform("camera.projectionMatrix"); + fogShader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); fogShader->IgnoreNonExistingUniform("normalTex"); fogShader->IgnoreNonExistingUniform("albedoTex"); fogShader->IgnoreNonExistingUniform("materialTex"); @@ -643,23 +693,33 @@ namespace MxEngine std::swap(input, output); } + void RenderController::ApplySSR(CameraUnit& camera, TextureHandle& input, TextureHandle& temporary, TextureHandle& output) { - if (camera.SSR == nullptr || camera.SSR->GetSteps() == 0) return; - MAKE_RENDER_PASS_SCOPE("RenderController::ApplySSR()"); - - auto& SSRShader = this->Pipeline.Environment.Shaders["SSR"_id]; - SSRShader->Bind(); + if (camera.SSR == nullptr ) return; + + int level = camera.SSR->GetMaxLevel(); +#if defined(MXENGINE_PROFILING_ENABLED) + auto profilerTitle = MxFormat("RenderController::ApplySSR(level={})", level); + MAKE_RENDER_PASS_SCOPE(profilerTitle.c_str()); +#endif + auto& SSRShader = this->Pipeline.Environment.Shaders["SSR"_id]; + SSRShader->Bind(); SSRShader->IgnoreNonExistingUniform("albedoTex"); SSRShader->IgnoreNonExistingUniform("materialTex"); + SSRShader->IgnoreNonExistingUniform("camera.viewProjMatrix"); Texture::TextureBindId textureId = 0; this->BindGBuffer(camera, *SSRShader, textureId); this->BindCameraInformation(camera, *SSRShader); + SSRShader->SetUniform("ssrMask", camera.SSRMaskTexture->GetBoundId()); + camera.SSRMaskTexture->Bind(textureId++); + this->BindHiZ(camera, *SSRShader, textureId); SSRShader->SetUniform("thickness", camera.SSR->GetThickness()); - SSRShader->SetUniform("startDistance", camera.SSR->GetStartDistance()); - SSRShader->SetUniform("steps", (int)camera.SSR->GetSteps()); + SSRShader->SetUniform("screenResolution", Vector2(camera.HDRTexture->GetWidth(), camera.HDRTexture->GetHeight())); + SSRShader->SetUniform("maxLevel", level); + SSRShader->SetUniform("maxStep", camera.SSR->GetMaxStep()); this->RenderToTexture(temporary, SSRShader); @@ -692,6 +752,9 @@ namespace MxEngine SSGIShader->IgnoreNonExistingUniform("normalTex"); SSGIShader->IgnoreNonExistingUniform("materialTex"); SSGIShader->IgnoreNonExistingUniform("camera.position"); + SSGIShader->IgnoreNonExistingUniform("camera.viewMatrix"); + SSGIShader->IgnoreNonExistingUniform("camera.projectionMatrix"); + SSGIShader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); Texture::TextureBindId textureId = 0; this->BindGBuffer(camera, *SSGIShader, textureId); @@ -738,6 +801,9 @@ namespace MxEngine auto cocShader = this->Pipeline.Environment.Shaders["COC"_id]; cocShader->Bind(); + cocShader->IgnoreNonExistingUniform("camera.viewMatrix"); + cocShader->IgnoreNonExistingUniform("camera.projectionMatrix"); + cocShader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); cocShader->IgnoreNonExistingUniform("camera.viewProjMatrix"); cocShader->IgnoreNonExistingUniform("normalTex"); cocShader->IgnoreNonExistingUniform("albedoTex"); @@ -855,6 +921,9 @@ namespace MxEngine auto shader = this->Pipeline.Environment.Shaders["SpotLightShadow"_id]; shader->Bind(); shader->IgnoreNonExistingUniform("camera.position"); + shader->IgnoreNonExistingUniform("camera.viewMatrix"); + shader->IgnoreNonExistingUniform("camera.projectionMatrix"); + shader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); shader->IgnoreNonExistingUniform("albedoTex"); shader->IgnoreNonExistingUniform("materialTex"); @@ -898,6 +967,9 @@ namespace MxEngine auto shader = this->Pipeline.Environment.Shaders["PointLightShadow"_id]; shader->Bind(); shader->IgnoreNonExistingUniform("camera.position"); + shader->IgnoreNonExistingUniform("camera.viewMatrix"); + shader->IgnoreNonExistingUniform("camera.projectionMatrix"); + shader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); shader->IgnoreNonExistingUniform("albedoTex"); shader->IgnoreNonExistingUniform("materialTex"); @@ -938,6 +1010,9 @@ namespace MxEngine auto shader = this->Pipeline.Environment.Shaders["PointLightNonShadow"_id]; shader->Bind(); shader->IgnoreNonExistingUniform("camera.position"); + shader->IgnoreNonExistingUniform("camera.viewMatrix"); + shader->IgnoreNonExistingUniform("camera.projectionMatrix"); + shader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); shader->IgnoreNonExistingUniform("albedoTex"); shader->IgnoreNonExistingUniform("materialTex"); auto viewportSize = MakeVector2((float)camera.OutputTexture->GetWidth(), (float)camera.OutputTexture->GetHeight()); @@ -968,6 +1043,9 @@ namespace MxEngine auto shader = this->Pipeline.Environment.Shaders["SpotLightNonShadow"_id]; shader->Bind(); + shader->IgnoreNonExistingUniform("camera.viewMatrix"); + shader->IgnoreNonExistingUniform("camera.projectionMatrix"); + shader->IgnoreNonExistingUniform("camera.invProjectionMatrix"); shader->IgnoreNonExistingUniform("camera.position"); shader->IgnoreNonExistingUniform("albedoTex"); shader->IgnoreNonExistingUniform("materialTex"); @@ -1029,11 +1107,13 @@ namespace MxEngine shader.SetUniform("camera.position", camera.ViewportPosition); shader.SetUniform("camera.viewProjMatrix", camera.ViewProjectionMatrix); shader.SetUniform("camera.invViewProjMatrix", camera.InverseViewProjMatrix); + shader.SetUniform("camera.viewMatrix", camera.ViewMatrix); + shader.SetUniform("camera.projectionMatrix", camera.ProjectionMatrix); + shader.SetUniform("camera.invProjectionMatrix", Inverse(camera.ProjectionMatrix)); } - void RenderController::BindGBuffer(const CameraUnit& camera, const Shader& shader, Texture::TextureBindId& startId) { - camera.AlbedoTexture->Bind(startId++); + camera.AlbedoTexture->Bind(startId++); camera.NormalTexture->Bind(startId++); camera.MaterialTexture->Bind(startId++); camera.DepthTexture->Bind(startId++); @@ -1044,8 +1124,17 @@ namespace MxEngine shader.SetUniform("depthTex", camera.DepthTexture->GetBoundId()); } - const Renderer& RenderController::GetRenderEngine() const + void RenderController::BindHiZ(const CameraUnit& camera, const Shader& shader, Texture::TextureBindId& startId) { + camera.PackedDepth->Bind(startId++); + shader.SetUniform("depthPyramid", camera.PackedDepth->GetBoundId()); + + for (int i = 0; i < camera.PackedDepthOrigins.size(); i++) + shader.SetUniform(MxFormat("basePositions[{}]", i), camera.PackedDepthOrigins[i]); + } + + const Renderer& RenderController::GetRenderEngine() const + { return this->renderer; } @@ -1443,7 +1532,7 @@ namespace MxEngine } void RenderController::SubmitCamera( - const CameraController& controller, + const CameraController& cameraController, const Transform& parentTransform, const Skybox* skybox, const CameraEffects* effects, @@ -1455,28 +1544,33 @@ namespace MxEngine { auto& camera = this->Pipeline.Cameras.emplace_back(); - bool isPerspective = controller.GetCameraType() == CameraType::PERSPECTIVE; + bool isPerspective = cameraController.GetCameraType() == CameraType::PERSPECTIVE; bool hasSkybox = skybox != nullptr; bool hasToneMapping = toneMapping != nullptr; camera.ViewportPosition = parentTransform.GetPosition(); - camera.AspectRatio = controller.Camera.GetAspectRatio(); - camera.StaticViewProjectionMatrix = controller.GetMatrix(MakeVector3(0.0f)); - camera.ViewProjectionMatrix = controller.GetMatrix(parentTransform.GetPosition()); + camera.AspectRatio = cameraController.Camera.GetAspectRatio(); + camera.StaticViewProjectionMatrix = cameraController.GetMatrix(MakeVector3(0.0f)); + camera.ViewMatrix = cameraController.GetViewMatrix(parentTransform.GetPosition()); + camera.ProjectionMatrix = cameraController.GetProjectionMatrix(); + camera.ViewProjectionMatrix = cameraController.GetMatrix(parentTransform.GetPosition()); camera.InverseViewProjMatrix = Inverse(camera.ViewProjectionMatrix); - camera.Culler = controller.GetFrustrumCuller(); - camera.IsPerspective = controller.GetCameraType() == CameraType::PERSPECTIVE; - camera.GBuffer = controller.GetGBuffer(); - camera.AlbedoTexture = controller.GetAlbedoTexture(); - camera.NormalTexture = controller.GetNormalTexture(); - camera.MaterialTexture = controller.GetMaterialTexture(); - camera.DepthTexture = controller.GetDepthTexture(); - camera.AverageWhiteTexture = controller.GetAverageWhiteTexture(); - camera.HDRTexture = controller.GetHDRTexture(); - camera.SwapTexture1 = controller.GetSwapHDRTexture1(); - camera.SwapTexture2 = controller.GetSwapHDRTexture2(); - camera.OutputTexture = controller.GetRenderTexture(); - camera.RenderToTexture = controller.IsRendering(); + camera.Culler = cameraController.GetFrustrumCuller(); + camera.IsPerspective = cameraController.GetCameraType() == CameraType::PERSPECTIVE; + camera.GBuffer = cameraController.GetGBuffer(); + camera.AlbedoTexture = cameraController.GetAlbedoTexture(); + camera.NormalTexture = cameraController.GetNormalTexture(); + camera.MaterialTexture = cameraController.GetMaterialTexture(); + camera.SSRMaskTexture = cameraController.GetSSRMask(); + camera.DepthTexture = cameraController.GetDepthTexture(); + camera.HiZ = cameraController.GetHiZ(); + camera.PackedDepth = cameraController.GetPackedDepth(); + camera.AverageWhiteTexture = cameraController.GetAverageWhiteTexture(); + camera.HDRTexture = cameraController.GetHDRTexture(); + camera.SwapTexture1 = cameraController.GetSwapHDRTexture1(); + camera.SwapTexture2 = cameraController.GetSwapHDRTexture2(); + camera.OutputTexture = cameraController.GetRenderTexture(); + camera.RenderToTexture = cameraController.IsRendering(); camera.SkyboxTexture = hasSkybox && skybox->CubeMap.IsValid() ? skybox->CubeMap : this->Pipeline.Environment.DefaultSkybox; camera.IrradianceTexture = hasSkybox && skybox->Irradiance.IsValid() ? skybox->Irradiance : camera.SkyboxTexture; camera.SkyboxIntensity = hasSkybox ? skybox->GetIntensity() : Skybox::DefaultIntensity; @@ -1626,7 +1720,7 @@ namespace MxEngine this->DrawObjects(camera, *this->Pipeline.Environment.Shaders["GBuffer"_id], this->Pipeline.OpaqueObjects); this->DrawObjects(camera, *this->Pipeline.Environment.Shaders["GBufferMask"_id], this->Pipeline.MaskedObjects); - this->GenerateDepthPyramid(camera.DepthTexture); + this->GenerateDepthPyramid(camera); this->DrawParticles(camera, this->Pipeline.OpaqueParticleSystems, *this->Pipeline.Environment.Shaders["ParticleOpaque"_id]); diff --git a/src/Core/Rendering/RenderController.h b/src/Core/Rendering/RenderController.h index 2aa40502c..190dd0efd 100644 --- a/src/Core/Rendering/RenderController.h +++ b/src/Core/Rendering/RenderController.h @@ -62,7 +62,7 @@ namespace MxEngine void DrawObject(const RenderUnit& unit, size_t instanceCount, size_t baseInstance, const Shader& shader); void ComputeBloomEffect(CameraUnit& camera, const TextureHandle& output); TextureHandle ComputeAverageWhite(CameraUnit& camera); - void GenerateDepthPyramid(TextureHandle& depth); + void GenerateDepthPyramid(CameraUnit& camera); void PerformPostProcessing(CameraUnit& camera); void PerformLightPass(CameraUnit& camera); void DrawTransparentObjects(CameraUnit& camera); @@ -86,6 +86,7 @@ namespace MxEngine void SubmitInstancedLights(); void SubmitDirectionalLightInformation(ShaderHandle& shader, Texture::TextureBindId textureId); void BindGBuffer(const CameraUnit& camera, const Shader& shader, Texture::TextureBindId& startId); + void BindHiZ(const CameraUnit& camera, const Shader& shader, Texture::TextureBindId& startId); void BindSkyboxInformation(const CameraUnit& camera, const Shader& shader, Texture::TextureBindId& startId); void BindCameraInformation(const CameraUnit& camera, const Shader& shader); void BindFogInformation(const CameraUnit& camera, const Shader& shader); diff --git a/src/Core/Rendering/RenderPipeline.h b/src/Core/Rendering/RenderPipeline.h index 8b7b3c701..918a84a45 100644 --- a/src/Core/Rendering/RenderPipeline.h +++ b/src/Core/Rendering/RenderPipeline.h @@ -62,6 +62,10 @@ namespace MxEngine TextureHandle NormalTexture; TextureHandle MaterialTexture; TextureHandle DepthTexture; + TextureHandle SSRMaskTexture; + MxVector< TextureHandle> HiZ; + TextureHandle PackedDepth; + std::array PackedDepthOrigins; TextureHandle AverageWhiteTexture; TextureHandle HDRTexture; TextureHandle SwapTexture1; @@ -70,6 +74,8 @@ namespace MxEngine FrustrumCuller Culler; Matrix4x4 InverseViewProjMatrix; Matrix4x4 ViewProjectionMatrix; + Matrix4x4 ViewMatrix; + Matrix4x4 ProjectionMatrix; Matrix4x4 StaticViewProjectionMatrix; TextureHandle OutputTexture; diff --git a/src/Core/Resources/Material.cpp b/src/Core/Resources/Material.cpp index 2df5459ab..a7b6ebf94 100644 --- a/src/Core/Resources/Material.cpp +++ b/src/Core/Resources/Material.cpp @@ -57,6 +57,10 @@ namespace MxEngine ( rttr::metadata(MetaInfo::FLAGS, MetaInfo::SERIALIZABLE | MetaInfo::EDITABLE) ) + .property("receive SSR", &Material::ReceiveSSR) + ( + rttr::metadata(MetaInfo::FLAGS, MetaInfo::SERIALIZABLE | MetaInfo::EDITABLE) + ) .property("normal map", &Material::NormalMap) ( rttr::metadata(MetaInfo::FLAGS, MetaInfo::SERIALIZABLE | MetaInfo::EDITABLE) diff --git a/src/Core/Resources/Material.h b/src/Core/Resources/Material.h index 00ae21025..b98735f61 100644 --- a/src/Core/Resources/Material.h +++ b/src/Core/Resources/Material.h @@ -59,6 +59,7 @@ namespace MxEngine float RoughnessFactor = 0.75f; float MetallicFactor = 0.0f; + bool ReceiveSSR = false; Vector3 BaseColor{ 1.0f }; Vector2 UVMultipliers{ 1.0f }; AlphaModeGroup AlphaMode = AlphaModeGroup::OPAQUE; diff --git a/src/Platform/OpenGL/Shaders/Library/common_utils.glsl b/src/Platform/OpenGL/Shaders/Library/common_utils.glsl index abd47b755..fd02a9660 100644 --- a/src/Platform/OpenGL/Shaders/Library/common_utils.glsl +++ b/src/Platform/OpenGL/Shaders/Library/common_utils.glsl @@ -3,6 +3,9 @@ struct Camera vec3 position; mat4 viewProjMatrix; mat4 invViewProjMatrix; + mat4 viewMatrix; + mat4 projectionMatrix; + mat4 invProjectionMatrix; }; vec3 reconstructWorldPosition(float depth, vec2 texcoord, mat4 invViewProjMatrix) { diff --git a/src/Platform/OpenGL/Shaders/gbuffer_fragment.glsl b/src/Platform/OpenGL/Shaders/gbuffer_fragment.glsl index 712e5450e..9497c8013 100644 --- a/src/Platform/OpenGL/Shaders/gbuffer_fragment.glsl +++ b/src/Platform/OpenGL/Shaders/gbuffer_fragment.glsl @@ -13,6 +13,7 @@ in VSout layout(location = 0) out vec4 OutAlbedo; layout(location = 1) out vec4 OutNormal; layout(location = 2) out vec4 OutMaterial; +layout(location = 3) out vec4 OutSSRMask; struct Material { @@ -34,6 +35,7 @@ uniform vec2 uvMultipliers; uniform float displacement; uniform float gamma; uniform Camera camera; +uniform float receiveSSR; vec3 calcNormal(vec2 texcoord, mat3 TBN, sampler2D normalMap) { @@ -68,4 +70,5 @@ void main() OutAlbedo = vec4(fsin.RenderColor * albedo, emmisive / (emmisive + 1.0f)); OutNormal = vec4(0.5f * normal + 0.5f, 1.0f); OutMaterial = vec4(parallaxOcclusion * occlusion, roughness, metallic, 1.0f); + OutSSRMask = vec4(receiveSSR, vec3(1.0)); } \ No newline at end of file diff --git a/src/Platform/OpenGL/Shaders/hierarchical_depth_buffer_generator.glsl b/src/Platform/OpenGL/Shaders/hierarchical_depth_buffer_generator.glsl new file mode 100644 index 000000000..62dbc8065 --- /dev/null +++ b/src/Platform/OpenGL/Shaders/hierarchical_depth_buffer_generator.glsl @@ -0,0 +1,46 @@ +layout(binding = 0) uniform sampler2D depthBuffer; +uniform ivec2 uPreviousLevelRes; + +out vec4 OutColor; + +void main() +{ + ivec2 currentTexCoord = ivec2(gl_FragCoord); + ivec2 lastTexCoord = 2 * currentTexCoord; + + vec4 depth; + depth.x = texelFetch(depthBuffer, lastTexCoord, 0).r; + depth.y = texelFetch(depthBuffer, lastTexCoord + ivec2(1, 0), 0).r; + depth.z = texelFetch(depthBuffer, lastTexCoord + ivec2(1, 1),0).r; + depth.w = texelFetch(depthBuffer, lastTexCoord + ivec2(0, 1), 0).r; + + //Depth is reversed.The closer to the object the higher depth we get. + //So instead of min pooling we perform max pooling. + float maxDepth = max( max(depth.x, depth.y), + max(depth.z, depth.w)); + + bool extraCol = ((uPreviousLevelRes.x & 1) != 0); + bool extraRow = ((uPreviousLevelRes.y & 1) != 0); + if (extraCol) + { + vec2 col; + col.x = texelFetch(depthBuffer, lastTexCoord + ivec2(2, 0), 0).r; + col.y = texelFetch(depthBuffer, lastTexCoord + ivec2(2, 1), 0).r; + + if (extraRow) + { + float corner = texelFetch(depthBuffer, lastTexCoord + ivec2(2, 2), 0).r; + maxDepth = max(maxDepth, corner); + } + maxDepth = max(maxDepth, max(col.x, col.y)); + } + if (extraRow) + { + vec2 row; + row.x = texelFetch(depthBuffer, lastTexCoord + ivec2(0, 2), 0).r; + row.y = texelFetch(depthBuffer, lastTexCoord + ivec2(1, 2), 0).r; + maxDepth = max(maxDepth, max(row.x, row.y)); + } + + OutColor = vec4(maxDepth, 0.0, 0.0, 1.0); +} diff --git a/src/Platform/OpenGL/Shaders/ssr_fragment.glsl b/src/Platform/OpenGL/Shaders/ssr_fragment.glsl index bb354b28b..4d6d41030 100644 --- a/src/Platform/OpenGL/Shaders/ssr_fragment.glsl +++ b/src/Platform/OpenGL/Shaders/ssr_fragment.glsl @@ -7,55 +7,164 @@ uniform sampler2D albedoTex; uniform sampler2D normalTex; uniform sampler2D materialTex; uniform sampler2D depthTex; +uniform sampler2D ssrMask; uniform sampler2D HDRTex; +uniform sampler2D depthPyramid; uniform Camera camera; uniform EnvironmentInfo environment; -uniform int steps; uniform float thickness; -uniform float startDistance; +uniform vec2 screenResolution; +uniform int maxLevel; +uniform int maxStep; -void main() +uniform ivec2 basePositions[5]; +float sampleDepth(ivec2 uv, int level) +{ + ivec2 div = ivec2(1 << level); + ivec2 base = basePositions[level]; + return texelFetch(depthPyramid, base + uv / div, 0).r; +} + +vec3 transform(mat4 m, vec3 p) { - FragmentInfo fragment = getFragmentInfo(TexCoord, albedoTex, normalTex, materialTex, depthTex, camera.invViewProjMatrix); + vec4 temp = m * vec4(p, 1.0); + return temp.xyz; +} +vec4 transform(mat4 m, vec4 p) +{ + return m * p; +} + +void swapComp(inout vec2 v) +{ + v = v.yx; +} + +#define TRACE_RAY(stp) \ + do {\ + scr0 += dScr * stp;\ + vpProj0.z += dVpProj.z * stp;\ + w0 += dw * stp;\ + } while(false); + +void main() +{ + if (texelFetch(ssrMask, ivec2(gl_FragCoord.xy), 0).r < 0.5) + { + OutColor = vec4(0.0); + return; + } + FragmentInfo fragment = getFragmentInfo( + TexCoord, albedoTex, normalTex, materialTex, depthTex, camera.invViewProjMatrix); + float rayLength = 4000.f; + vec3 viewDistance = camera.position - fragment.position; vec3 viewDirection = normalize(viewDistance); + vec3 reflectDirection = normalize(reflect(-viewDirection, fragment.normal)); + + //0:start 1:end + vec3 wp0 = fragment.position; + vec3 wp1 = fragment.position + reflectDirection * rayLength; + + vec3 vp0 = transform(camera.viewMatrix, wp0); + vec3 vp1 = transform(camera.viewMatrix, wp1); + + vec4 ho0 = transform(camera.projectionMatrix, vec4(vp0, 1.0)); + vec4 ho1 = transform(camera.projectionMatrix, vec4(vp1, 1.0)); + + float w0 = 1.0 / ho0.w; + float w1 = 1.0 / ho1.w; + //to avoid nonlinearity,project to view space + vec3 vpProj0 = vp0 * w0; + vec3 vpProj1 = vp1 * w1; - vec3 pivot = normalize(reflect(-viewDirection, fragment.normal)); - vec3 startPos = fragment.position + (pivot * 0.0001); + vec2 ndc0 = ho0.xy * w0; + vec2 ndc1 = ho1.xy * w1; - float currentLength = min(length(viewDistance) / steps, startDistance); - float bestDepth = 10000.0; - vec2 bestUV = vec2(0.0); + vec2 scr0 = (ndc0 + 1.0) / 2.0 * screenResolution; + vec2 scr1 = (ndc1 + 1.0) / 2.0 * screenResolution; - for (int i = 0; i < steps; i++) + scr1 += distance(scr0, scr1) < 0.01 ? 0.01 : 0.0; + //Using Line Generation Algorithm + //https://en.wikipedia.org/wiki/Digital_differential_analyzer_(graphics_algorithm) + vec2 delta = scr1 - scr0; + bool permute = false; + if (abs(delta.x) < abs(delta.y)) { - vec3 currentPosition = startPos + pivot * currentLength; - vec4 projectedPosition = worldToFragSpace(currentPosition, camera.viewProjMatrix); - vec2 currentUV = projectedPosition.xy; - float projectedDepth = projectedPosition.z; + permute = true; + swapComp(delta); + swapComp(scr0); + swapComp(scr1); + } + float stepDir = sign(delta.x); + float invDx = stepDir / delta.x; + + vec3 dVpProj = (vpProj1 - vpProj0) * invDx; + float dw = (w1 - w0) * invDx; + vec2 dScr = vec2(stepDir, delta.y * invDx); - if (currentUV.x > 1.0 || currentUV.y > 1.0 || - currentUV.x < 0.0 || currentUV.y < 0.0) break; + //Start position + scr0 += dScr; + vpProj0 += dVpProj; + w0 += dw; - float currentFragDepth = texture(depthTex, currentUV).r; - float depthDiff = abs(1.0 / projectedDepth - 1.0 / currentFragDepth); - if (depthDiff < bestDepth) + float lastZmax = vp0.z; + vec2 result; + bool isHit = false; + bool checkthickness = false; + int level = 0; + //Todo: /(ㄒoㄒ)/~~ Handle diffuse lobe. Sampling more rays or using cone tracing when the BRDF lobe is fat. + for (int curStep = 0; curStep < maxStep; curStep++) + { + //reconstruct camera space ray depth + result.xy = permute ? scr0.yx : scr0; + vec2 depths; + depths.x = lastZmax; + depths.y = (dVpProj.z * 0.5 + vpProj0.z) / (dw * 0.5 + w0); + lastZmax = depths.y; + //check direction + if (depths.x < depths.y) + swapComp(depths); + //check boundary + if (result.x > screenResolution.x || result.x < 0 || + result.y > screenResolution.y || result.y < 0) + break; + //reconstruct in cam space + float d = sampleDepth(ivec2(result), level); + vec4 normPosition = vec4(2.0f * result - vec2(1.0f), d, 1.0f); + vec4 vpos = transform(camera.invProjectionMatrix, normPosition); + vpos /= vpos.w; + float sceneDepth = vpos.z; + //check if near the start point + if (!checkthickness && (depths.y - sceneDepth > thickness)) + checkthickness = true; + //check hit & switch depth layer + float curDiff = sceneDepth - depths.y; + int stp = 1 << level; + if (checkthickness && curDiff >= thickness) { - bestUV = currentUV; - bestDepth = depthDiff; - if (depthDiff < thickness) + if (level == 0) + { + isHit = true; break; + } + //stepping back + TRACE_RAY(-stp); + level = 0; } else { - vec3 newPosition = reconstructWorldPosition(currentFragDepth, currentUV, camera.invViewProjMatrix); - currentLength = length(startPos - newPosition); + level = min(maxLevel, level + 1); + //stepping forward + TRACE_RAY(stp); } } - - vec3 reflection = bestUV != vec2(0.0) ? texture(HDRTex, bestUV).rgb : vec3(0.0); - OutColor = vec4(reflection, 1.0); + + if (isHit) + OutColor = vec4(texelFetch(HDRTex, ivec2(result), 0).rgb, 1.0); + else + OutColor = vec4(0.0); } \ No newline at end of file diff --git a/src/Platform/OpenGL/Texture.cpp b/src/Platform/OpenGL/Texture.cpp index 8bed20146..82ac81c87 100644 --- a/src/Platform/OpenGL/Texture.cpp +++ b/src/Platform/OpenGL/Texture.cpp @@ -43,7 +43,7 @@ namespace MxEngine GL_RG8, GL_RG16, GL_R16F, - GL_R32F, + GL_R32F_EXT,//cannot use GL_R32F. Why? GL_RG16F, GL_RG32F, GL_RGB, @@ -205,7 +205,7 @@ namespace MxEngine switch (channels) { case 1: - dataChannels = GL_RED; + dataChannels = (format == TextureFormat::R32F) ? GL_RED_EXT : GL_RED; break; case 2: dataChannels = GL_RG; @@ -240,6 +240,12 @@ namespace MxEngine this->Load(image.GetRawData(), (int)image.GetWidth(), (int)image.GetHeight(), (int)image.GetChannelCount(), image.IsFloatingPoint(), format); } + void Texture::Copy(TextureBindId src, TextureBindId dst, int x, int y, int w, int h) + { + GLCALL(glCopyImageSubData(src, GL_TEXTURE_2D, 0, 0, 0, 0, + dst, GL_TEXTURE_2D, 0, x, y, 0, w, h, 1)); + } + void Texture::LoadDepth(int width, int height, TextureFormat format) { this->filepath = MXENGINE_MAKE_INTERNAL_TAG("depth"); diff --git a/src/Platform/OpenGL/Texture.h b/src/Platform/OpenGL/Texture.h index 0602fe4ce..b9e03ff06 100644 --- a/src/Platform/OpenGL/Texture.h +++ b/src/Platform/OpenGL/Texture.h @@ -110,6 +110,7 @@ namespace MxEngine void Load(RawDataPointer data, int width, int height, int channels, bool isFloating, TextureFormat format = TextureFormat::RGB); void Load(const Image& image, TextureFormat format = TextureFormat::RGB); + static void Copy(TextureBindId src, TextureBindId dst, int x, int y, int w, int h); void LoadDepth(int width, int height, TextureFormat format = TextureFormat::DEPTH); void SetMaxLOD(size_t lod); void SetMinLOD(size_t lod);