From 3c64abd77e92cc7e82f73272b62ac2b02b8b2e36 Mon Sep 17 00:00:00 2001 From: Atdunbg Date: Mon, 6 Apr 2026 19:06:04 +0800 Subject: [PATCH] add simple fog --- Editor/Editor/EditorLayer.cpp | 2 +- Editor/assets/shaders/PBRShader_Anim.glsl | 5 +- Editor/assets/shaders/PBRShader_Static.glsl | 6 +- Editor/assets/shaders/SceneComposite.glsl | 65 ++++++++++++++++++- .../src/Prism/Editor/DefaultAssetEditors.cpp | 1 + Prism/src/Prism/Editor/DefaultAssetEditors.h | 2 + .../Prism/Platform/OpenGL/OpenGLShader.cpp | 11 ++++ .../src/Prism/Platform/OpenGL/OpenGLShader.h | 1 + Prism/src/Prism/Renderer/Camera.cpp | 16 +++++ Prism/src/Prism/Renderer/Camera.h | 3 + Prism/src/Prism/Renderer/Renderer.cpp | 10 +++ Prism/src/Prism/Renderer/Renderer.h | 1 + Prism/src/Prism/Renderer/Renderer3D.cpp | 60 +++++++++++++++-- Prism/src/Prism/Renderer/SceneRenderer.cpp | 2 - Prism/src/Prism/Renderer/Shader.h | 1 + Prism/src/Prism/Scene/Scene.cpp | 3 + 16 files changed, 171 insertions(+), 18 deletions(-) diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index 65de028..2dd0072 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -664,7 +664,7 @@ namespace Prism { SceneRenderer::SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); m_EditorScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); + m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 1000.0f)); m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); diff --git a/Editor/assets/shaders/PBRShader_Anim.glsl b/Editor/assets/shaders/PBRShader_Anim.glsl index 059aa80..72845fa 100644 --- a/Editor/assets/shaders/PBRShader_Anim.glsl +++ b/Editor/assets/shaders/PBRShader_Anim.glsl @@ -75,6 +75,9 @@ const float Epsilon = 0.00001; const int LightCount = 1; const vec3 Fdielectric = vec3(0.04); +layout(location = 0) out vec4 color; +layout(location = 1) out vec4 o_BloomColor; + struct DirectionalLight { vec3 Direction; vec3 Radiance; @@ -113,8 +116,6 @@ in VertexOutput vec4 FragPosLightSpace; } vs_Input; -layout(location = 0) out vec4 color; -layout(location = 1) out vec4 o_BloomColor; uniform DirectionalLight u_DirectionalLights; uniform vec3 u_CameraPosition; diff --git a/Editor/assets/shaders/PBRShader_Static.glsl b/Editor/assets/shaders/PBRShader_Static.glsl index 23a48b6..303a2d1 100644 --- a/Editor/assets/shaders/PBRShader_Static.glsl +++ b/Editor/assets/shaders/PBRShader_Static.glsl @@ -61,6 +61,9 @@ const float Epsilon = 0.00001; const int LightCount = 1; const vec3 Fdielectric = vec3(0.04); +layout(location = 0) out vec4 color; +layout(location = 1) out vec4 o_BloomColor; + struct DirectionalLight { vec3 Direction; vec3 Radiance; @@ -100,9 +103,6 @@ in VertexOutput vec4 FragPosLightSpace; } vs_Input; -layout(location = 0) out vec4 color; -layout(location = 1) out vec4 o_BloomColor; - uniform DirectionalLight u_DirectionalLights; uniform int u_PointLightCount; diff --git a/Editor/assets/shaders/SceneComposite.glsl b/Editor/assets/shaders/SceneComposite.glsl index b004448..68caf74 100644 --- a/Editor/assets/shaders/SceneComposite.glsl +++ b/Editor/assets/shaders/SceneComposite.glsl @@ -22,8 +22,20 @@ layout(location = 1) out vec4 o_BloomTexture; in vec2 v_TexCoord; uniform sampler2DMS u_Texture; +uniform sampler2DMS u_DepthTexture; uniform sampler2D u_BloomTexture; +uniform mat4 u_InvProjection; +uniform mat4 u_InvView; + +// Fog +uniform float u_FogEnabled; +uniform vec3 u_FogColor; +uniform float u_FogDensity; // 雾密度,典型值 0.01~0.05 +uniform float u_FogHeight; // 雾的起始高度(世界 Y 坐标),低于此高度雾最浓 +uniform float u_FogHeightFalloff; // 高度衰减系数,值越大高度影响越剧烈(典型 0.5~2.0) + + uniform bool u_EnableAutoExposure; uniform float u_ManualExposure; @@ -62,7 +74,7 @@ float MultiSampleDepth(sampler2DMS tex, vec2 tc) ivec2 texCoord = ivec2(tc * texSize); float result = 0.0; for (int i = 0; i < u_TextureSamples; i++) - result += texelFetch(tex, texCoord, i).r; + result += texelFetch(tex, texCoord, i).r; result /= float(u_TextureSamples); return result; } @@ -82,10 +94,14 @@ void main() color += bloomColor; } + + float exposure = 1.0f; if(u_EnableAutoExposure) - color *= u_Exposure; + exposure = u_Exposure; else - color *= u_ManualExposure; + exposure = u_ManualExposure; + color *= exposure; + // Reinhard tonemapping operator. // see: "Photographic Tone Reproduction for Digital Images", eq. 4 @@ -95,6 +111,49 @@ void main() // Scale color by ratio of average luminances. vec3 mappedColor = (mappedLuminance / luminance) * color; + if (u_FogEnabled > 0.5) + { + // 1. 获取深度并重建视空间坐标 + float depth = MultiSampleDepth(u_DepthTexture, v_TexCoord); + vec4 clipPos = vec4(v_TexCoord * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); + vec4 viewPos4 = u_InvProjection * clipPos; + viewPos4 /= viewPos4.w; + vec3 viewPos = viewPos4.xyz; // 视空间坐标(相机为原点,Z 轴向内) + + // 2. 距离雾因子(指数雾) + float distance = length(viewPos); + float distanceFactor = 1.0 - exp(-distance * u_FogDensity); + // 可选:指数平方雾(更平滑) + // float distanceFactor = 1.0 - exp(-pow(distance * u_FogDensity, 2.0)); + + // 3. 高度雾因子(基于世界 Y) + // 将视空间坐标转换到世界空间 + vec4 worldPos4 = u_InvView* vec4(viewPos, 1.0); + vec3 worldPos = worldPos4.xyz / worldPos4.w; + float worldY = worldPos.y; + + float heightFactor = 0.0; + if (worldY < u_FogHeight) + { + // 低于起始高度:雾最浓,因子为 1 + heightFactor = 1.0; + } + else + { + // 高于起始高度:指数衰减,高度越高雾越淡 + float deltaY = worldY - u_FogHeight; + heightFactor = exp(-deltaY * u_FogHeightFalloff); + } + // 可选:使用 clamp 限制范围 + heightFactor = clamp(heightFactor, 0.0, 1.0); + + // 4. 综合雾因子(可相乘,也可取最大值,通常相乘) + float fogFactor = distanceFactor * heightFactor; + + // 5. 混合雾颜色(雾颜色直接使用 u_FogColor,已在 LDR 空间) + mappedColor = mix(mappedColor, u_FogColor, fogFactor); + } + // Gamma correction. o_Color = vec4(pow(mappedColor, vec3(1.0 / gamma)), 1.0); diff --git a/Prism/src/Prism/Editor/DefaultAssetEditors.cpp b/Prism/src/Prism/Editor/DefaultAssetEditors.cpp index 219b876..3f65755 100644 --- a/Prism/src/Prism/Editor/DefaultAssetEditors.cpp +++ b/Prism/src/Prism/Editor/DefaultAssetEditors.cpp @@ -5,6 +5,7 @@ #include "DefaultAssetEditors.h" #include "Prism/Asset/AssetSerializer.h" +#include "Prism/Core/Log.h" namespace Prism { diff --git a/Prism/src/Prism/Editor/DefaultAssetEditors.h b/Prism/src/Prism/Editor/DefaultAssetEditors.h index 587c5b9..c7c4825 100644 --- a/Prism/src/Prism/Editor/DefaultAssetEditors.h +++ b/Prism/src/Prism/Editor/DefaultAssetEditors.h @@ -10,6 +10,8 @@ namespace Prism { + class RenderPass; + class PhysicsMaterialEditor : public AssetEditor { public: diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp index 8bdbc18..5f4bbde 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp @@ -148,6 +148,17 @@ namespace Prism }); } + void OpenGLShader::SetFloatArray(const std::string& name, float* arr, int count) + { + Renderer::Submit([=]() + { + for (int i = 0; i < count; i++) + { + UploadUniformFloat(name, arr[i]); + } + }); + } + void OpenGLShader::SetInt(const std::string& name, int value) { Renderer::Submit([=]() { diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h index bedf04f..0d5c847 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h @@ -31,6 +31,7 @@ namespace Prism virtual void SetFloat(const std::string& name, float value) override; virtual void SetFloat2(const std::string& name, const glm::vec2& value) override; virtual void SetFloat3(const std::string& name, const glm::vec3& value) override; + virtual void SetFloatArray(const std::string& name, float* arr, int count) override; virtual void SetInt(const std::string& name, int value) override; virtual void SetUInt(const std::string& name, unsigned int value) override; virtual void SetMat4(const std::string& name, const glm::mat4& value) override; diff --git a/Prism/src/Prism/Renderer/Camera.cpp b/Prism/src/Prism/Renderer/Camera.cpp index fb562ee..2bde26e 100644 --- a/Prism/src/Prism/Renderer/Camera.cpp +++ b/Prism/src/Prism/Renderer/Camera.cpp @@ -13,4 +13,20 @@ namespace Prism { } + + float Camera::GetNear() const + { + if (m_ProjectionMatrix[3][3] == 1.0f) // 正交投影 + return (m_ProjectionMatrix[3][2] - 1.0f) / m_ProjectionMatrix[2][2]; + else // 透视投影 + return m_ProjectionMatrix[3][2] / (m_ProjectionMatrix[2][2] - 1.0f); + } + + float Camera::GetFar() const + { + if (m_ProjectionMatrix[3][3] == 1.0f) // 正交 + return (m_ProjectionMatrix[3][2] + 1.0f) / m_ProjectionMatrix[2][2]; + else // 透视 + return m_ProjectionMatrix[3][2] / (m_ProjectionMatrix[2][2] + 1.0f); + } } diff --git a/Prism/src/Prism/Renderer/Camera.h b/Prism/src/Prism/Renderer/Camera.h index dd80ab9..9552018 100644 --- a/Prism/src/Prism/Renderer/Camera.h +++ b/Prism/src/Prism/Renderer/Camera.h @@ -24,6 +24,9 @@ namespace Prism const glm::mat4& GetProjectionMatrix() const { return m_ProjectionMatrix; } + float GetNear() const; + float GetFar() const; + void SetProjectionMatrix(const glm::mat4& projectionMatrix) { m_ProjectionMatrix = projectionMatrix; } float GetExposure() const { return m_Exposure; } diff --git a/Prism/src/Prism/Renderer/Renderer.cpp b/Prism/src/Prism/Renderer/Renderer.cpp index b4e9627..334c23a 100644 --- a/Prism/src/Prism/Renderer/Renderer.cpp +++ b/Prism/src/Prism/Renderer/Renderer.cpp @@ -201,6 +201,16 @@ namespace Prism DrawIndexed(6, PrimitiveType::Triangles, depthTest, cullFace); } + void Renderer::SubmitFullscreenQuad(const bool depthTest, const bool cullFace) + { + + s_Data.m_FullscreenQuadVertexBuffer->Bind(); + s_Data.m_FullscreenQuadPipeline->Bind(); + s_Data.m_FullscreenQuadIndexBuffer->Bind(); + + DrawIndexed(6, PrimitiveType::Triangles, depthTest, cullFace); + } + void Renderer::SubmitMesh(Ref& mesh, const glm::mat4& transform, const std::vector>& overrideMaterials) { // auto material = overrideMaterial ? overrideMaterial : mesh->GetMaterialInstance(); diff --git a/Prism/src/Prism/Renderer/Renderer.h b/Prism/src/Prism/Renderer/Renderer.h index 9e0d766..a7b6d5b 100644 --- a/Prism/src/Prism/Renderer/Renderer.h +++ b/Prism/src/Prism/Renderer/Renderer.h @@ -61,6 +61,7 @@ namespace Prism static void SubmitQuad(Ref& material, const glm::mat4& transform = glm::mat4(1.0f)); static void SubmitFullscreenQuad(Ref material); + static void SubmitFullscreenQuad(bool depthTest = true, bool cullFace = true); static void SubmitMesh(Ref& mesh, const glm::mat4& transform, const std::vector>& overrideMaterials = {}); static void SubmitMesh(Ref& mesh, const glm::mat4& transform, const Ref& overrideMaterial = nullptr); static void SubmitMeshWithShader(Ref mesh, const glm::mat4& transform, Ref shader); diff --git a/Prism/src/Prism/Renderer/Renderer3D.cpp b/Prism/src/Prism/Renderer/Renderer3D.cpp index f6d3e0a..492278b 100644 --- a/Prism/src/Prism/Renderer/Renderer3D.cpp +++ b/Prism/src/Prism/Renderer/Renderer3D.cpp @@ -11,7 +11,6 @@ #include "Renderer2D.h" #include "SceneRenderer.h" #include "StorageBuffer.h" -#include "glad/glad.h" #include "Prism/Scene/Components.h" #include "RHI/RHICommandBuffer.h" #include "RHI/RHIDevice.h" @@ -31,6 +30,15 @@ namespace Prism Ref SceneEnvironment; } SceneData; + struct FogData + { + bool FogEnabled = false; + glm::vec3 FogColor{1.0f}; + float FogDensity = 0.01f; + float FogHeight = 0.0f; + float FogHeightFalloff = 0.01f; + }FogData; + Ref BRDFLUT; Ref CompositeShader; Ref BloomBlurShader; @@ -815,10 +823,10 @@ namespace Prism auto reg = rd->GetRegister(); auto tex = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); - Renderer::Submit([reg, tex]() mutable + Renderer::Submit([reg, tex, cmd]() mutable { - glBindTextureUnit(reg, tex); - glBindSampler(reg, s_Data.ShadowMapSampler); + cmd->BindTextureUnit(reg, tex); + cmd->BindSampler(reg, s_Data.ShadowMapSampler); }); } @@ -1039,17 +1047,39 @@ namespace Prism s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); s_Data.CompositeShader->SetBool("u_EnableBloom", s_Data.EnableBloom); - // 绑定几何阶段颜色纹理(多重采样) + + // Fog + s_Data.CompositeShader->SetFloat("u_FogEnabled", s_Data.FogData.FogEnabled ? 1.0f : 0.0f); + if (s_Data.FogData.FogEnabled) + { + s_Data.CompositeShader->SetMat4("u_InvProjection", glm::inverse(s_Data.SceneData.SceneCamera.Camera.GetProjectionMatrix())); + s_Data.CompositeShader->SetMat4("u_InvView", glm::inverse(s_Data.SceneData.SceneCamera.ViewMatrix)); + s_Data.CompositeShader->SetFloat3("u_FogColor", s_Data.FogData.FogColor); + s_Data.CompositeShader->SetFloat("u_FogDensity", s_Data.FogData.FogDensity); + s_Data.CompositeShader->SetFloat("u_FogHeight", s_Data.FogData.FogHeight); + s_Data.CompositeShader->SetFloat("u_FogHeightFalloff", s_Data.FogData.FogHeightFalloff); + } + s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindDepthTexture(1); if (s_Data.EnableBloom) { s_Data.BloomBlendPass->GetSpecification().TargetFramebuffer->BindTexture(0, 2); - s_Data.CompositeShader->SetInt("u_BloomTexture", 2); // 告诉着色器使用单元2 + s_Data.CompositeShader->SetInt("u_BloomTexture", 2); } - Renderer::SubmitFullscreenQuad(nullptr); + Renderer::SubmitFullscreenQuad(false); + + auto depthFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer; + auto outFB = outRenderPass->GetSpecification().TargetFramebuffer; + Renderer::Submit([cmd, depthFB, outFB]() + { + cmd->BindFramebuffer(FramebufferTarget::Read, depthFB->GetDepthAttachmentRendererID()); + cmd->BindFramebuffer(FramebufferTarget::Draw, outFB->GetDepthAttachmentRendererID()); + cmd->BlitFramebuffer(0, 0, depthFB->GetWidth(), depthFB->GetHeight(), + 0, 0, outFB->GetWidth(), outFB->GetHeight(), BlitMask::Depth, BlitFilter::Nearest); + }); Renderer::EndRenderPass(); } @@ -1110,6 +1140,22 @@ namespace Prism UI::Property("Shadow Pass time", s_Stats.ShadowPass); UI::Property("AutoExposure Pass time", s_Stats.AutoExposurePass); + if (UI::BeginTreeNode("FogConfig", false)) + { + UI::BeginPropertyGrid(); + UI::Property("Enabled", s_Data.FogData.FogEnabled); + if (s_Data.FogData.FogEnabled) + { + UI::PropertyColor("Fog Color", s_Data.FogData.FogColor); + UI::Property("Fog Density", s_Data.FogData.FogDensity, 0.001f, 0.001f, 0.1f); + UI::Property("Fog Height", s_Data.FogData.FogHeight, 0.001f, -10.0f, 10.0f); + UI::Property("Fog HeightFalloff", s_Data.FogData.FogHeightFalloff, 0.001f, 0.001f, 0.5f); + } + UI::EndPropertyGrid(); + + UI::EndTreeNode(); + } + if (UI::BeginTreeNode("Grid Config", false)) { // Grid plane: 0 = XZ (Y up), 1 = XY (Z forward), 2 = YZ (X right) diff --git a/Prism/src/Prism/Renderer/SceneRenderer.cpp b/Prism/src/Prism/Renderer/SceneRenderer.cpp index 596baef..8dbddb9 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.cpp +++ b/Prism/src/Prism/Renderer/SceneRenderer.cpp @@ -215,8 +215,6 @@ namespace Prism const auto fbWidth = fb->GetWidth(); const auto fbHeight = fb->GetHeight(); - - Renderer::Submit([fb, fbWidth, fbHeight]() { glBindFramebuffer(GL_READ_FRAMEBUFFER, fb->GetColorAttachmentRendererID()); diff --git a/Prism/src/Prism/Renderer/Shader.h b/Prism/src/Prism/Renderer/Shader.h index 86e5adf..619d526 100644 --- a/Prism/src/Prism/Renderer/Shader.h +++ b/Prism/src/Prism/Renderer/Shader.h @@ -116,6 +116,7 @@ namespace Prism virtual void SetFloat(const std::string& name, float value) = 0; virtual void SetFloat2(const std::string& name, const glm::vec2& value) = 0; virtual void SetFloat3(const std::string& name, const glm::vec3& value) = 0; + virtual void SetFloatArray(const std::string& name, float* arr, int count) = 0; virtual void SetMat4(const std::string& name, const glm::mat4& value) = 0; virtual void SetMat4FromRenderThread(const std::string& name, const glm::mat4& value, bool bind = true) = 0; diff --git a/Prism/src/Prism/Scene/Scene.cpp b/Prism/src/Prism/Scene/Scene.cpp index 65c0a5e..1c6282c 100644 --- a/Prism/src/Prism/Scene/Scene.cpp +++ b/Prism/src/Prism/Scene/Scene.cpp @@ -239,7 +239,10 @@ namespace Prism ///////////////////////////////////////////////////////////////////// } + +#ifdef PRISM_RUNTIME SceneRenderer::FlushToScreen(); +#endif SceneRenderer::EndScene(); }