From 5cb9b04ab0b2e55ffee184b6c144eda0b372612e Mon Sep 17 00:00:00 2001 From: Atdunbg Date: Thu, 12 Mar 2026 23:42:52 +0800 Subject: [PATCH] add compute shader to auto calculate screen exposure, add SSBO impl --- Editor/assets/shaders/Exposure.glsl | 63 +++ Editor/assets/shaders/Histogram.glsl | 26 ++ Editor/assets/shaders/SceneComposite.glsl | 14 +- Prism/src/Prism/Asset/Asset.h | 2 +- Prism/src/Prism/Core/Application.cpp | 5 +- Prism/src/Prism/Core/Timer.h | 2 + .../Platform/OpenGL/OpenGLRendererAPI.cpp | 11 + .../Platform/OpenGL/OpenGLStorageBuffer.cpp | 73 ++++ .../Platform/OpenGL/OpenGLStorageBuffer.h | 35 ++ Prism/src/Prism/Renderer/Renderer.cpp | 16 + Prism/src/Prism/Renderer/Renderer.h | 3 + Prism/src/Prism/Renderer/Renderer3D.cpp | 368 +++++++++--------- Prism/src/Prism/Renderer/RendererAPI.h | 3 + Prism/src/Prism/Renderer/StorageBuffer.cpp | 25 ++ Prism/src/Prism/Renderer/StorageBuffer.h | 42 ++ README.md | 22 +- 16 files changed, 511 insertions(+), 199 deletions(-) create mode 100644 Editor/assets/shaders/Exposure.glsl create mode 100644 Editor/assets/shaders/Histogram.glsl create mode 100644 Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.cpp create mode 100644 Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.h create mode 100644 Prism/src/Prism/Renderer/StorageBuffer.cpp create mode 100644 Prism/src/Prism/Renderer/StorageBuffer.h diff --git a/Editor/assets/shaders/Exposure.glsl b/Editor/assets/shaders/Exposure.glsl new file mode 100644 index 0000000..60b1e6e --- /dev/null +++ b/Editor/assets/shaders/Exposure.glsl @@ -0,0 +1,63 @@ +#type compute +#version 460 core +layout(local_size_x = 1, local_size_y = 1) in; + +layout(binding = 0, std430) buffer Histogram { + uint bins[64]; +}; +layout(binding = 1, std430) buffer Exposure { + float exposure; +}; + +uniform float u_SpeedUp; +uniform float u_SpeedDown; +uniform float u_Key; +uniform float u_LowPercent; +uniform float u_HighPercent; +uniform float u_MinExposure; +uniform float u_MaxExposure; +uniform float u_DeltaTime; +uniform float u_LogMin; +uniform float u_LogMax; + +void main() { + float currentExposure = exposure; + + uint total = 0; + uint prefix[64]; + for (int i = 0; i < 64; i++) { + total += bins[i]; + prefix[i] = total; + } + + float lowCount = u_LowPercent * 0.01 * total; + float highCount = u_HighPercent * 0.01 * total; + int lowBin = 0, highBin = 63; + for (int i = 0; i < 64; i++) { + if (prefix[i] < lowCount) lowBin = i + 1; + if (prefix[i] < highCount) highBin = i + 1; + } + lowBin = clamp(lowBin, 0, 63); + highBin = clamp(highBin, 0, 63); + + float sumLum = 0.0; + uint count = 0; + for (int i = lowBin; i <= highBin; i++) { + float t = (float(i) + 0.5) / 64.0; + float logLum = u_LogMin + t * (u_LogMax - u_LogMin); + float lum = exp2(logLum); + sumLum += lum * float(bins[i]); + count += bins[i]; + } + float avgLum = count > 0 ? sumLum / count : 0.18; + + float targetExposure = u_Key / max(avgLum, 0.0001); + targetExposure = clamp(targetExposure, u_MinExposure, u_MaxExposure); + + float speed = (targetExposure > currentExposure) ? u_SpeedUp : u_SpeedDown; + float adaptFactor = 1.0 - exp(-speed * u_DeltaTime); + float newExposure = mix(currentExposure, targetExposure, adaptFactor); + newExposure = clamp(newExposure, u_MinExposure, u_MaxExposure); + + exposure = newExposure; +} diff --git a/Editor/assets/shaders/Histogram.glsl b/Editor/assets/shaders/Histogram.glsl new file mode 100644 index 0000000..ba373a9 --- /dev/null +++ b/Editor/assets/shaders/Histogram.glsl @@ -0,0 +1,26 @@ +#type compute +#version 460 core +layout(local_size_x = 16, local_size_y = 16) in; + +layout(binding = 0) uniform sampler2D u_SceneColor; +layout(binding = 1, std430) buffer Histogram { + uint bins[64]; +}; + +uniform float u_LogMin; +uniform float u_LogMax; + +void main() { + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = textureSize(u_SceneColor, 0); + if (texel.x >= size.x || texel.y >= size.y) return; + + vec3 color = texelFetch(u_SceneColor, texel, 0).rgb; + float lum = max(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0.0001); + float logLum = log2(lum); + + float invLogRange = 1.0 / (u_LogMax - u_LogMin); + float t = (logLum - u_LogMin) * invLogRange; + int bin = int(clamp(t * 64.0, 0.0, 63.0)); + atomicAdd(bins[bin], 1u); +} \ No newline at end of file diff --git a/Editor/assets/shaders/SceneComposite.glsl b/Editor/assets/shaders/SceneComposite.glsl index 4fdbbd8..0609dfb 100644 --- a/Editor/assets/shaders/SceneComposite.glsl +++ b/Editor/assets/shaders/SceneComposite.glsl @@ -23,7 +23,14 @@ in vec2 v_TexCoord; uniform sampler2DMS u_Texture; -uniform float u_Exposure; + +uniform bool u_EnableAutoExposure; +uniform float u_ManualExposure; +layout(std430, binding = 2) buffer Exposure +{ + float u_Exposure; +}; + uniform int u_TextureSamples; uniform bool u_EnableBloom; @@ -64,7 +71,10 @@ void main() color += bloomColor; } - color *= u_Exposure; + if(u_EnableAutoExposure) + color *= u_Exposure; + else + color *= u_ManualExposure; // Reinhard tonemapping operator. // see: "Photographic Tone Reproduction for Digital Images", eq. 4 diff --git a/Prism/src/Prism/Asset/Asset.h b/Prism/src/Prism/Asset/Asset.h index fe164a7..fb25a71 100644 --- a/Prism/src/Prism/Asset/Asset.h +++ b/Prism/src/Prism/Asset/Asset.h @@ -44,7 +44,7 @@ namespace Prism PhysicsMaterial() = default; - PhysicsMaterial(float staticFriction, float dynamicFriction, float bounciness) + PhysicsMaterial(const float staticFriction, const float dynamicFriction, const float bounciness) : StaticFriction(staticFriction), DynamicFriction(dynamicFriction), Bounciness(bounciness) { Type = AssetType::PhysicsMaterial; diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index 990fc0a..e9c3b57 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -41,11 +41,10 @@ namespace Prism ScriptEngine::Init("assets/scripts/ExampleApp.dll"); Physics3D::Init(); - Renderer::Init(); - Renderer::WaitAndRender(); - AssetTypes::Init(); AssetsManager::Init(); + Renderer::Init(); + Renderer::WaitAndRender(); } Application::~Application() diff --git a/Prism/src/Prism/Core/Timer.h b/Prism/src/Prism/Core/Timer.h index 6ea2c5d..488c6ca 100644 --- a/Prism/src/Prism/Core/Timer.h +++ b/Prism/src/Prism/Core/Timer.h @@ -27,6 +27,8 @@ namespace Prism return Elapsed() * 1000.0f; } + operator float() const { return Elapsed(); } + private: std::chrono::time_point m_Start; }; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLRendererAPI.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLRendererAPI.cpp index 06ba789..0191703 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLRendererAPI.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLRendererAPI.cpp @@ -188,4 +188,15 @@ namespace Prism { glLineWidth(thickness); } + + void RendererAPI::DispatchCompute(int x, int y, int z) + { + glDispatchCompute(x, y, z); + } + + void RendererAPI::MemoryBarrier(const int barrier) + { + glMemoryBarrier(barrier); + } + } diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.cpp new file mode 100644 index 0000000..a1e78df --- /dev/null +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.cpp @@ -0,0 +1,73 @@ +// +// Created by Atdunbg on 2026/3/12. +// + +#include "OpenGLStorageBuffer.h" + +#include "Prism/Renderer/Renderer.h" +#include + +namespace Prism { + + static GLenum UsageToGL(const StorageBufferUsage usage) + { + switch (usage) + { + case StorageBufferUsage::Static: return GL_STATIC_DRAW; + case StorageBufferUsage::Dynamic: return GL_DYNAMIC_DRAW; + case StorageBufferUsage::Stream: return GL_STREAM_DRAW; + case StorageBufferUsage::DynamicCopy: return GL_DYNAMIC_COPY; + default: return GL_DYNAMIC_DRAW; + } + } + + OpenGLStorageBuffer::OpenGLStorageBuffer(uint32_t size, const void* data, StorageBufferUsage usage) + : m_Size(size), m_Usage(usage) + { + Renderer::Submit([this, data]() { + glCreateBuffers(1, &m_RendererID); + glNamedBufferData(m_RendererID, m_Size, data, UsageToGL(m_Usage)); + }); + } + + OpenGLStorageBuffer::~OpenGLStorageBuffer() + { + Renderer::Submit([this]() { + glDeleteBuffers(1, &m_RendererID); + }); + } + + void OpenGLStorageBuffer::Bind() const + { + Renderer::Submit([this]() { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_RendererID); + }); + } + + void OpenGLStorageBuffer::Unbind() const + { + Renderer::Submit([]() { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + }); + } + + void OpenGLStorageBuffer::BindBase(uint32_t index) const + { + Renderer::Submit([this, index]() { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index, m_RendererID); + }); + } + + void OpenGLStorageBuffer::SetData(const void* data, uint32_t size, uint32_t offset) + { + Renderer::Submit([this, data, size, offset]() { + glNamedBufferSubData(m_RendererID, offset, size, data); + }); + } + + void OpenGLStorageBuffer::GetData(void* outData, const uint32_t size, const uint32_t offset) const + { + glGetNamedBufferSubData(m_RendererID, offset, size, outData); + } + +} \ No newline at end of file diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.h b/Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.h new file mode 100644 index 0000000..21d9052 --- /dev/null +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLStorageBuffer.h @@ -0,0 +1,35 @@ +// +// Created by Atdunbg on 2026/3/12. +// + +#ifndef PRISM_OPENGLSTORAGEBUFFER_H +#define PRISM_OPENGLSTORAGEBUFFER_H + +#include "Prism/Renderer/StorageBuffer.h" + +namespace Prism { + + class OpenGLStorageBuffer : public StorageBuffer + { + public: + OpenGLStorageBuffer(uint32_t size, const void* data, StorageBufferUsage usage); + virtual ~OpenGLStorageBuffer(); + + virtual void Bind() const override; + virtual void Unbind() const override; + virtual void BindBase(uint32_t index) const override; + virtual void SetData(const void* data, uint32_t size, uint32_t offset = 0) override; + virtual void GetData(void* outData, uint32_t size, uint32_t offset = 0) const override; + virtual uint32_t GetRendererID() const override { return m_RendererID; } + virtual uint32_t GetSize() const override { return m_Size; } + + private: + uint32_t m_RendererID = 0; + uint32_t m_Size = 0; + StorageBufferUsage m_Usage; + }; + +} + + +#endif //PRISM_OPENGLSTORAGEBUFFER_H \ No newline at end of file diff --git a/Prism/src/Prism/Renderer/Renderer.cpp b/Prism/src/Prism/Renderer/Renderer.cpp index 79ba4b5..1e6e61c 100644 --- a/Prism/src/Prism/Renderer/Renderer.cpp +++ b/Prism/src/Prism/Renderer/Renderer.cpp @@ -253,6 +253,22 @@ namespace Prism } } + void Renderer::DispatchCompute(int x, int y, int z) + { + Submit([x, y, z]() + { + RendererAPI::DispatchCompute(x, y, z); + }); + } + + void Renderer::MemoryBarrier(int barrier) + { + Submit([barrier]() + { + RendererAPI::MemoryBarrier(barrier); + }); + } + void Renderer::DrawAABB(const AABB& aabb, const glm::mat4& transform, const glm::vec4& color) { glm::vec4 min = { aabb.Min.x, aabb.Min.y, aabb.Min.z, 1.0f }; diff --git a/Prism/src/Prism/Renderer/Renderer.h b/Prism/src/Prism/Renderer/Renderer.h index 02ab038..4be8899 100644 --- a/Prism/src/Prism/Renderer/Renderer.h +++ b/Prism/src/Prism/Renderer/Renderer.h @@ -59,6 +59,9 @@ namespace Prism static void DrawAABB(const AABB& aabb, const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f)); static void DrawAABB(const Ref& mesh,const glm::mat4& transform, const glm::vec4& color = glm::vec4(1.0f)); + static void DispatchCompute(int x, int y, int z); + static void MemoryBarrier(int barrier); + private: static RenderCommandQueue& GetRenderCommandQueue(); }; diff --git a/Prism/src/Prism/Renderer/Renderer3D.cpp b/Prism/src/Prism/Renderer/Renderer3D.cpp index f00f3d3..88f213b 100644 --- a/Prism/src/Prism/Renderer/Renderer3D.cpp +++ b/Prism/src/Prism/Renderer/Renderer3D.cpp @@ -11,6 +11,7 @@ #include "Renderer.h" #include "Renderer2D.h" #include "SceneRenderer.h" +#include "StorageBuffer.h" #include "Prism/Scene/Components.h" namespace Prism @@ -39,19 +40,29 @@ namespace Prism Ref BloomBlurPass[2]; Ref BloomBlendPass; - // Auto exposure struct AutoExposureData { Ref LuminancePass; - float CurrentExposure = 1.0f; bool EnableAutoExposure = true; - float Key = 0.11f; // middle gray - float AdaptationSpeed = 5.0f; // stops per second - + float Key = 0.11f; Timer ExposureTimer; float MaxExposure = 5.0f; - }AutoExposureData; + float MinExposure = 0.01f; + + // 直方图模式参数 + float SpeedUp = 5.0f; + float SpeedDown = 2.0f; + float LowPercent = 70.0f; + float HighPercent = 95.0f; + float LogMin = -4.0f; + float LogMax = 4.0f; + + Ref HistogramSSBO; + Ref ExposureSSBO; + Ref HistogramCS; + Ref ExposureCS; + } AutoExposureData; Ref ShadowMapShader, ShadowMapAnimShader; Ref ShadowMapRenderPass; @@ -135,65 +146,100 @@ namespace Prism void Renderer3D::Init() { - FramebufferSpecification geoFramebufferSpec; - geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::DEPTH24STENCIL8 }; - geoFramebufferSpec.Samples = 8; - geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + //////////////// GeoPass //////////////// + { + FramebufferSpecification geoFramebufferSpec; + geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::DEPTH24STENCIL8 }; + geoFramebufferSpec.Samples = 8; + geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; - RenderPassSpecification geoRenderPassSpec; - geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec); - s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec); + RenderPassSpecification geoRenderPassSpec; + geoRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(geoFramebufferSpec); + s_Data.GeoPass = RenderPass::Create(geoRenderPassSpec); + } + ///////////////////////////////////////////// - /* - FramebufferSpecification compFramebufferSpec; - compFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 , FramebufferTextureFormat::RGBA8}; - compFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; - RenderPassSpecification compRenderPassSpec; - compRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(compFramebufferSpec); - s_Data.CompositePass = RenderPass::Create(compRenderPassSpec); - */ + //////////////// BloomPass //////////////// + { + FramebufferSpecification bloomBlurFramebufferSpec; + bloomBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F , FramebufferTextureFormat::RGBA8}; + bloomBlurFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; - FramebufferSpecification bloomBlurFramebufferSpec; - bloomBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F , FramebufferTextureFormat::RGBA8}; - bloomBlurFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + RenderPassSpecification bloomBlurRenderPassSpec; + bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec); + s_Data.BloomBlurPass[0] = RenderPass::Create(bloomBlurRenderPassSpec); + bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec); + s_Data.BloomBlurPass[1] = RenderPass::Create(bloomBlurRenderPassSpec); - RenderPassSpecification bloomBlurRenderPassSpec; - bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec); - s_Data.BloomBlurPass[0] = RenderPass::Create(bloomBlurRenderPassSpec); - bloomBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlurFramebufferSpec); - s_Data.BloomBlurPass[1] = RenderPass::Create(bloomBlurRenderPassSpec); + FramebufferSpecification bloomBlendFramebufferSpec; + bloomBlendFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 }; + bloomBlendFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; - FramebufferSpecification bloomBlendFramebufferSpec; - bloomBlendFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA8 }; - bloomBlendFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + RenderPassSpecification bloomBlendRenderPassSpec; + bloomBlendRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlendFramebufferSpec); + s_Data.BloomBlendPass = RenderPass::Create(bloomBlendRenderPassSpec); - RenderPassSpecification bloomBlendRenderPassSpec; - bloomBlendRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(bloomBlendFramebufferSpec); - s_Data.BloomBlendPass = RenderPass::Create(bloomBlendRenderPassSpec); + s_Data.BloomBlurShader = Shader::Create("assets/shaders/BloomBlur.glsl"); + s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl"); + } + ///////////////////////////////////////////// + + //////////////// AutoExposure //////////////// + { + // Luminance pass: used to compute average scene luminance (for auto exposure) + FramebufferSpecification luminanceFramebufferSpec; + luminanceFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F }; + luminanceFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; + + RenderPassSpecification luminanceRenderPassSpec; + luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec); + s_Data.AutoExposureData.LuminancePass = RenderPass::Create(luminanceRenderPassSpec); + + constexpr uint32_t histogramSize = 64 * sizeof(uint32_t); + std::vector zeros(64, 0); + s_Data.AutoExposureData.HistogramSSBO = StorageBuffer::Create(histogramSize, zeros.data()); + float initialExposure = 1.0f; + s_Data.AutoExposureData.ExposureSSBO = StorageBuffer::Create(sizeof(float), &initialExposure); + s_Data.AutoExposureData.HistogramCS = Shader::Create("assets/shaders/Histogram.glsl"); + s_Data.AutoExposureData.ExposureCS = Shader::Create("assets/shaders/Exposure.glsl"); + } + ///////////////////////////////////////////// + + + //////////////// ShadowMapPass //////////////// + { + FramebufferSpecification shadowMapFramebufferSpec; + shadowMapFramebufferSpec.Width = 4096; + shadowMapFramebufferSpec.Height = 4096; + shadowMapFramebufferSpec.Attachments = { FramebufferTextureFormat::DEPTH32F }; + shadowMapFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; + shadowMapFramebufferSpec.NoResize = true; + + RenderPassSpecification shadowMapRenderPassSpec; + shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec); + s_Data.ShadowMapRenderPass = RenderPass::Create(shadowMapRenderPassSpec); + + Renderer::Submit([]() + { + glGenSamplers(1, &s_Data.ShadowMapSampler); + + // Setup the shadowMap depth sampler + glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + }); + } + ///////////////////////////////////////////// s_Data.CompositeShader = Shader::Create("assets/shaders/SceneComposite.glsl"); - s_Data.BloomBlurShader = Shader::Create("assets/shaders/BloomBlur.glsl"); - s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl"); s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga"); - // Luminance pass: used to compute average scene luminance (for auto exposure) - FramebufferSpecification luminanceFramebufferSpec; - luminanceFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F }; - luminanceFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; - - RenderPassSpecification luminanceRenderPassSpec; - luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec); - s_Data.AutoExposureData.LuminancePass = RenderPass::Create(luminanceRenderPassSpec); - // Grid // const auto gridShader = Shader::Create("assets/shaders/Grid.glsl"); const auto gridShader = Shader::Create("assets/shaders/InfiniteGrid.glsl"); s_Data.GridData.GridMaterial = MaterialInstance::Create(Material::Create(gridShader)); - // constexpr float gridScale = 16.025f; - // constexpr float gridSize = 0.025f; - // s_Data.GridMaterial->Set("u_Scale", gridScale); - // s_Data.GridMaterial->Set("u_Res", gridSize); s_Data.GridData.GridMaterial->SetFlag(MaterialFlag::TwoSided, true); // outline @@ -201,41 +247,18 @@ namespace Prism s_Data.OutlineMaterial = MaterialInstance::Create(Material::Create(outlineShader)); s_Data.OutlineMaterial->SetFlag(MaterialFlag::DepthTest, false); + // outlineAnim + auto outlineAnimShader = Shader::Create("assets/shaders/Outline_Anim.glsl"); + s_Data.OutlineAnimMaterial = MaterialInstance::Create(Material::Create(outlineAnimShader)); + s_Data.OutlineAnimMaterial->SetFlag(MaterialFlag::DepthTest, false); + // Collider const auto colliderShader = Shader::Create("assets/shaders/Collider.glsl"); s_Data.ColliderMaterial = MaterialInstance::Create(Material::Create(colliderShader)); s_Data.ColliderMaterial->SetFlag(MaterialFlag::DepthTest, false); - auto outlineAnimShader = Shader::Create("assets/shaders/Outline_Anim.glsl"); - s_Data.OutlineAnimMaterial = MaterialInstance::Create(Material::Create(outlineAnimShader)); - s_Data.OutlineAnimMaterial->SetFlag(MaterialFlag::DepthTest, false); - - // Shadow Map s_Data.ShadowMapShader = Shader::Create("assets/shaders/ShadowMap.glsl"); s_Data.ShadowMapAnimShader = Shader::Create("assets/shaders/ShadowMap_Anim.glsl"); - - FramebufferSpecification shadowMapFramebufferSpec; - shadowMapFramebufferSpec.Width = 4096; - shadowMapFramebufferSpec.Height = 4096; - shadowMapFramebufferSpec.Attachments = { FramebufferTextureFormat::DEPTH32F }; - shadowMapFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; - shadowMapFramebufferSpec.NoResize = true; - - RenderPassSpecification shadowMapRenderPassSpec; - shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec); - s_Data.ShadowMapRenderPass = RenderPass::Create(shadowMapRenderPassSpec); - - Renderer::Submit([]() - { - glGenSamplers(1, &s_Data.ShadowMapSampler); - - // Setup the shadowmap depth sampler - glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(s_Data.ShadowMapSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - }); - } void Renderer3D::SetViewportSize(uint32_t width, uint32_t height) @@ -429,7 +452,10 @@ namespace Prism Renderer::BeginRenderPass(outRenderPass); s_Data.CompositeShader->Bind(); - s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure()); + + s_Data.AutoExposureData.ExposureSSBO->BindBase(2); + s_Data.CompositeShader->SetBool("u_EnableAutoExposure",s_Data.AutoExposureData.EnableAutoExposure); + s_Data.CompositeShader->SetFloat("u_ManualExposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); s_Data.CompositeShader->SetFloat("u_EnableBloom", s_Data.EnableBloom); @@ -790,125 +816,72 @@ namespace Prism void Renderer3D::AutoExposurePass() { - if (!s_Data.AutoExposureData.EnableAutoExposure) + auto& ae = s_Data.AutoExposureData; + if (!ae.EnableAutoExposure) return; auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer; - auto dstFB = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer; - if (!srcFB || !dstFB) - return; + auto dstFB = ae.LuminancePass->GetSpecification().TargetFramebuffer; + if (!srcFB || !dstFB) return; - const uint32_t dstID = dstFB->GetColorAttachmentRendererID(); - const uint32_t width = dstFB->GetWidth(); - const uint32_t height = dstFB->GetHeight(); + float dt = ae.ExposureTimer; + ae.ExposureTimer.Reset(); - Renderer::Submit([dstID, width, height, srcFB]() mutable - { - // Use framebuffer blit to resolve multisampled source into non-multisampled luminance target. - const GLuint srcFBO = srcFB->GetRendererID(); - const GLuint dstFBO = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer->GetRendererID(); - - // Bind read/draw FBOs and blit color attachment 0 - glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO); + Renderer::Submit([srcFB, dstFB, logMin = ae.LogMin, logMax = ae.LogMax, histCS = ae.HistogramCS, histSSBO = ae.HistogramSSBO]() { + glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFB->GetRendererID()); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFB->GetRendererID()); glReadBuffer(GL_COLOR_ATTACHMENT0); glDrawBuffer(GL_COLOR_ATTACHMENT0); - - // Source size — try to use srcFB dimensions if available - const int srcWidth = srcFB->GetWidth(); - const int srcHeight = srcFB->GetHeight(); - - glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); - - // Unbind + glBlitFramebuffer(0, 0, srcFB->GetWidth(), srcFB->GetHeight(), + 0, 0, dstFB->GetWidth(), dstFB->GetHeight(), + GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - // Generate mipmaps so the smallest mip is the average color - glGenerateTextureMipmap(dstID); + const uint32_t zero = 0; + glClearNamedBufferData(histSSBO->GetRendererID(), GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); - // Determine highest mip level - int maxLevel = (int)std::floor(std::log2((float)std::max(width, height))); - if (maxLevel < 0) maxLevel = 0; + glUseProgram(histCS->GetRendererID()); + glBindTextureUnit(0, dstFB->GetColorAttachmentRendererID()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, histSSBO->GetRendererID()); + const GLint locLogMin = glGetUniformLocation(histCS->GetRendererID(), "u_LogMin"); + const GLint locLogMax = glGetUniformLocation(histCS->GetRendererID(), "u_LogMax"); + if (locLogMin != -1) glProgramUniform1f(histCS->GetRendererID(), locLogMin, logMin); + if (locLogMax != -1) glProgramUniform1f(histCS->GetRendererID(), locLogMax, logMax); - GLint levelWidth = 0, levelHeight = 0; - glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_WIDTH, &levelWidth); - glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_HEIGHT, &levelHeight); - - if (levelWidth == 0 || levelHeight == 0) return; - - const int bufSize = levelWidth * levelHeight * 4 * sizeof(float); - std::vector pixelData(bufSize / sizeof(float)); - - glGetTextureImage(dstID, maxLevel, GL_RGBA, GL_FLOAT, bufSize, pixelData.data()); - - // Sanitize pixel values (handle NaN/Inf or negative values coming from GPU) - for (int i = 0; i < 3; ++i) - { - if (!std::isfinite(pixelData[i]) || pixelData[i] < 0.0f) - pixelData[i] = 0.0f; - } - - // Convert to luminance - float lum = 0.2126f * pixelData[0] + 0.7152f * pixelData[1] + 0.0722f * pixelData[2]; - if (!std::isfinite(lum) || lum <= 0.0f) - lum = 1e-6f; // fallback minimum luminance - - // Compute desired exposure (simple key/avg approach) - const float key = s_Data.AutoExposureData.Key; - constexpr float minLum = 1e-6f; - float desiredExposure = key / std::max(lum, minLum); - desiredExposure = std::clamp(desiredExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure); - - - // Adapt exposure over time (exponential) - const float dt = s_Data.AutoExposureData.ExposureTimer.ElapsedMillis() / 1000.0f; - s_Data.AutoExposureData.ExposureTimer.Reset(); - const float tau = s_Data.AutoExposureData.AdaptationSpeed; - const float adaptFactor = 1.0f - std::exp(-tau * dt); - s_Data.AutoExposureData.CurrentExposure = s_Data.AutoExposureData.CurrentExposure + (desiredExposure - s_Data.AutoExposureData.CurrentExposure) * adaptFactor; - s_Data.AutoExposureData.CurrentExposure = std::clamp(s_Data.AutoExposureData.CurrentExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure); - - // Write exposure directly into composite shader program uniform so the subsequent composite pass uses it - /* - if (const GLuint prog = s_Data.CompositeShader->GetRendererID()) - { - const GLint loc = glGetUniformLocation(prog, "u_Exposure"); - if (loc >= 0) - glProgramUniform1f(prog, loc, s_Data.CurrentExposure); - } - */ + const uint32_t groupsX = (dstFB->GetWidth() + 15) / 16; + const uint32_t groupsY = (dstFB->GetHeight() + 15) / 16; + glDispatchCompute(groupsX, groupsY, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); }); + Renderer::Submit([&ae, dt]() { + glUseProgram(ae.ExposureCS->GetRendererID()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ae.HistogramSSBO->GetRendererID()); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ae.ExposureSSBO->GetRendererID()); + + auto setUniform = [&](const char* name, const float value) { + const GLint loc = glGetUniformLocation(ae.ExposureCS->GetRendererID(), name); + if (loc != -1) glProgramUniform1f(ae.ExposureCS->GetRendererID(), loc, value); + }; + + setUniform("u_SpeedUp", ae.SpeedUp); + setUniform("u_SpeedDown", ae.SpeedDown); + setUniform("u_Key", ae.Key); + setUniform("u_LowPercent", ae.LowPercent); + setUniform("u_HighPercent", ae.HighPercent); + setUniform("u_MinExposure", ae.MinExposure); + setUniform("u_MaxExposure", ae.MaxExposure); + setUniform("u_DeltaTime", dt); + setUniform("u_LogMin", ae.LogMin); + setUniform("u_LogMax", ae.LogMax); + + glDispatchCompute(1, 1, 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + }); } - /* - void Renderer3D::CompositePass() - { - auto& compositeBuffer = s_Data.CompositePass->GetSpecification().TargetFramebuffer; - - Renderer::BeginRenderPass(s_Data.CompositePass); - s_Data.CompositeShader->Bind(); - s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure()); - s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); - // s_Data.CompositeShader->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight())); - // s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint); - s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); - // s_Data.CompositeShader->SetFloat("u_BloomThreshold", s_Data.BloomThreshold); - s_Data.CompositeShader->SetFloat("u_EnableBloom", s_Data.EnableBloom); - - s_Data.GeoPass->GetSpecification().TargetFramebuffer->BindTexture(); - Renderer::Submit([]() - { - glBindTextureUnit(1, s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID()); - }); - - Renderer::SubmitFullscreenQuad(nullptr); - Renderer::EndRenderPass(); - } - */ - /* void Renderer3D::BloomBlurPass() { @@ -1111,7 +1084,7 @@ namespace Prism // TODO: this will not be hardcode const glm::vec3 lightDir = glm::normalize(directionalLights[0].Direction); // 光线方向(从光源指向场景) - glm::vec3 lightPos = lightDir * 10.0f; + glm::vec3 lightPos = lightDir * 100.0f; glm::mat4 lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); @@ -1225,6 +1198,7 @@ namespace Prism UI::EndTreeNode(); } + /* if (UI::BeginTreeNode("Auto Exposure", false)) { UI::BeginPropertyGrid(); @@ -1236,6 +1210,36 @@ namespace Prism UI::EndTreeNode(); } + */ + + if (UI::BeginTreeNode("Auto Exposure", false)) + { + UI::BeginPropertyGrid(); + UI::Property("Enable Auto Exposure", s_Data.AutoExposureData.EnableAutoExposure); + if (s_Data.AutoExposureData.EnableAutoExposure) + { + UI::Property("Key (middle gray)", s_Data.AutoExposureData.Key, 0.001f, 0.001f, 2.5f); + + UI::Separator(); + UI::Property("Speed Up (brighten)", s_Data.AutoExposureData.SpeedUp, 0.1f, 0.1f, 20.0f); + UI::Property("Speed Down (darken)", s_Data.AutoExposureData.SpeedDown, 0.1f, 0.1f, 20.0f); + + UI::Separator(); + UI::Property("Low Percent", s_Data.AutoExposureData.LowPercent, 1.0f, 0.0f, 100.0f); + UI::Property("High Percent", s_Data.AutoExposureData.HighPercent, 1.0f, 0.0f, 100.0f); + + UI::Separator(); + UI::Property("Min Exposure", s_Data.AutoExposureData.MinExposure, 0.01f, 0.0f, 10.0f); + UI::Property("Max Exposure", s_Data.AutoExposureData.MaxExposure, 0.1f, 0.0f, 20.0f); + + UI::Separator(); + UI::Property("Log Min", s_Data.AutoExposureData.LogMin, 0.1f, -10.0f, 10.0f); + UI::Property("Log Max", s_Data.AutoExposureData.LogMax, 0.1f, -10.0f, 10.0f); + } + + UI::EndPropertyGrid(); + UI::EndTreeNode(); + } ImGui::End(); diff --git a/Prism/src/Prism/Renderer/RendererAPI.h b/Prism/src/Prism/Renderer/RendererAPI.h index 3fbff92..e27f291 100644 --- a/Prism/src/Prism/Renderer/RendererAPI.h +++ b/Prism/src/Prism/Renderer/RendererAPI.h @@ -52,6 +52,9 @@ namespace Prism return capabilities; } + static void DispatchCompute(int x, int y, int z); + static void MemoryBarrier(int barrier); + private: static RendererAPIType s_CurrentRendererAPI; }; diff --git a/Prism/src/Prism/Renderer/StorageBuffer.cpp b/Prism/src/Prism/Renderer/StorageBuffer.cpp new file mode 100644 index 0000000..87e1b5a --- /dev/null +++ b/Prism/src/Prism/Renderer/StorageBuffer.cpp @@ -0,0 +1,25 @@ +// +// Created by Atdunbg on 2026/3/12. +// + +#include "StorageBuffer.h" + +#include "RendererAPI.h" +#include "Prism/Platform/OpenGL/OpenGLStorageBuffer.h" + +namespace Prism +{ + Ref StorageBuffer::Create(uint32_t size, const void* data, StorageBufferUsage usage) + { + Ref result = nullptr; + + switch (RendererAPI::Current()) + { + case RendererAPIType::None: return nullptr; + case RendererAPIType::OpenGL: result = Ref::Create(size, data, usage); break; + } + + return result; + } + +} diff --git a/Prism/src/Prism/Renderer/StorageBuffer.h b/Prism/src/Prism/Renderer/StorageBuffer.h new file mode 100644 index 0000000..c5fc88e --- /dev/null +++ b/Prism/src/Prism/Renderer/StorageBuffer.h @@ -0,0 +1,42 @@ +// +// Created by Atdunbg on 2026/3/12. +// + +#ifndef PRISM_STORAGEBUFFER_H +#define PRISM_STORAGEBUFFER_H +#include "Prism/Core/Ref.h" + +namespace Prism +{ + enum class StorageBufferUsage + { + Static = 0, + Dynamic, + Stream, + DynamicCopy + }; + + class StorageBuffer : public RefCounted + { + public: + virtual ~StorageBuffer() = default; + + static Ref Create(uint32_t size, const void* data = nullptr, StorageBufferUsage usage = StorageBufferUsage::Dynamic); + + virtual void Bind() const = 0; + virtual void Unbind() const = 0; + + virtual void BindBase(uint32_t index) const = 0; + + virtual void SetData(const void* data, uint32_t size, uint32_t offset = 0) = 0; + virtual void GetData(void* outData, uint32_t size, uint32_t offset = 0) const = 0; + + virtual uint32_t GetRendererID() const = 0; + virtual uint32_t GetSize() const = 0; + + protected: + StorageBuffer() = default; + }; +} + +#endif //PRISM_STORAGEBUFFER_H \ No newline at end of file diff --git a/README.md b/README.md index 0220b1a..d46c919 100644 --- a/README.md +++ b/README.md @@ -95,19 +95,19 @@ ScriptComponent 2. **配置依赖** - PhysX SDK: Prism/CmakeLists.txt内记录了是用的PhysX的构建配置,建议手动按照配置构建SDK,构建好后无需移动文件,项目可以自动链接库 -3. **编译** -**Cmake**: -```bash -cd Prism -cmake -B build -G Ninja -cmake --build build --target PrismEditor --config Release -j10 -``` +3. **编译**: + - **Cmake**: + ```bash + cd Prism + cmake -B build -G Ninja + cmake --build build --target PrismEditor --config Release -j10 + ``` -**Visual Studio**: -visual Studio启用Cmake。 然后点击project下拉框,找到 PrismEditor,构建。 + **Visual Studio**: + - visual Studio启用Cmake。 然后点击project下拉框,找到 PrismEditor,构建。 -**JetBrain Clion**: -clion 会自动识别Cmake, 运行调试配置找到PrismEditor构建。 + **JetBrain Clion**: + - clion 会自动识别Cmake, 运行调试配置找到PrismEditor构建。 ---